diff options
author | chriseth <chris@ethereum.org> | 2018-11-14 02:33:35 +0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2018-11-14 02:33:35 +0800 |
commit | 1d4f565a64988a3400847d2655ca24f73f234bc6 (patch) | |
tree | caaa6c26e307513505349b50ca4f2a8a9506752b | |
parent | 59dbf8f1085b8b92e8b7eb0ce380cbeb642e97eb (diff) | |
parent | 91b6b8a88e76016e0324036cb7a7f9300a1e2439 (diff) | |
download | dexon-solidity-1d4f565a64988a3400847d2655ca24f73f234bc6.tar dexon-solidity-1d4f565a64988a3400847d2655ca24f73f234bc6.tar.gz dexon-solidity-1d4f565a64988a3400847d2655ca24f73f234bc6.tar.bz2 dexon-solidity-1d4f565a64988a3400847d2655ca24f73f234bc6.tar.lz dexon-solidity-1d4f565a64988a3400847d2655ca24f73f234bc6.tar.xz dexon-solidity-1d4f565a64988a3400847d2655ca24f73f234bc6.tar.zst dexon-solidity-1d4f565a64988a3400847d2655ca24f73f234bc6.zip |
Merge pull request #5416 from ethereum/develop
Merge develop into release for 0.5.0
2011 files changed, 49431 insertions, 31482 deletions
diff --git a/.circleci/config.yml b/.circleci/config.yml new file mode 100644 index 00000000..cf9c9212 --- /dev/null +++ b/.circleci/config.yml @@ -0,0 +1,332 @@ +defaults: + # The default for tags is to not run, so we have to explicitly match a filter. + - build_on_tags: &build_on_tags + filters: + tags: + only: /.*/ + - setup_prerelease_commit_hash: &setup_prerelease_commit_hash + name: Store commit hash and prerelease + command: | + if [ "$CIRCLE_BRANCH" = release -o -n "$CIRCLE_TAG" ]; then echo -n > prerelease.txt; else date -u +"nightly.%Y.%-m.%-d" > prerelease.txt; fi + echo -n "$CIRCLE_SHA1" > commit_hash.txt + - run_build: &run_build + name: Build + command: | + mkdir -p build + cd build + cmake .. -DCMAKE_BUILD_TYPE=RelWithDebInfo $CMAKE_OPTIONS + make -j4 + - run_tests: &run_tests + name: Tests + command: scripts/tests.sh --junit_report test_results + - solc_artifact: &solc_artifact + path: build/solc/solc + destination: solc + - all_artifacts: &all_artifacts + root: build + paths: + - solc/solc + - test/soltest + - test/tools/solfuzzer + +version: 2 +jobs: + build_emscripten: + docker: + - image: trzeci/emscripten:sdk-tag-1.37.21-64bit + environment: + TERM: xterm + steps: + - checkout + - restore_cache: + name: Restore Boost build + key: &boost-cache-key emscripten-boost-{{ checksum "scripts/travis-emscripten/install_deps.sh" }}{{ checksum "scripts/travis-emscripten/build_emscripten.sh" }} + - run: + name: Bootstrap Boost + command: | + scripts/travis-emscripten/install_deps.sh + - run: + name: Build + command: | + scripts/travis-emscripten/build_emscripten.sh + - save_cache: + name: Save Boost build + key: *boost-cache-key + paths: + - boost_1_57_0 + - store_artifacts: + path: build/libsolc/soljson.js + destination: soljson.js + - run: mkdir -p workspace + - run: cp build/libsolc/soljson.js workspace/soljson.js + - run: scripts/get_version.sh > workspace/version.txt + - persist_to_workspace: + root: workspace + paths: + - soljson.js + - version.txt + test_emscripten_solcjs: + docker: + - image: trzeci/emscripten:sdk-tag-1.37.21-64bit + environment: + TERM: xterm + steps: + - checkout + - attach_workspace: + at: /tmp/workspace + - run: + name: Install external tests deps + command: | + apt-get -qq update + apt-get -qy install netcat curl + curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.33.4/install.sh | NVM_DIR=/usr/local/nvm bash + export NVM_DIR="/usr/local/nvm" + [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" # This loads nvm + nvm --version + nvm install 8 + node --version + npm --version + - run: + name: Test solcjs + command: | + . /usr/local/nvm/nvm.sh + test/solcjsTests.sh /tmp/workspace/soljson.js $(cat /tmp/workspace/version.txt) + test_emscripten_external: + docker: + - image: trzeci/emscripten:sdk-tag-1.37.21-64bit + environment: + TERM: xterm + steps: + - checkout + - attach_workspace: + at: /tmp/workspace + - run: + name: Install external tests deps + command: | + apt-get -qq update + apt-get -qy install netcat curl + curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.33.4/install.sh | NVM_DIR=/usr/local/nvm bash + export NVM_DIR="/usr/local/nvm" + [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" # This loads nvm + nvm --version + nvm install 8 + node --version + npm --version + - run: + name: External tests + command: | + . /usr/local/nvm/nvm.sh + test/externalTests.sh /tmp/workspace/soljson.js || test/externalTests.sh /tmp/workspace/soljson.js + build_x86_linux: + docker: + - image: buildpack-deps:artful + environment: + TERM: xterm + CMAKE_OPTIONS: -DCOVERAGE=OFF + steps: + - checkout + - run: + name: Install build dependencies + command: | + apt-get -qq update + apt-get -qy install cmake libboost-regex-dev libboost-filesystem-dev libboost-test-dev libboost-system-dev libboost-program-options-dev libz3-dev + ./scripts/install_obsolete_jsoncpp_1_7_4.sh + - run: *setup_prerelease_commit_hash + - run: *run_build + - store_artifacts: *solc_artifact + - persist_to_workspace: + root: build + paths: + - "*" + + build_x86_clang7: + docker: + - image: buildpack-deps:cosmic + environment: + TERM: xterm + CC: /usr/bin/clang-7 + CXX: /usr/bin/clang++-7 + steps: + - checkout + - run: + name: Install build dependencies + command: | + apt-get -qq update + apt-get -qy install clang-7 cmake libboost-regex-dev libboost-filesystem-dev libboost-test-dev libboost-system-dev libboost-program-options-dev libz3-dev + ./scripts/install_obsolete_jsoncpp_1_7_4.sh + - run: *setup_prerelease_commit_hash + - run: *run_build + - store_artifacts: *solc_artifact + - persist_to_workspace: + root: build + paths: + - "*" + + build_x86_mac: + macos: + xcode: "10.0.0" + environment: + TERM: xterm + steps: + - checkout + - run: + name: Install build dependencies + command: | + brew update + brew upgrade + brew unlink python + brew install z3 + brew install boost + brew install cmake + brew install wget + ./scripts/install_obsolete_jsoncpp_1_7_4.sh + - run: *setup_prerelease_commit_hash + - run: *run_build + - store_artifacts: *solc_artifact + - persist_to_workspace: *all_artifacts + + test_check_spelling: + docker: + - image: circleci/python:3.6 + environment: + TERM: xterm + steps: + - checkout + - attach_workspace: + at: build + - run: + name: Install dependencies + command: | + pip install --user codespell + - run: + name: Check spelling + command: ~/.local/bin/codespell -S "*.enc,.git" -I ./scripts/codespell_whitelist.txt + + test_check_style: + docker: + - image: buildpack-deps:artful + steps: + - checkout + - run: + name: Check for trailing whitespace + command: ./scripts/check_style.sh + + test_buglist: + docker: + - image: circleci/node + environment: + TERM: xterm + steps: + - checkout + - run: + name: JS deps + command: | + npm install download + npm install JSONPath + npm install mktemp + - run: + name: Test buglist + command: ./test/buglistTests.js + + test_x86_linux: + docker: + - image: buildpack-deps:artful + environment: + TERM: xterm + steps: + - checkout + - attach_workspace: + at: build + - run: + name: Install dependencies + command: | + apt-get -qq update + apt-get -qy install libz3-dev libleveldb1v5 python-pip + pip install codecov + - run: mkdir -p test_results + - run: + name: Test type checker + command: build/test/soltest -t 'syntaxTest*' -- --no-ipc --testpath test + - run: + name: Coverage of type checker + command: codecov --flags syntax --gcov-root build + - run: *run_tests + - run: + name: Coverage of all + command: codecov --flags all --gcov-root build + - store_test_results: + path: test_results/ + - store_artifacts: + path: test_results/ + destination: test_results/ + + test_x86_mac: + macos: + xcode: "10.0.0" + environment: + TERM: xterm + steps: + - checkout + - attach_workspace: + at: build + - run: + name: Install dependencies + command: | + brew update + brew upgrade + brew unlink python + brew install z3 + - run: mkdir -p test_results + - run: *run_tests + - store_test_results: + path: test_results/ + - store_artifacts: + path: test_results/ + destination: test_results/ + + docs: + docker: + - image: buildpack-deps:artful + steps: + - checkout + - run: + name: Install build dependencies + command: | + apt-get -qq update + apt-get -qy install python-sphinx python-pip + - run: *setup_prerelease_commit_hash + - run: + name: Build documentation + command: ./scripts/docs.sh + - store_artifacts: + path: docs/_build/html/ + destination: docs-html + +workflows: + version: 2 + build_all: + jobs: + - test_check_spelling: *build_on_tags + - test_check_style: *build_on_tags + - test_buglist: *build_on_tags + - build_emscripten: *build_on_tags + - test_emscripten_solcjs: + <<: *build_on_tags + requires: + - build_emscripten + - test_emscripten_external: + <<: *build_on_tags + requires: + - build_emscripten + - build_x86_linux: *build_on_tags + - build_x86_clang7: *build_on_tags + - build_x86_mac: *build_on_tags + - test_x86_linux: + <<: *build_on_tags + requires: + - build_x86_linux + - test_x86_mac: + <<: *build_on_tags + requires: + - build_x86_mac + - docs: *build_on_tags diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 00000000..ab452ecf --- /dev/null +++ b/.dockerignore @@ -0,0 +1,6 @@ +# out-of-tree builds usually go here. This helps improving performance of uploading +# the build context to the docker image build server +/build + +# in-tree builds +/deps diff --git a/.editorconfig b/.editorconfig index 86a837c1..7b8a7be9 100644 --- a/.editorconfig +++ b/.editorconfig @@ -8,6 +8,7 @@ trim_trailing_whitespace = true [*.{cpp,h}] indent_style = tab +indent_size = 4 [*.{py,rst,sh,yml}] indent_style = space diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 00000000..c3caf86e --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,39 @@ +--- +name: Bug Report +about: Bug reports about the Solidity Compiler. +--- + +<!--## Prerequisites + +- First, many thanks for taking part in the community. We really appreciate that. +- We realize there is a lot of information requested here. We ask only that you do your best to provide as much information as possible so we can better help you. +- Support questions are better asked in one of the following locations: + - [Solidity chat](https://gitter.im/ethereum/solidity) + - [Stack Overflow](https://ethereum.stackexchange.com/) +- Ensure the issue isn't already reported. +- The issue should be reproducible with the latest solidity version; however, this isn't a hard requirement and being reproducible with an older version is sufficient. +--> + +## Description + +<!--Please shortly describe the bug you have found, and what you expect instead.--> + +## Environment + +- Compiler version: +- Framework/IDE (e.g. Truffle or Remix): +- EVM execution environment / backend / blockchain client: +- Operating system: + +## Steps to Reproduce + +<!-- +Please provide a *minimal* source code example to trigger the bug you have found. +Please also mention any command line flags that are necessary for triggering the bug. +Provide as much information as necessary to reproduce the bug. + +``` +// Some *minimal* Solidity source code to reproduce the bug. +// ... +``` +-->
\ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 00000000..6702b62c --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,47 @@ +--- +name: Feature Request +about: Solidity language or infrastructure feature requests. +--- + +<!--## Prerequisites + +- First, many thanks for taking part in the community. We really appreciate that. +- We realize there is a lot of data requested here. We ask only that you do your best to provide as much information as possible so we can better help you. +- Support questions are better asked in one of the following locations: + - [Solidity chat](https://gitter.im/ethereum/solidity) + - [Stack Overflow](https://ethereum.stackexchange.com/) +- Ensure the issue isn't already reported (check `feature` and `language design` labels). + +*Delete the above section and the instructions in the sections below before submitting* + +--> + +## Abstract + +<!-- +Please describe by example what problem you see in the current Solidity language +and reason about it. +--> + +## Motivation + +<!-- +In this section you describe how you propose to address the problem you described earlier, +including by giving one or more exemplary source code snippets for demonstration. +--> + +## Specification + +<!-- +The technical specification should describe the syntax and semantics of any new feature. The +specification should be detailed enough to allow any developer to implement the functionality. +--> + +## Backwards Compatibility + +<!-- +All language changes that introduce backwards incompatibilities must include a section describing +these incompatibilities and their severity. + +Please describe how you propose to deal with these incompatibilities. +-->
\ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/general.md b/.github/ISSUE_TEMPLATE/general.md new file mode 100644 index 00000000..410b42e0 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/general.md @@ -0,0 +1,21 @@ +--- +name: General Feedback +about: Any general feedback (neither feature request nor bug reports) +--- + +<!--## Prerequisites + +- First, many thanks for taking part in the community. We really appreciate that. +- Read the [contributing guidelines](http://solidity.readthedocs.io/en/latest/contributing.html). +- Support questions are better asked in one of the following locations: + - [Solidity chat](https://gitter.im/ethereum/solidity) + - [Stack Overflow](https://ethereum.stackexchange.com/) +- Ensure the issue isn't already reported. + +*Delete the above section and the instructions in the sections below before submitting* +--> +## Description + +<!-- +Please describe the purpose of your ticket. +--> diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 00000000..9fdbf158 --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,22 @@ +<!--### Your checklist for this pull request + +Please review the [guidelines for contributing](http://solidity.readthedocs.io/en/latest/contributing.html) to this repository. + +Please also note that this project is released with a [Contributor Code of Conduct](CONDUCT.md). By participating in this project you agree to abide by its terms. +--> + +### Description + +<!-- +Please explain the changes you made here. + +Thank you for your help! +--> + +### Checklist +- [ ] Code compiles correctly +- [ ] All tests are passing +- [ ] New tests have been created which fail without the change (if possible) +- [ ] README / documentation was extended, if necessary +- [ ] Changelog entry (if change is visible to the user) +- [ ] Used meaningful commit messages @@ -36,13 +36,19 @@ docs/_build docs/utils/__pycache__ docs/utils/*.pyc /deps/downloads/ +deps/install +deps/cache # vim stuff -*.swp +[._]*.sw[a-p] +[._]sw[a-p] # IDE files .idea +.vscode browse.VC.db CMakeLists.txt.user /CMakeSettings.json /.vs +/.cproject +/.project diff --git a/.travis.yml b/.travis.yml index 8487deef..ef4f8b39 100644 --- a/.travis.yml +++ b/.travis.yml @@ -184,7 +184,7 @@ deploy: # Both the build and deploy steps for Emscripten are only run within the Ubuntu # configurations (not for macOS). That is controlled by conditionals within the bash # scripts because TravisCI doesn't provide much in the way of conditional logic. - + - provider: script script: test $SOLC_EMSCRIPTEN != On || (scripts/release_emscripten.sh) skip_cleanup: true diff --git a/CMakeLists.txt b/CMakeLists.txt index f30872af..f2c84d20 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -8,7 +8,7 @@ include(EthPolicy) eth_policy() # project name and version should be set after cmake_policy CMP0048 -set(PROJECT_VERSION "0.4.25") +set(PROJECT_VERSION "0.5.0") project(solidity VERSION ${PROJECT_VERSION}) option(SOLC_LINK_STATIC "Link solc executable statically on supported platforms" OFF) diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 00000000..36813f36 --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,76 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +In the interest of fostering an open and welcoming environment, we as +contributors and maintainers pledge to making participation in our project and +our community a harassment-free experience for everyone, regardless of age, body +size, disability, ethnicity, sex characteristics, gender identity and expression, +level of experience, education, socio-economic status, nationality, personal +appearance, race, religion, or sexual identity and orientation. + +## Our Standards + +Examples of behavior that contributes to creating a positive environment +include: + +* Using welcoming and inclusive language +* Being respectful of differing viewpoints and experiences +* Gracefully accepting constructive criticism +* Focusing on what is best for the community +* Showing empathy towards other community members + +Examples of unacceptable behavior by participants include: + +* The use of sexualized language or imagery and unwelcome sexual attention or + advances +* Trolling, insulting/derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or electronic + address, without explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Our Responsibilities + +Project maintainers are responsible for clarifying the standards of acceptable +behavior and are expected to take appropriate and fair corrective action in +response to any instances of unacceptable behavior. + +Project maintainers have the right and responsibility to remove, edit, or +reject comments, commits, code, wiki edits, issues, and other contributions +that are not aligned to this Code of Conduct, or to ban temporarily or +permanently any contributor for other behaviors that they deem inappropriate, +threatening, offensive, or harmful. + +## Scope + +This Code of Conduct applies both within project spaces and in public spaces +when an individual is representing the project or its community. Examples of +representing a project or community include using an official project e-mail +address, posting via an official social media account, or acting as an appointed +representative at an online or offline event. Representation of a project may be +further defined and clarified by project maintainers. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported by contacting the project team at chris@ethereum.org which only goes to +Christian Reitwiessner or axic@ethereum.org which only goes to Alex Beregszaszi. +To report an issue involving either of them please email Hudson Jameson at +hudson@ethereum.org. +All complaints will be reviewed and investigated and will result in a response that +is deemed necessary and appropriate to the circumstances. The project team is +obligated to maintain confidentiality with regard to the reporter of an incident. +Further details of specific enforcement policies may be posted separately. + +Project maintainers who do not follow or enforce the Code of Conduct in good +faith may face temporary or permanent repercussions as determined by other +members of the project's leadership. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, +available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html + +[homepage]: https://www.contributor-covenant.org diff --git a/CODING_STYLE.md b/CODING_STYLE.md index f36503d0..a0fe9864 100644 --- a/CODING_STYLE.md +++ b/CODING_STYLE.md @@ -146,7 +146,7 @@ for (auto i = x->begin(); i != x->end(); ++i) {} ``` No: -```cp +```cpp const double d = 0; int i, j; char *s; @@ -193,7 +193,7 @@ for (map<ComplexTypeOne, ComplexTypeTwo>::iterator i = l.begin(); i != l.end(); 2. Generally avoid shortening a standard form that already includes all important information: - e.g. stick to `shared_ptr<X>` rather than shortening to `ptr<X>`. 3. Where there are exceptions to this (due to excessive use and clear meaning), note the change prominently and use it consistently: - - e.g. `using Guard = std::lock_guard<std::mutex>;` ///< Guard is used throughout the codebase since it is clear in meaning and used commonly. + - e.g. `using Guard = std::lock_guard<std::mutex>;` ///< Guard is used throughout the codebase since it is clear in meaning and used commonly. 4. In general expressions should be roughly as important/semantically meaningful as the space they occupy. 5. Avoid introducing aliases for types unless they are very complicated. Consider the number of items a brain can keep track of at the same time. diff --git a/Changelog.md b/Changelog.md index bfe82f8c..026e2bac 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,3 +1,142 @@ +### 0.5.0 (2018-11-13) + +How to update your code: + * Change every ``.call()`` to a ``.call("")`` and every ``.call(signature, a, b, c)`` to use ``.call(abi.encodeWithSignature(signature, a, b, c))`` (the last one only works for value types). + * Change every ``keccak256(a, b, c)`` to ``keccak256(abi.encodePacked(a, b, c))``. + * Add ``public`` to every function and ``external`` to every fallback or interface function that does not specify its visibility already. + * Make your fallback functions ``external``. + * Explicitly state the data location for all variables of struct, array or mapping types (including function parameters), e.g. change ``uint[] x = m_x`` to ``uint[] storage x = m_x``. Note that ``external`` functions require parameters with a data location of ``calldata``. + * Explicitly convert values of contract type to addresses before using an ``address`` member. Example: if ``c`` is a contract, change ``c.transfer(...)`` to ``address(c).transfer(...)``. + * Declare variables and especially function arguments as ``address payable``, if you want to call ``transfer`` on them. + +Breaking Changes: + * ABI Encoder: Properly pad data from calldata (``msg.data`` and external function parameters). Use ``abi.encodePacked`` for unpadded encoding. + * C API (``libsolc`` / raw ``soljson.js``): Removed the ``version``, ``license``, ``compileSingle``, ``compileJSON``, ``compileJSONCallback`` methods + and replaced them with the ``solidity_license``, ``solidity_version`` and ``solidity_compile`` methods. + * Code Generator: Signed right shift uses proper arithmetic shift, i.e. rounding towards negative infinity. Warning: this may silently change the semantics of existing code! + * Code Generator: Revert at runtime if calldata is too short or points out of bounds. This is done inside the ``ABI decoder`` and therefore also applies to ``abi.decode()``. + * Code Generator: Use ``STATICCALL`` for ``pure`` and ``view`` functions. This was already the case in the experimental 0.5.0 mode. + * Commandline interface: Remove obsolete ``--formal`` option. + * Commandline interface: Rename the ``--julia`` option to ``--yul``. + * Commandline interface: Require ``-`` if standard input is used as source. + * Commandline interface: Use hash of library name for link placeholder instead of name itself. + * Compiler interface: Disallow remappings with empty prefix. + * Control Flow Analyzer: Consider mappings as well when checking for uninitialized return values. + * Control Flow Analyzer: Turn warning about returning uninitialized storage pointers into an error. + * General: ``continue`` in a ``do...while`` loop jumps to the condition (it used to jump to the loop body). Warning: this may silently change the semantics of existing code. + * General: Disallow declaring empty structs. + * General: Disallow raw ``callcode`` (was already deprecated in 0.4.12). It is still possible to use it via inline assembly. + * General: Disallow ``var`` keyword. + * General: Disallow ``sha3`` and ``suicide`` aliases. + * General: Disallow the ``throw`` statement. This was already the case in the experimental 0.5.0 mode. + * General: Disallow the ``years`` unit denomination (was already deprecated in 0.4.24) + * General: Introduce ``emit`` as a keyword instead of parsing it as identifier. + * General: New keywords: ``calldata`` and ``constructor`` + * General: New reserved keywords: ``alias``, ``apply``, ``auto``, ``copyof``, ``define``, ``immutable``, + ``implements``, ``macro``, ``mutable``, ``override``, ``partial``, ``promise``, ``reference``, ``sealed``, + ``sizeof``, ``supports``, ``typedef`` and ``unchecked``. + * General: Remove assembly instruction aliases ``sha3`` and ``suicide`` + * General: C99-style scoping rules are enforced now. This was already the case in the experimental 0.5.0 mode. + * General: Disallow combining hex numbers with unit denominations (e.g. ``0x1e wei``). This was already the case in the experimental 0.5.0 mode. + * JSON AST: Remove ``constant`` and ``payable`` fields (the information is encoded in the ``stateMutability`` field). + * JSON AST: Replace the ``isConstructor`` field by a new ``kind`` field, which can be ``constructor``, ``fallback`` or ``function``. + * Interface: Remove "clone contract" feature. The ``--clone-bin`` and ``--combined-json clone-bin`` commandline options are not available anymore. + * Name Resolver: Do not exclude public state variables when looking for conflicting declarations. + * Optimizer: Remove the no-op ``PUSH1 0 NOT AND`` sequence. + * Parser: Disallow trailing dots that are not followed by a number. + * Parser: Remove ``constant`` as function state mutability modifier. + * Parser: Disallow uppercase X in hex number literals + * Type Checker: Disallow assignments between tuples with different numbers of components. This was already the case in the experimental 0.5.0 mode. + * Type Checker: Disallow values for constants that are not compile-time constants. This was already the case in the experimental 0.5.0 mode. + * Type Checker: Disallow arithmetic operations for boolean variables. + * Type Checker: Disallow tight packing of literals. This was already the case in the experimental 0.5.0 mode. + * Type Checker: Disallow calling base constructors without parentheses. This was already the case in the experimental 0.5.0 mode. + * Type Checker: Disallow conversions between ``bytesX`` and ``uintY`` of different size. + * Type Checker: Disallow conversions between unrelated contract types. Explicit conversion via ``address`` can still achieve it. + * Type Checker: Disallow empty return statements for functions with one or more return values. + * Type Checker: Disallow empty tuple components. This was partly already the case in the experimental 0.5.0 mode. + * Type Checker: Disallow multi-variable declarations with mismatching number of values. This was already the case in the experimental 0.5.0 mode. + * Type Checker: Disallow specifying base constructor arguments multiple times in the same inheritance hierarchy. This was already the case in the experimental 0.5.0 mode. + * Type Checker: Disallow calling constructor with wrong argument count. This was already the case in the experimental 0.5.0 mode. + * Type Checker: Disallow uninitialized storage variables. This was already the case in the experimental 0.5.0 mode. + * Type Checker: Detecting cyclic dependencies in variables and structs is limited in recursion to 256. + * Type Checker: Require explicit data location for all variables, including function parameters. This was partly already the case in the experimental 0.5.0 mode. + * Type Checker: Only accept a single ``bytes`` type for ``.call()`` (and family), ``keccak256()``, ``sha256()`` and ``ripemd160()``. + * Type Checker: Fallback function must be external. This was already the case in the experimental 0.5.0 mode. + * Type Checker: Interface functions must be declared external. This was already the case in the experimental 0.5.0 mode. + * Type Checker: Address members are not included in contract types anymore. An explicit conversion is now required before invoking an ``address`` member from a contract. + * Type Checker: Disallow "loose assembly" syntax entirely. This means that jump labels, jumps and non-functional instructions cannot be used anymore. + * Type System: Disallow explicit and implicit conversions from decimal literals to ``bytesXX`` types. + * Type System: Disallow explicit and implicit conversions from hex literals to ``bytesXX`` types of different size. + * Type System: Distinguish between payable and non-payable address types. + * View Pure Checker: Disallow ``msg.value`` in (or introducing it via a modifier to) a non-payable function. + * Remove obsolete ``std`` directory from the Solidity repository. This means accessing ``https://github.com/ethereum/solidity/blob/develop/std/*.sol`` (or ``https://github.com/ethereum/solidity/std/*.sol`` in Remix) will not be possible. + * References Resolver: Turn missing storage locations into an error. This was already the case in the experimental 0.5.0 mode. + * Syntax Checker: Disallow functions without implementation to use modifiers. This was already the case in the experimental 0.5.0 mode. + * Syntax Checker: Named return values in function types are an error. + * Syntax Checker: Strictly require visibility specifier for functions. This was already the case in the experimental 0.5.0 mode. + * Syntax Checker: Disallow unary ``+``. This was already the case in the experimental 0.5.0 mode. + * Syntax Checker: Disallow single statement variable declaration inside if/while/for bodies that are not blocks. + * View Pure Checker: Strictly enforce state mutability. This was already the case in the experimental 0.5.0 mode. + +Language Features: + * General: Add ``staticcall`` to ``address``. + * General: Allow appending ``calldata`` keyword to types, to explicitly specify data location for arguments of external functions. + * General: Support ``pop()`` for storage arrays. + * General: Scoping rules now follow the C99-style. + * General: Allow ``enum``s in interfaces. + * General: Allow ``mapping`` storage pointers as arguments and return values in all internal functions. + * General: Allow ``struct``s in interfaces. + * General: Provide access to the ABI decoder through ``abi.decode(bytes memory data, (...))``. + * General: Disallow zero length for fixed-size arrays. + * Parser: Accept the ``address payable`` type during parsing. + +Compiler Features: + * Build System: Support for Mojave version of macOS added. + * Code Generator: ``CREATE2`` instruction has been updated to match EIP1014 (aka "Skinny CREATE2"). It also is accepted as part of Constantinople. + * Code Generator: ``EXTCODEHASH`` instruction has been added based on EIP1052. + * Type Checker: Nicer error message when trying to reference overloaded identifiers in inline assembly. + * Type Checker: Show named argument in case of error. + * Type System: IntegerType is split into IntegerType and AddressType internally. + * Tests: Determine transaction status during IPC calls. + * Code Generator: Allocate and free local variables according to their scope. + * Removed ``pragma experimental "v0.5.0";``. + * Syntax Checker: Improved error message for lookup in function types. + * Name Resolver: Updated name suggestion look up function to take into account length of the identifier: 1: no search, 2-3: at most one change, 4-: at most two changes + * SMTChecker: Support calls to internal functions that return none or a single value. + +Bugfixes: + * Build System: Support versions of CVC4 linked against CLN instead of GMP. In case of compilation issues due to the experimental SMT solver support, the solvers can be disabled when configuring the project with CMake using ``-DUSE_CVC4=OFF`` or ``-DUSE_Z3=OFF``. + * Tests: Fix chain parameters to make ipc tests work with newer versions of cpp-ethereum. + * Code Generator: Fix allocation of byte arrays (zeroed out too much memory). + * Code Generator: Properly handle negative number literals in ABIEncoderV2. + * Code Generator: Do not crash on using a length of zero for multidimensional fixed-size arrays. + * Commandline Interface: Correctly handle paths with backslashes on windows. + * Control Flow Analyzer: Ignore unimplemented functions when detecting uninitialized storage pointer returns. + * Fix NatSpec json output for `@notice` and `@dev` tags on contract definitions. + * Optimizer: Correctly estimate gas costs of constants for special cases. + * Optimizer: Fix simplification rule initialization bug that appeared on some emscripten platforms. + * References Resolver: Do not crash on using ``_slot`` and ``_offset`` suffixes on their own. + * References Resolver: Enforce ``storage`` as data location for mappings. + * References Resolver: Properly handle invalid references used together with ``_slot`` and ``_offset``. + * References Resolver: Report error instead of assertion fail when FunctionType has an undeclared type as parameter. + * References Resolver: Fix high CPU usage when using large variable names issue. Only suggest similar name if identifiers shorter than 80 characters. + * Type Checker: Default data location for type conversions (e.g. from literals) is memory and not storage. + * Type Checker: Disallow assignments to mappings within tuple assignments as well. + * Type Checker: Disallow packed encoding of arrays of structs. + * Type Checker: Allow assignments to local variables of mapping types. + * Type Checker: Consider fixed size arrays when checking for recursive structs. + * Type Checker: Fix crashes in erroneous tuple assignments in which the type of the right hand side cannot be determined. + * Type Checker: Fix freeze for negative fixed-point literals very close to ``0``, such as ``-1e-100``. + * Type Checker: Dynamic types as key for public mappings return error instead of assertion fail. + * Type Checker: Fix internal error when array index value is too large. + * Type Checker: Fix internal error when fixed-size array is too large to be encoded. + * Type Checker: Fix internal error for array type conversions. + * Type Checker: Fix internal error when array index is not an unsigned. + * Type System: Allow arbitrary exponents for literals with a mantissa of zero. + * Parser: Fix incorrect source location for nameless parameters. + * Command Line Interface: Fix internal error when compiling stdin with no content and --ast option. + ### 0.4.25 (2018-09-12) Important Bugfixes: @@ -63,6 +202,7 @@ Features: * General: Introduce new constructor syntax using the ``constructor`` keyword as experimental 0.5.0 feature. * General: Limit the number of errors output in a single run to 256. * General: Support accessing dynamic return data in post-byzantium EVMs. + * General: Allow underscores in numeric and hex literals to separate thousands and quads. * Inheritance: Error when using empty parentheses for base class constructors that require arguments as experimental 0.5.0 feature. * Inheritance: Error when using no parentheses in modifier-style constructor calls as experimental 0.5.0 feature. * Interfaces: Allow overriding external functions in interfaces with public in an implementing contract. @@ -106,7 +246,7 @@ Bugfixes: Features: * Code Generator: Assert that ``k != 0`` for ``mulmod(a, b, k)`` and ``addmod(a, b, k)`` as experimental 0.5.0 feature. * Code Generator: Do not retain any gas in calls (except if EVM version is set to homestead). - * Code Generator: Use ``STATICCALL`` opcode for calling ``view`` and ``pure`` functions as experimenal 0.5.0 feature. + * Code Generator: Use ``STATICCALL`` opcode for calling ``view`` and ``pure`` functions as experimental 0.5.0 feature. * General: C99/C++-style scoping rules (instead of JavaScript function scoping) take effect as experimental v0.5.0 feature. * General: Improved messaging when error spans multiple lines of a sourcefile * General: Support and recommend using ``emit EventName();`` to call events explicitly. @@ -659,7 +799,7 @@ Bugfixes: * Conditional: `x ? y : z` * Bugfix: Fixed several bugs where the optimizer generated invalid code. * Bugfix: Enums and structs were not accessible to other contracts. - * Bugfix: Fixed segfault connected to function paramater types, appeared during gas estimation. + * Bugfix: Fixed segfault connected to function parameter types, appeared during gas estimation. * Bugfix: Type checker crash for wrong number of base constructor parameters. * Bugfix: Allow function overloads with different array types. * Bugfix: Allow assignments of type `(x) = 7`. @@ -1,19 +1,66 @@ # The Solidity Contract-Oriented Programming Language [](https://gitter.im/ethereum/solidity?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) [](https://travis-ci.org/ethereum/solidity) +Solidity is a statically typed, contract-oriented, high-level language for implementing smart contracts on the Ethereum platform. -## Useful links -To get started you can find an introduction to the language in the [Solidity documentation](https://solidity.readthedocs.org). In the documentation, you can find [code examples](https://solidity.readthedocs.io/en/latest/solidity-by-example.html) as well as [a reference](https://solidity.readthedocs.io/en/latest/solidity-in-depth.html) of the syntax and details on how to write smart contracts. +## Table of Contents -You can start using [Solidity in your browser](http://remix.ethereum.org) with no need to download or compile anything. +- [Background](#background) +- [Build and Install](#build-and-install) +- [Example](#example) +- [Documentation](#documentation) +- [Development](#development) +- [Maintainers](#maintainers) +- [License](#license) -The changelog for this project can be found [here](https://github.com/ethereum/solidity/blob/develop/Changelog.md). +## Background -Solidity is still under development. So please do not hesitate and open an [issue in GitHub](https://github.com/ethereum/solidity/issues) if you encounter anything strange. +Solidity is a statically-typed curly-braces programming language designed for developing smart contracts +that run on the Ethereum Virtual Machine. Smart contracts are programs that are executed inside a peer-to-peer +network where nobody has special authority over the execution and thus they allow to implement tokens of value, +ownership, voting and other kinds of logics. -## Building -See the [Solidity documentation](https://solidity.readthedocs.io/en/latest/installing-solidity.html#building-from-source) for build instructions. +## Build and Install -## How to Contribute -Please see our [contribution guidelines](https://solidity.readthedocs.io/en/latest/contributing.html) in the Solidity documentation. +Instructions about how to build and install the Solidity compiler can be found in the [Solidity documentation](https://solidity.readthedocs.io/en/latest/installing-solidity.html#building-from-source) + + +## Example + +A "Hello World" program in Solidity is of even less use than in other languages, but still: + +``` +pragma solidity ^0.4.16; + +contract HelloWorld { + function helloWorld() external pure returns (string memory) { + return "Hello, World!"; + } +} +``` + +To get started with Solidity, you can use [Remix](https://remix.ethereum.org/), which is an +browser-based IDE. Here are some example contracts: + +1. [Voting](https://solidity.readthedocs.io/en/v0.4.24/solidity-by-example.html#voting) +2. [Blind Auction](https://solidity.readthedocs.io/en/v0.4.24/solidity-by-example.html#blind-auction) +3. [Safe remote purchase](https://solidity.readthedocs.io/en/v0.4.24/solidity-by-example.html#safe-remote-purchase) +4. [Micropayment Channel](https://solidity.readthedocs.io/en/v0.4.24/solidity-by-example.html#micropayment-channel) + +## Documentation + +The Solidity documentation is hosted at [Read the docs](https://solidity.readthedocs.io). + +## Development + +Solidity is still under development. Contributions are always welcome! +Please follow the +[Developers Guide](https://solidity.readthedocs.io/en/latest/contributing.html) +if you want to help. + +## Maintainers +[@axic](https://github.com/axic) +[@chriseth](https://github.com/chriseth) + +## License +Solidity is licensed under [GNU General Public License v3.0](https://github.com/ethereum/solidity/blob/develop/LICENSE.txt) -Any contributions are welcome! diff --git a/ReleaseChecklist.md b/ReleaseChecklist.md index ebdb7539..b84ae4c4 100644 --- a/ReleaseChecklist.md +++ b/ReleaseChecklist.md @@ -8,7 +8,7 @@ Checklist for making a release: - [ ] Make a final check that there are no platform-dependency issues in the ``solc-test-bytecode`` repository. - [ ] Wait for the tests for the commit on ``release``, create a release in Github, creating the tag. - [ ] Thank voluntary contributors in the Github release page (use ``git shortlog -s -n -e origin/release..origin/develop``). - - [ ] Wait for the CI runs on the tag itself (they should push artefacts onto the Github release page). + - [ ] Wait for the CI runs on the tag itself (they should push artifacts onto the Github release page). - [ ] Run ``scripts/release_ppa.sh release`` to create the PPA release (you need the relevant openssl key). - [ ] Check that the Docker release was pushed to Docker Hub (this still seems to have problems, run ``./scripts/docker_deploy_manual.sh release``). - [ ] Update the homebrew realease in https://github.com/ethereum/homebrew-ethereum/blob/master/solidity.rb (version and hash) diff --git a/appveyor.yml b/appveyor.yml index 81ec711d..91ea8af7 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -55,7 +55,6 @@ install: $fileContent += "`n-----END RSA PRIVATE KEY-----`n"; Set-Content c:\users\appveyor\.ssh\id_rsa $fileContent } - - git submodule update --init --recursive - ps: $prerelease = "nightly." - ps: $prerelease += Get-Date -format "yyyy.M.d" - ps: if($env:appveyor_repo_branch -eq 'release') { Set-Content prerelease.txt $null } else { Set-Content prerelease.txt $prerelease } diff --git a/circle.yml b/circle.yml deleted file mode 100644 index 7c95fa7f..00000000 --- a/circle.yml +++ /dev/null @@ -1,257 +0,0 @@ -defaults: - # The default for tags is to not run, so we have to explicitly match a filter. - - build_on_tags: &build_on_tags - filters: - tags: - only: /.*/ - - setup_prerelease_commit_hash: &setup_prerelease_commit_hash - name: Store commit hash and prerelease - command: | - if [ "$CIRCLE_BRANCH" = release -o -n "$CIRCLE_TAG" ]; then echo -n > prerelease.txt; else date -u +"nightly.%Y.%-m.%-d" > prerelease.txt; fi - echo -n "$CIRCLE_SHA1" > commit_hash.txt - - run_build: &run_build - name: Build - command: | - mkdir -p build - cd build - cmake .. -DCMAKE_BUILD_TYPE=RelWithDebInfo - make -j4 - - run_tests: &run_tests - name: Tests - command: scripts/tests.sh --junit_report test_results - - solc_artifact: &solc_artifact - path: build/solc/solc - destination: solc - - all_artifacts: &all_artifacts - root: build - paths: - - solc/solc - - test/soltest - - test/tools/solfuzzer - -version: 2 -jobs: - build_emscripten: - docker: - - image: trzeci/emscripten:sdk-tag-1.37.21-64bit - environment: - TERM: xterm - steps: - - checkout - - restore_cache: - name: Restore Boost build - key: &boost-cache-key emscripten-boost-{{ checksum "scripts/travis-emscripten/install_deps.sh" }}{{ checksum "scripts/travis-emscripten/build_emscripten.sh" }} - - run: - name: Bootstrap Boost - command: | - scripts/travis-emscripten/install_deps.sh - - run: - name: Build - command: | - scripts/travis-emscripten/build_emscripten.sh - - save_cache: - name: Save Boost build - key: *boost-cache-key - paths: - - boost_1_57_0 - - store_artifacts: - path: build/libsolc/soljson.js - destination: soljson.js - - run: mkdir -p workspace - - run: cp build/libsolc/soljson.js workspace/soljson.js - - run: scripts/get_version.sh > workspace/version.txt - - persist_to_workspace: - root: workspace - paths: - - soljson.js - - version.txt - test_emscripten_solcjs: - docker: - - image: trzeci/emscripten:sdk-tag-1.37.21-64bit - environment: - TERM: xterm - steps: - - checkout - - attach_workspace: - at: /tmp/workspace - - run: - name: Install external tests deps - command: | - apt-get -qq update - apt-get -qy install netcat curl - curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.33.4/install.sh | NVM_DIR=/usr/local/nvm bash - export NVM_DIR="/usr/local/nvm" - [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" # This loads nvm - nvm --version - nvm install 8 - node --version - npm --version - - run: - name: Test solcjs - command: | - . /usr/local/nvm/nvm.sh - test/solcjsTests.sh /tmp/workspace/soljson.js $(cat /tmp/workspace/version.txt) - test_emscripten_external: - docker: - - image: trzeci/emscripten:sdk-tag-1.37.21-64bit - environment: - TERM: xterm - steps: - - checkout - - attach_workspace: - at: /tmp/workspace - - run: - name: Install external tests deps - command: | - apt-get -qq update - apt-get -qy install netcat curl - curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.33.4/install.sh | NVM_DIR=/usr/local/nvm bash - export NVM_DIR="/usr/local/nvm" - [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" # This loads nvm - nvm --version - nvm install 8 - node --version - npm --version - - run: - name: External tests - command: | - . /usr/local/nvm/nvm.sh - test/externalTests.sh /tmp/workspace/soljson.js || test/externalTests.sh /tmp/workspace/soljson.js - build_x86_linux: - docker: - - image: buildpack-deps:artful - environment: - TERM: xterm - steps: - - checkout - - run: - name: Install build dependencies - command: | - apt-get -qq update - apt-get -qy install cmake libboost-regex-dev libboost-filesystem-dev libboost-test-dev libboost-system-dev libboost-program-options-dev libz3-dev - - run: *setup_prerelease_commit_hash - - run: *run_build - - store_artifacts: *solc_artifact - - persist_to_workspace: *all_artifacts - - build_x86_mac: - macos: - xcode: "9.0" - environment: - TERM: xterm - steps: - - checkout - - run: - name: Install build dependencies - command: | - brew update - brew upgrade - brew unlink python - brew install z3 - brew install boost - brew install cmake - - run: *setup_prerelease_commit_hash - - run: *run_build - - store_artifacts: *solc_artifact - - persist_to_workspace: *all_artifacts - - test_buglist: - docker: - - image: circleci/node - environment: - TERM: xterm - steps: - - checkout - - run: - name: JS deps - command: | - npm install download - npm install JSONPath - npm install mktemp - - run: - name: Test buglist - command: ./test/buglistTests.js - - test_x86_linux: - docker: - - image: buildpack-deps:artful - environment: - TERM: xterm - steps: - - checkout - - attach_workspace: - at: build - - run: - name: Install dependencies - command: | - apt-get -qq update - apt-get -qy install libz3-dev libleveldb1v5 - - run: mkdir -p test_results - - run: *run_tests - - store_test_results: - path: test_results/ - - test_x86_mac: - macos: - xcode: "9.0" - environment: - TERM: xterm - steps: - - checkout - - attach_workspace: - at: build - - run: - name: Install dependencies - command: | - brew update - brew upgrade - brew unlink python - brew install z3 - - run: mkdir -p test_results - - run: *run_tests - - store_test_results: - path: test_results/ - - docs: - docker: - - image: buildpack-deps:artful - steps: - - checkout - - run: - name: Install build dependencies - command: | - apt-get -qq update - apt-get -qy install python-sphinx - - run: *setup_prerelease_commit_hash - - run: - name: Build documentation - command: ./scripts/docs.sh - - store_artifacts: - path: docs/_build/html/ - destination: docs-html - -workflows: - version: 2 - build_all: - jobs: - - test_buglist: *build_on_tags - - build_emscripten: *build_on_tags - - test_emscripten_solcjs: - <<: *build_on_tags - requires: - - build_emscripten - - test_emscripten_external: - <<: *build_on_tags - requires: - - build_emscripten - - build_x86_linux: *build_on_tags - - build_x86_mac: *build_on_tags - - test_x86_linux: - <<: *build_on_tags - requires: - - build_x86_linux - - test_x86_mac: - <<: *build_on_tags - requires: - - build_x86_mac - - docs: *build_on_tags diff --git a/cmake/EthBuildInfo.cmake b/cmake/EthBuildInfo.cmake index cae3e5ce..b87d29f5 100644 --- a/cmake/EthBuildInfo.cmake +++ b/cmake/EthBuildInfo.cmake @@ -31,7 +31,7 @@ function(create_build_info NAME) # Generate header file containing useful build information add_custom_target(${NAME}_BuildInfo.h ALL WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} - COMMAND ${CMAKE_COMMAND} -DETH_SOURCE_DIR="${PROJECT_SOURCE_DIR}" -DETH_BUILDINFO_IN="${ETH_CMAKE_DIR}/templates/BuildInfo.h.in" -DETH_DST_DIR="${PROJECT_BINARY_DIR}/include/${PROJECT_NAME}" -DETH_CMAKE_DIR="${ETH_CMAKE_DIR}" + COMMAND ${CMAKE_COMMAND} -DETH_SOURCE_DIR=${PROJECT_SOURCE_DIR} -DETH_BUILDINFO_IN=${ETH_CMAKE_DIR}/templates/BuildInfo.h.in -DETH_DST_DIR=${PROJECT_BINARY_DIR}/include/${PROJECT_NAME} -DETH_CMAKE_DIR=${ETH_CMAKE_DIR} -DETH_BUILD_TYPE="${_cmake_build_type}" -DETH_BUILD_OS="${ETH_BUILD_OS}" -DETH_BUILD_COMPILER="${ETH_BUILD_COMPILER}" diff --git a/cmake/EthCompilerSettings.cmake b/cmake/EthCompilerSettings.cmake index 3ae5bf2a..b4cc6656 100644 --- a/cmake/EthCompilerSettings.cmake +++ b/cmake/EthCompilerSettings.cmake @@ -26,7 +26,7 @@ eth_add_cxx_compiler_flag_if_supported(-Wimplicit-fallthrough) if (("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU") OR ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang")) # Use ISO C++11 standard language. - set(CMAKE_CXX_FLAGS -std=c++11) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") # Enables all the warnings about constructions that some users consider questionable, # and that are easy to avoid. Also enable some extra warning flags that are not @@ -36,13 +36,6 @@ if (("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU") OR ("${CMAKE_CXX_COMPILER_ID}" MA add_compile_options(-Wextra) add_compile_options(-Werror) - # Disable warnings about unknown pragmas (which is enabled by -Wall). I assume we have external - # dependencies (probably Boost) which have some of these. Whatever the case, we shouldn't be - # disabling these globally. Instead, we should pragma around just the problem #includes. - # - # TODO - Track down what breaks if we do NOT do this. - add_compile_options(-Wno-unknown-pragmas) - # Configuration-specific compiler settings. set(CMAKE_CXX_FLAGS_DEBUG "-O0 -g -DETH_DEBUG") set(CMAKE_CXX_FLAGS_MINSIZEREL "-Os -DNDEBUG") @@ -73,13 +66,13 @@ if (("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU") OR ("${CMAKE_CXX_COMPILER_ID}" MA # 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) - + # See "How to silence unused command line argument error with clang without disabling it?" # When using -Werror with clang, it transforms "warning: argument unused during compilation" messages # into errors, which makes sense. @@ -90,7 +83,7 @@ if (("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU") OR ("${CMAKE_CXX_COMPILER_ID}" MA if (EMSCRIPTEN) # Do not emit a separate memory initialiser file set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} --memory-init-file 0") - # Leave only exported symbols as public and agressively remove others + # Leave only exported symbols as public and aggressively remove others set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fdata-sections -ffunction-sections -Wl,--gc-sections -fvisibility=hidden") # Optimisation level set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O3") @@ -146,26 +139,29 @@ endif () if (SANITIZE) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-omit-frame-pointer -fsanitize=${SANITIZE}") - if (${CMAKE_CXX_COMPILER_ID} MATCHES "Clang") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize-blacklist=${CMAKE_SOURCE_DIR}/sanitizer-blacklist.txt") - endif() endif() -if (PROFILING AND (("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU") OR ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang"))) - set(CMAKE_CXX_FLAGS "-g ${CMAKE_CXX_FLAGS}") - set(CMAKE_C_FLAGS "-g ${CMAKE_C_FLAGS}") - add_definitions(-DETH_PROFILING_GPERF) - set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -lprofiler") -# set(CMAKE_STATIC_LINKER_FLAGS "${CMAKE_STATIC_LINKER_FLAGS} -lprofiler") - set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -lprofiler") -endif () +# Code coverage support. +# Copied from Cable: +# https://github.com/ethereum/cable/blob/v0.2.4/CableCompilerSettings.cmake#L118-L132 +option(COVERAGE "Build with code coverage support" OFF) +if(COVERAGE) + # Set the linker flags first, they are required to properly test the compiler flag. + set(CMAKE_SHARED_LINKER_FLAGS "--coverage ${CMAKE_SHARED_LINKER_FLAGS}") + set(CMAKE_EXE_LINKER_FLAGS "--coverage ${CMAKE_EXE_LINKER_FLAGS}") + + set(CMAKE_REQUIRED_LIBRARIES "--coverage ${CMAKE_REQUIRED_LIBRARIES}") + check_cxx_compiler_flag(--coverage have_coverage) + string(REPLACE "--coverage " "" CMAKE_REQUIRED_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES}) + if(NOT have_coverage) + message(FATAL_ERROR "Coverage not supported") + endif() + add_compile_options(-g --coverage) +endif() -if (PROFILING AND (("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU"))) - set(CMAKE_CXX_FLAGS "-g --coverage ${CMAKE_CXX_FLAGS}") - set(CMAKE_C_FLAGS "-g --coverage ${CMAKE_C_FLAGS}") - set(CMAKE_SHARED_LINKER_FLAGS "--coverage ${CMAKE_SHARED_LINKER_FLAGS} -lprofiler") - set(CMAKE_EXE_LINKER_FLAGS "--coverage ${CMAKE_EXE_LINKER_FLAGS} -lprofiler") -endif () +# SMT Solvers integration +option(USE_Z3 "Allow compiling with Z3 SMT solver integration" ON) +option(USE_CVC4 "Allow compiling with CVC4 SMT solver integration" ON) if (("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU") OR ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang")) option(USE_LD_GOLD "Use GNU gold linker" ON) diff --git a/cmake/EthOptions.cmake b/cmake/EthOptions.cmake index b4efd6c9..a79e5135 100644 --- a/cmake/EthOptions.cmake +++ b/cmake/EthOptions.cmake @@ -2,7 +2,7 @@ macro(configure_project) set(NAME ${PROJECT_NAME}) # features - eth_default_option(PROFILING OFF) + eth_default_option(COVERAGE OFF) # components eth_default_option(TESTS ON) @@ -27,7 +27,7 @@ macro(print_config NAME) message("-- CMAKE_BUILD_TYPE Build type ${CMAKE_BUILD_TYPE}") message("-- TARGET_PLATFORM Target platform ${CMAKE_SYSTEM_NAME}") message("--------------------------------------------------------------- features") - message("-- PROFILING Profiling support ${PROFILING}") + message("-- COVERAGE Coverage support ${COVERAGE}") message("------------------------------------------------------------- components") if (SUPPORT_TESTS) message("-- TESTS Build tests ${TESTS}") diff --git a/cmake/EthPolicy.cmake b/cmake/EthPolicy.cmake index 31b09f15..4e29898c 100644 --- a/cmake/EthPolicy.cmake +++ b/cmake/EthPolicy.cmake @@ -4,32 +4,16 @@ macro (eth_policy) # link_directories() treats paths relative to the source dir. cmake_policy(SET CMP0015 NEW) - # let cmake autolink dependencies on windows - cmake_policy(SET CMP0020 NEW) + # Avoid warnings in CMake 3.0.2: + cmake_policy(SET CMP0042 NEW) + cmake_policy(SET CMP0043 NEW) - # CMake 2.8.12 and lower allowed the use of targets and files with double - # colons in target_link_libraries, - cmake_policy(SET CMP0028 OLD) + # allow VERSION argument in project() + cmake_policy(SET CMP0048 NEW) - if (${CMAKE_VERSION} VERSION_GREATER 3.0) - - # fix MACOSX_RPATH - cmake_policy(SET CMP0042 OLD) - - # ignore COMPILE_DEFINITIONS_<Config> properties - cmake_policy(SET CMP0043 OLD) - - # allow VERSION argument in project() - cmake_policy(SET CMP0048 NEW) - - endif() - - if (${CMAKE_VERSION} VERSION_GREATER 3.1) - + if (POLICY CMP0054) # do not interpret if() arguments as variables! cmake_policy(SET CMP0054 NEW) - endif() - endmacro() diff --git a/cmake/FindCLN.cmake b/cmake/FindCLN.cmake new file mode 100644 index 00000000..f2234bb4 --- /dev/null +++ b/cmake/FindCLN.cmake @@ -0,0 +1,3 @@ +find_library(CLN_LIBRARY NAMES cln) +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(CLN DEFAULT_MSG CLN_LIBRARY) diff --git a/cmake/FindCVC4.cmake b/cmake/FindCVC4.cmake index 0fb13196..2649d7c7 100644 --- a/cmake/FindCVC4.cmake +++ b/cmake/FindCVC4.cmake @@ -1,4 +1,26 @@ -find_path(CVC4_INCLUDE_DIR cvc4/cvc4.h) -find_library(CVC4_LIBRARY NAMES cvc4 ) -include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(CVC4 DEFAULT_MSG CVC4_LIBRARY CVC4_INCLUDE_DIR) +if (USE_CVC4) + find_path(CVC4_INCLUDE_DIR cvc4/cvc4.h) + find_library(CVC4_LIBRARY NAMES cvc4) + include(FindPackageHandleStandardArgs) + find_package_handle_standard_args(CVC4 DEFAULT_MSG CVC4_LIBRARY CVC4_INCLUDE_DIR) + if(CVC4_FOUND) + # CVC4 may depend on either CLN or GMP. + # We can assume that the one it requires is present on the system, + # so we quietly try to find both and link against them, if they are + # present. + find_package(CLN QUIET) + find_package(GMP QUIET) + + set(CVC4_LIBRARIES ${CVC4_LIBRARY}) + + if (CLN_FOUND) + set(CVC4_LIBRARIES ${CVC4_LIBRARIES} ${CLN_LIBRARY}) + endif () + + if (GMP_FOUND) + set(CVC4_LIBRARIES ${CVC4_LIBRARIES} ${GMP_LIBRARY}) + endif () + endif() +else() + set(CVC4_FOUND FALSE) +endif() diff --git a/cmake/FindZ3.cmake b/cmake/FindZ3.cmake index 971d3b4b..c017cac2 100644 --- a/cmake/FindZ3.cmake +++ b/cmake/FindZ3.cmake @@ -1,7 +1,9 @@ -find_path(Z3_INCLUDE_DIR z3++.h) -find_library(Z3_LIBRARY NAMES z3 ) -include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(Z3 DEFAULT_MSG Z3_LIBRARY Z3_INCLUDE_DIR) - +if (USE_Z3) + find_path(Z3_INCLUDE_DIR NAMES z3++.h PATH_SUFFIXES z3) + find_library(Z3_LIBRARY NAMES z3 ) + include(FindPackageHandleStandardArgs) + find_package_handle_standard_args(Z3 DEFAULT_MSG Z3_LIBRARY Z3_INCLUDE_DIR) +else() + set(Z3_FOUND FALSE) +endif() # TODO: Create IMPORTED library for Z3. - diff --git a/cmake/scripts/buildinfo.cmake b/cmake/scripts/buildinfo.cmake index efbfb8fb..3fb6beb2 100644 --- a/cmake/scripts/buildinfo.cmake +++ b/cmake/scripts/buildinfo.cmake @@ -1,5 +1,5 @@ # generates BuildInfo.h -# +# # this module expects # ETH_SOURCE_DIR - main CMAKE_SOURCE_DIR # ETH_DST_DIR - main CMAKE_BINARY_DIR @@ -19,7 +19,7 @@ if (NOT ETH_BUILD_PLATFORM) set(ETH_BUILD_PLATFORM "unknown") endif() -# Logic here: If prereleases.txt exists but is empty, it is a non-pre release. +# Logic here: If prerelease.txt exists but is empty, it is a non-pre release. # If it does not exist, create our own prerelease string if (EXISTS ${ETH_SOURCE_DIR}/prerelease.txt) file(READ ${ETH_SOURCE_DIR}/prerelease.txt SOL_VERSION_PRERELEASE) diff --git a/cmake/templates/license.h.in b/cmake/templates/license.h.in index 84524a52..4f22d8f4 100644 --- a/cmake/templates/license.h.in +++ b/cmake/templates/license.h.in @@ -66,6 +66,37 @@ jsoncpp: Public Domain "license" you can re-license your copy using whatever license you like. +scanner/token: + The libsolidity/parsing/{scanner,token}.{h,cpp} files are dervied from + code originating from the V8 project licensed under the following terms: + + Copyright 2006-2012, the V8 project authors. All rights reserved. + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of Google Inc. nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + All other code is licensed under GPL version 3: )"}; diff --git a/codecov.yml b/codecov.yml new file mode 100644 index 00000000..f20980ef --- /dev/null +++ b/codecov.yml @@ -0,0 +1,18 @@ +codecov: + branch: develop +coverage: + range: 70...100 + status: + project: + default: + target: auto + paths: "!test/" + syntax: + target: auto + paths: "libsolidity/analysis" + flags: syntax + tests: + target: auto + paths: "test/" +comment: + layout: "reach, diff, flags" diff --git a/docs/050-breaking-changes.rst b/docs/050-breaking-changes.rst new file mode 100644 index 00000000..48112cd9 --- /dev/null +++ b/docs/050-breaking-changes.rst @@ -0,0 +1,511 @@ +******************************** +Solidity v0.5.0 Breaking Changes +******************************** + +This section highlights the main breaking changes introduced in Solidity +version 0.5.0, along with the reasoning behind the changes and how to update +affected code. +For the full list check +`the release changelog <https://github.com/ethereum/solidity/releases/tag/v0.5.0>`_. + +.. note:: + Contracts compiled with Solidity v0.5.0 can still interface with contracts + and even libraries compiled with older versions without recompiling or + redeploying them. Changing the interfaces to include data locations and + visibility and mutability specifiers suffices. See the + :ref:`Interoperability With Older Contracts <interoperability>` section below. + +Semantic Only Changes +===================== + +This section lists the changes that are semantic-only, thus potentially +hiding new and different behavior in existing code. + +* Signed right shift now uses proper arithmetic shift, i.e. rounding towards + negative infinity, instead of rounding towards zero. Signed and unsigned + shift will have dedicated opcodes in Constantinople, and are emulated by + Solidity for the moment. + +* The ``continue`` statement in a ``do...while`` loop now jumps to the + condition, which is the common behavior in such cases. It used to jump to the + loop body. Thus, if the condition is false, the loop terminates. + +* The functions ``.call()``, ``.delegatecall()`` and ``.staticcall()`` do not + pad anymore when given a single ``bytes`` parameter. + +* Pure and view functions are now called using the opcode ``STATICCALL`` + instead of ``CALL`` if the EVM version is Byzantium or later. This + disallows state changes on the EVM level. + +* The ABI encoder now properly pads byte arrays and strings from calldata + (``msg.data`` and external function parameters) when used in external + function calls and in ``abi.encode``. For unpadded encoding, use + ``abi.encodePacked``. + +* The ABI decoder reverts in the beginning of functions and in + ``abi.decode()`` if passed calldata is too short or points out of bounds. + Note that dirty higher order bits are still simply ignored. + +* Forward all available gas with external function calls starting from + Tangerine Whistle. + +Semantic and Syntactic Changes +============================== + +This section highlights changes that affect syntax and semantics. + +* The functions ``.call()``, ``.delegatecall()``, ``staticcall()``, + ``keccak256()``, ``sha256()`` and ``ripemd160()`` now accept only a single + ``bytes`` argument. Moreover, the argument is not padded. This was changed to + make more explicit and clear how the arguments are concatenated. Change every + ``.call()`` (and family) to a ``.call("")`` and every ``.call(signature, a, + b, c)`` to use ``.call(abi.encodeWithSignature(signature, a, b, c))`` (the + last one only works for value types). Change every ``keccak256(a, b, c)`` to + ``keccak256(abi.encodePacked(a, b, c))``. Even though it is not a breaking + change, it is suggested that developers change + ``x.call(bytes4(keccak256("f(uint256)"), a, b)`` to + ``x.call(abi.encodeWithSignature("f(uint256)", a, b))``. + +* Functions ``.call()``, ``.delegatecall()`` and ``.staticcall()`` now return + ``(bool, bytes memory)`` to provide access to the return data. Change + ``bool success = otherContract.call("f")`` to ``(bool success, bytes memory + data) = otherContract.call("f")``. + +* Solidity now implements C99-style scoping rules for function local + variables, that is, variables can only be used after they have been + declared and only in the same or nested scopes. Variables declared in the + initialization block of a ``for`` loop are valid at any point inside the + loop. + +Explicitness Requirements +========================= + +This section lists changes where the code now needs to be more explicit. +For most of the topics the compiler will provide suggestions. + +* Explicit function visibility is now mandatory. Add ``public`` to every + function and constructor, and ``external`` to every fallback or interface + function that does not specify its visibility already. + +* Explicit data location for all variables of struct, array or mapping types is + now mandatory. This is also applied to function parameters and return + variables. For example, change ``uint[] x = m_x`` to ``uint[] storage x = + m_x``, and ``function f(uint[][] x)`` to ``function f(uint[][] memory x)`` + where ``memory`` is the data location and might be replaced by ``storage`` or + ``calldata`` accordingly. Note that ``external`` functions require + parameters with a data location of ``calldata``. + +* Contract types do not include ``address`` members anymore in + order to separate the namespaces. Therefore, it is now necessary to + explicitly convert values of contract type to addresses before using an + ``address`` member. Example: if ``c`` is a contract, change + ``c.transfer(...)`` to ``address(c).transfer(...)``, + and ``c.balance`` to ``address(c).balance``. + +* Explicit conversions between unrelated contract types are now disallowed. You can only + convert from a contract type to one of its base or ancestor types. If you are sure that + a contract is compatible with the contract type you want to convert to, although it does not + inherit from it, you can work around this by converting to ``address`` first. + Example: if ``A`` and ``B`` are contract types, ``B`` does not inherit from ``A`` and + ``b`` is a contract of type ``B``, you can still convert ``b`` to type ``A`` using ``A(address(b))``. + Note that you still need to watch out for matching payable fallback functions, as explained below. + +* The ``address`` type was split into ``address`` and ``address payable``, + where only ``address payable`` provides the ``transfer`` function. An + ``address payable`` can be directly converted to an ``address``, but the + other way around is not allowed. Converting ``address`` to ``address + payable`` is possible via conversion through ``uint160``. If ``c`` is a + contract, ``address(c)`` results in ``address payable`` only if ``c`` has a + payable fallback function. If you use the :ref:`withdraw pattern<withdrawal_pattern>`, + you most likely do not have to change your code because ``transfer`` + is only used on ``msg.sender`` instead of stored addresses and ``msg.sender`` + is an ``address payable``. + +* Conversions between ``bytesX`` and ``uintY`` of different size are now + disallowed due to ``bytesX`` padding on the right and ``uintY`` padding on + the left which may cause unexpected conversion results. The size must now be + adjusted within the type before the conversion. For example, you can convert + a ``bytes4`` (4 bytes) to a ``uint64`` (8 bytes) by first converting the + ``bytes4`` variable to ``bytes8`` and then to ``uint64``. You get the + opposite padding when converting through ``uint32``. + +* Using ``msg.value`` in non-payable functions (or introducing it via a + modifier) is disallowed as a security feature. Turn the function into + ``payable`` or create a new internal function for the program logic that + uses ``msg.value``. + +* For clarity reasons, the command line interface now requires ``-`` if the + standard input is used as source. + +Deprecated Elements +=================== + +This section lists changes that deprecate prior features or syntax. Note that +many of these changes were already enabled in the experimental mode +``v0.5.0``. + +Command Line and JSON Interfaces +-------------------------------- + +* The command line option ``--formal`` (used to generate Why3 output for + further formal verification) was deprecated and is now removed. A new + formal verification module, the SMTChecker, is enabled via ``pragma + experimental SMTChecker;``. + +* The command line option ``--julia`` was renamed to ``--yul`` due to the + renaming of the intermediate language ``Julia`` to ``Yul``. + +* The ``--clone-bin`` and ``--combined-json clone-bin`` command line options + were removed. + +* Remappings with empty prefix are disallowed. + +* The JSON AST fields ``constant`` and ``payable`` were removed. The + information is now present in the ``stateMutability`` field. + +* The JSON AST field ``isConstructor`` of the ``FunctionDefinition`` + node was replaced by a field called ``kind`` which can have the + value ``"constructor"``, ``"fallback"`` or ``"function"``. + +* In unlinked binary hex files, library address placeholders are now + the first 36 hex characters of the keccak256 hash of the fully qualified + library name, surrounded by ``$...$``. Previously, + just the fully qualified library name was used. + This recudes the chances of collisions, especially when long paths are used. + Binary files now also contain a list of mappings from these placeholders + to the fully qualified names. + +Constructors +------------ + +* Constructors must now be defined using the ``constructor`` keyword. + +* Calling base constructors without parentheses is now disallowed. + +* Specifying base constructor arguments multiple times in the same inheritance + hierarchy is now disallowed. + +* Calling a constructor with arguments but with wrong argument count is now + disallowed. If you only want to specify an inheritance relation without + giving arguments, do not provide parentheses at all. + +Functions +--------- + +* Function ``callcode`` is now disallowed (in favor of ``delegatecall``). It + is still possible to use it via inline assembly. + +* ``suicide`` is now disallowed (in favor of ``selfdestruct``). + +* ``sha3`` is now disallowed (in favor of ``keccak256``). + +* ``throw`` is now disallowed (in favor of ``revert``, ``require`` and + ``assert``). + +Conversions +----------- + +* Explicit and implicit conversions from decimal literals to ``bytesXX`` types + is now disallowed. + +* Explicit and implicit conversions from hex literals to ``bytesXX`` types + of different size is now disallowed. + +Literals and Suffixes +--------------------- + +* The unit denomination ``years`` is now disallowed due to complications and + confusions about leap years. + +* Trailing dots that are not followed by a number are now disallowed. + +* Combining hex numbers with unit denominations (e.g. ``0x1e wei``) is now + disallowed. + +* The prefix ``0X`` for hex numbers is disallowed, only ``0x`` is possible. + +Variables +--------- + +* Declaring empty structs is now disallowed for clarity. + +* The ``var`` keyword is now disallowed to favor explicitness. + +* Assignments between tuples with different number of components is now + disallowed. + +* Values for constants that are not compile-time constants are disallowed. + +* Multi-variable declarations with mismatching number of values are now + disallowed. + +* Uninitialized storage variables are now disallowed. + +* Empty tuple components are now disallowed. + +* Detecting cyclic dependencies in variables and structs is limited in + recursion to 256. + +* Fixed-size arrays with a length of zero are now disallowed. + +Syntax +------ + +* Using ``constant`` as function state mutability modifier is now disallowed. + +* Boolean expressions cannot use arithmetic operations. + +* The unary ``+`` operator is now disallowed. + +* Literals cannot anymore be used with ``abi.encodePacked`` without prior + conversion to an explicit type. + +* Empty return statements for functions with one or more return values are now + disallowed. + +* The "loose assembly" syntax is now disallowed entirely, that is, jump labels, + jumps and non-functional instructions cannot be used anymore. Use the new + ``while``, ``switch`` and ``if`` constructs instead. + +* Functions without implementation cannot use modifiers anymore. + +* Function types with named return values are now disallowed. + +* Single statement variable declarations inside if/while/for bodies that are + not blocks are now disallowed. + +* New keywords: ``calldata`` and ``constructor``. + +* New reserved keywords: ``alias``, ``apply``, ``auto``, ``copyof``, + ``define``, ``immutable``, ``implements``, ``macro``, ``mutable``, + ``override``, ``partial``, ``promise``, ``reference``, ``sealed``, + ``sizeof``, ``supports``, ``typedef`` and ``unchecked``. + +.. _interoperability: + +Interoperability With Older Contracts +===================================== + +It is still possible to interface with contracts written for Solidity versions prior to +v0.5.0 (or the other way around) by defining interfaces for them. +Consider you have the following pre-0.5.0 contract already deployed: + +:: + + // This will not compile with the current version of the compiler + pragma solidity ^0.4.25; + contract OldContract { + function someOldFunction(uint8 a) { + //... + } + function anotherOldFunction() constant returns (bool) { + //... + } + // ... + } + +This will no longer compile with Solidity v0.5.0. However, you can define a compatible interface for it: + +:: + + pragma solidity >0.4.99 <0.6.0; + interface OldContract { + function someOldFunction(uint8 a) external; + function anotherOldFunction() external returns (bool); + } + +Note that we did not declare ``anotherOldFunction`` to be ``view``, despite it being declared ``constant`` in the original +contract. This is due to the fact that starting with Solidity v0.5.0 ``staticcall`` is used to call ``view`` functions. +Prior to v0.5.0 the ``constant`` keyword was not enforced, so calling a function declared ``constant`` with ``staticcall`` +may still revert, since the ``constant`` function may still attempt to modify storage. Consequently, when defining an +interface for older contracts, you should only use ``view`` in place of ``constant`` in case you are absolutely sure that +the function will work with ``staticcall``. + +Given the interface defined above, you can now easily use the already deployed pre-0.5.0 contract: + +:: + + pragma solidity >0.4.99 <0.6.0; + + interface OldContract { + function someOldFunction(uint8 a) external; + function anotherOldFunction() external returns (bool); + } + + contract NewContract { + function doSomething(OldContract a) public returns (bool) { + a.someOldFunction(0x42); + return a.anotherOldFunction(); + } + } + +Similarly, pre-0.5.0 libraries can be used by defining the functions of the library without implementation and +supplying the address of the pre-0.5.0 library during linking (see :ref:`commandline-compiler` for how to use the +commandline compiler for linking): + +:: + + pragma solidity >0.4.99 <0.6.0; + + library OldLibrary { + function someFunction(uint8 a) public returns(bool); + } + + contract NewContract { + function f(uint8 a) public returns (bool) { + return OldLibrary.someFunction(a); + } + } + + +Example +======= + +The following example shows a contract and its updated version for Solidity +v0.5.0 with some of the changes listed in this section. + +Old version: + +:: + + // This will not compile + pragma solidity ^0.4.25; + + contract OtherContract { + uint x; + function f(uint y) external { + x = y; + } + function() payable external {} + } + + contract Old { + OtherContract other; + uint myNumber; + + // Function mutability not provided, not an error. + function someInteger() internal returns (uint) { return 2; } + + // Function visibility not provided, not an error. + // Function mutability not provided, not an error. + function f(uint x) returns (bytes) { + // Var is fine in this version. + var z = someInteger(); + x += z; + // Throw is fine in this version. + if (x > 100) + throw; + bytes b = new bytes(x); + y = -3 >> 1; + // y == -1 (wrong, should be -2) + do { + x += 1; + if (x > 10) continue; + // 'Continue' causes an infinite loop. + } while (x < 11); + // Call returns only a Bool. + bool success = address(other).call("f"); + if (!success) + revert(); + else { + // Local variables could be declared after their use. + int y; + } + return b; + } + + // No need for an explicit data location for 'arr' + function g(uint[] arr, bytes8 x, OtherContract otherContract) public { + otherContract.transfer(1 ether); + + // Since uint32 (4 bytes) is smaller than bytes8 (8 bytes), + // the first 4 bytes of x will be lost. This might lead to + // unexpected behavior since bytesX are right padded. + uint32 y = uint32(x); + myNumber += y + msg.value; + } + } + +New version: + +:: + + pragma solidity >0.4.99 <0.6.0; + + contract OtherContract { + uint x; + function f(uint y) external { + x = y; + } + function() payable external {} + } + + contract New { + OtherContract other; + uint myNumber; + + // Function mutability must be specified. + function someInteger() internal pure returns (uint) { return 2; } + + // Function visibility must be specified. + // Function mutability must be specified. + function f(uint x) public returns (bytes memory) { + // The type must now be explicitly given. + uint z = someInteger(); + x += z; + // Throw is now disallowed. + require(x > 100); + int y = -3 >> 1; + // y == -2 (correct) + do { + x += 1; + if (x > 10) continue; + // 'Continue' jumps to the condition below. + } while (x < 11); + + // Call returns (bool, bytes). + // Data location must be specified. + (bool success, bytes memory data) = address(other).call("f"); + if (!success) + revert(); + return data; + } + + using address_make_payable for address; + // Data location for 'arr' must be specified + function g(uint[] memory arr, bytes8 x, OtherContract otherContract, address unknownContract) public payable { + // 'otherContract.transfer' is not provided. + // Since the code of 'OtherContract' is known and has the fallback + // function, address(otherContract) has type 'address payable'. + address(otherContract).transfer(1 ether); + + // 'unknownContract.transfer' is not provided. + // 'address(unknownContract).transfer' is not provided + // since 'address(unknownContract)' is not 'address payable'. + // If the function takes an 'address' which you want to send + // funds to, you can convert it to 'address payable' via 'uint160'. + // Note: This is not recommended and the explicit type + // 'address payable' should be used whenever possible. + // To increase clarity, we suggest the use of a library for + // the conversion (provided after the contract in this example). + address payable addr = unknownContract.make_payable(); + require(addr.send(1 ether)); + + // Since uint32 (4 bytes) is smaller than bytes8 (8 bytes), + // the conversion is not allowed. + // We need to convert to a common size first: + bytes4 x4 = bytes4(x); // Padding happens on the right + uint32 y = uint32(x4); // Conversion is consistent + // 'msg.value' cannot be used in a 'non-payable' function. + // We need to make the function payable + myNumber += y + msg.value; + } + } + + // We can define a library for explicitly converting ``address`` + // to ``address payable`` as a workaround. + library address_make_payable { + function make_payable(address x) internal pure returns (address payable) { + return address(uint160(x)); + } + } diff --git a/docs/_static/css/custom.css b/docs/_static/css/custom.css new file mode 100644 index 00000000..fd506203 --- /dev/null +++ b/docs/_static/css/custom.css @@ -0,0 +1,14 @@ +pre { + white-space: pre-wrap; /* css-3 */ + white-space: -moz-pre-wrap; /* Mozilla, since 1999 */ + white-space: -pre-wrap; /* Opera 4-6 */ + white-space: -o-pre-wrap; /* Opera 7 */ + word-wrap: break-word; +} + +.wy-table-responsive table td, .wy-table-responsive table th { + white-space: normal; +} +.rst-content table.docutils td { + vertical-align: top; +} diff --git a/docs/_templates/layout.html b/docs/_templates/layout.html new file mode 100644 index 00000000..1db4ef92 --- /dev/null +++ b/docs/_templates/layout.html @@ -0,0 +1,6 @@ +{% extends "!layout.html" %} + + {% block menu %} + {{ super() }} + <a href="genindex.html">Keyword Index</a> + {% endblock %} diff --git a/docs/abi-spec.rst b/docs/abi-spec.rst index 8591a07f..0f4a16b6 100644 --- a/docs/abi-spec.rst +++ b/docs/abi-spec.rst @@ -2,20 +2,20 @@ .. _ABI: -****************************************** -Application Binary Interface Specification -****************************************** +************************** +Contract ABI Specification +************************** Basic Design ============ -The Application Binary Interface is the standard way to interact with contracts in the Ethereum ecosystem, both +The Contract Application Binary Interface (ABI) is the standard way to interact with contracts in the Ethereum ecosystem, both from outside the blockchain and for contract-to-contract interaction. Data is encoded according to its type, as described in this specification. The encoding is not self describing and thus requires a schema in order to decode. -We assume the interface functions of a contract are strongly typed, known at compilation time and static. No introspection mechanism will be provided. We assume that all contracts will have the interface definitions of any contracts they call available at compile-time. +We assume the interface functions of a contract are strongly typed, known at compilation time and static. We assume that all contracts will have the interface definitions of any contracts they call available at compile-time. -This specification does not address contracts whose interface is dynamic or otherwise known only at run-time. Should these cases become important they can be adequately handled as facilities built within the Ethereum ecosystem. +This specification does not address contracts whose interface is dynamic or otherwise known only at run-time. .. _abi_function_selector: @@ -23,12 +23,12 @@ Function Selector ================= The first four bytes of the call data for a function call specifies the function to be called. It is the -first (left, high-order in big-endian) four bytes of the Keccak (SHA-3) hash of the signature of the function. The signature is defined as the canonical expression of the basic prototype, i.e. +first (left, high-order in big-endian) four bytes of the Keccak-256 (SHA-3) hash of the signature of the function. The signature is defined as the canonical expression of the basic prototype without data location specifier, i.e. the function name with the parenthesised list of parameter types. Parameter types are split by a single comma - no spaces are used. .. note:: The return type of a function is not part of this signature. In :ref:`Solidity's function overloading <overload-function>` return types are not considered. The reason is to keep function call resolution context-independent. - The JSON description of the ABI however contains both inputs and outputs. See (the :ref:`JSON ABI <abi_json>`) + The :ref:`JSON description of the ABI<abi_json>` however contains both inputs and outputs. Argument Encoding ================= @@ -72,27 +72,48 @@ The following non-fixed-size types exist: - ``<type>[]``: a variable-length array of elements of the given type. -Types can be combined to a tuple by enclosing a finite non-negative number -of them inside parentheses, separated by commas: +Types can be combined to a tuple by enclosing them inside parentheses, separated by commas: - ``(T1,T2,...,Tn)``: tuple consisting of the types ``T1``, ..., ``Tn``, ``n >= 0`` It is possible to form tuples of tuples, arrays of tuples and so on. It is also possible to form zero-tuples (where ``n == 0``). -.. note:: - Solidity supports all the types presented above with the same names with the exception of tuples. The ABI tuple type is utilised for encoding Solidity ``structs``. - -Formal Specification of the Encoding -==================================== +Mapping Solidity to ABI types +----------------------------- + +Solidity supports all the types presented above with the same names with the +exception of tuples. On the other hand, some Solidity types are not supported +by the ABI. The following table shows on the left column Solidity types that +are not part of the ABI, and on the right column the ABI types that represent +them. + ++-------------------------------+-----------------------------------------------------------------------------+ +| Solidity | ABI | ++===============================+=============================================================================+ +|:ref:`address payable<address>`|``address`` | ++-------------------------------+-----------------------------------------------------------------------------+ +|:ref:`contract<contracts>` |``address`` | ++-------------------------------+-----------------------------------------------------------------------------+ +|:ref:`enum<enums>` |smallest ``uint`` type that is large enough to hold all values | +| | | +| |For example, an ``enum`` of 255 values or less is mapped to ``uint8`` and | +| |an ``enum`` of 256 values is mapped to ``uint16``. | ++-------------------------------+-----------------------------------------------------------------------------+ +|:ref:`struct<structs>` |``tuple`` | ++-------------------------------+-----------------------------------------------------------------------------+ + +Design Criteria for the Encoding +================================ + +The encoding is designed to have the following properties, which are especially useful if some arguments are nested arrays: -We will now formally specify the encoding, such that it will have the following -properties, which are especially useful if some arguments are nested arrays: + 1. The number of reads necessary to access a value is at most the depth of the value inside the argument array structure, i.e. four reads are needed to retrieve ``a_i[k][l][r]``. In a previous version of the ABI, the number of reads scaled linearly with the total number of dynamic parameters in the worst case. -Properties: + 2. The data of a variable or array element is not interleaved with other data and it is relocatable, i.e. it only uses relative "addresses". - 1. The number of reads necessary to access a value is at most the depth of the value inside the argument array structure, i.e. four reads are needed to retrieve ``a_i[k][l][r]``. In a previous version of the ABI, the number of reads scaled linearly with the total number of dynamic parameters in the worst case. - 2. The data of a variable or array element is not interleaved with other data and it is relocatable, i.e. it only uses relative "addresses" +Formal Specification of the Encoding +==================================== We distinguish static and dynamic types. Static types are encoded in-place and dynamic types are encoded at a separately allocated location after the current block. @@ -152,8 +173,8 @@ on the type of ``X`` being - ``bytes``, of length ``k`` (which is assumed to be of type ``uint256``): ``enc(X) = enc(k) pad_right(X)``, i.e. the number of bytes is encoded as a - ``uint256`` followed by the actual value of ``X`` as a byte sequence, followed by - the minimum number of zero-bytes such that ``len(enc(X))`` is a multiple of 32. + ``uint256`` followed by the actual value of ``X`` as a byte sequence, followed by + the minimum number of zero-bytes such that ``len(enc(X))`` is a multiple of 32. - ``string``: @@ -191,12 +212,12 @@ Given the contract: :: - pragma solidity ^0.4.16; + pragma solidity >=0.4.16 <0.6.0; contract Foo { - function bar(bytes3[2]) public pure {} + function bar(bytes3[2] memory) public pure {} function baz(uint32 x, bool y) public pure returns (bool r) { r = x > 32 || y; } - function sam(bytes, bool, uint[]) public pure {} + function sam(bytes memory, bool, uint[] memory) public pure {} } @@ -206,7 +227,9 @@ Thus for our ``Foo`` example if we wanted to call ``baz`` with the parameters `` - ``0x0000000000000000000000000000000000000000000000000000000000000045``: the first parameter, a uint32 value ``69`` padded to 32 bytes - ``0x0000000000000000000000000000000000000000000000000000000000000001``: the second parameter - boolean ``true``, padded to 32 bytes -In total:: +In total: + +.. code-block:: none 0xcdcd77c000000000000000000000000000000000000000000000000000000000000000450000000000000000000000000000000000000000000000000000000000000001 @@ -218,7 +241,9 @@ If we wanted to call ``bar`` with the argument ``["abc", "def"]``, we would pass - ``0x6162630000000000000000000000000000000000000000000000000000000000``: the first part of the first parameter, a ``bytes3`` value ``"abc"`` (left-aligned). - ``0x6465660000000000000000000000000000000000000000000000000000000000``: the second part of the first parameter, a ``bytes3`` value ``"def"`` (left-aligned). -In total:: +In total: + +.. code-block:: none 0xfce353f661626300000000000000000000000000000000000000000000000000000000006465660000000000000000000000000000000000000000000000000000000000 @@ -235,7 +260,9 @@ If we wanted to call ``sam`` with the arguments ``"dave"``, ``true`` and ``[1,2, - ``0x0000000000000000000000000000000000000000000000000000000000000002``: the second entry of the third parameter. - ``0x0000000000000000000000000000000000000000000000000000000000000003``: the third entry of the third parameter. -In total:: +In total: + +.. code-block:: none 0xa5643bf20000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000464617665000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000003 @@ -265,7 +292,7 @@ Finally, we encode the data part of the second dynamic argument, ``"Hello, world All together, the encoding is (newline after function selector and each 32-bytes for clarity): -:: +.. code-block:: none 0x8be65246 0000000000000000000000000000000000000000000000000000000000000123 @@ -278,6 +305,108 @@ All together, the encoding is (newline after function selector and each 32-bytes 000000000000000000000000000000000000000000000000000000000000000d 48656c6c6f2c20776f726c642100000000000000000000000000000000000000 +Let us apply the same principle to encode the data for a function with a signature ``g(uint[][],string[])`` with values ``([[1, 2], [3]], ["one", "two", "three"])`` but start from the most atomic parts of the encoding: + +First we encode the length and data of the first embedded dynamic array ``[1, 2]`` of the first root array ``[[1, 2], [3]]``: + + - ``0x0000000000000000000000000000000000000000000000000000000000000002`` (number of elements in the first array, 2; the elements themselves are ``1`` and ``2``) + - ``0x0000000000000000000000000000000000000000000000000000000000000001`` (first element) + - ``0x0000000000000000000000000000000000000000000000000000000000000002`` (second element) + +Then we encode the length and data of the second embedded dynamic array ``[3]`` of the first root array ``[[1, 2], [3]]``: + + - ``0x0000000000000000000000000000000000000000000000000000000000000001`` (number of elements in the second array, 1; the element is ``3``) + - ``0x0000000000000000000000000000000000000000000000000000000000000003`` (first element) + +Then we need to find the offsets ``a`` and ``b`` for their respective dynamic arrays ``[1, 2]`` and ``[3]``. To calculate the offsets we can take a look at the encoded data of the first root array ``[[1, 2], [3]]`` enumerating each line in the encoding: + +.. code-block:: none + + 0 - a - offset of [1, 2] + 1 - b - offset of [3] + 2 - 0000000000000000000000000000000000000000000000000000000000000002 - count for [1, 2] + 3 - 0000000000000000000000000000000000000000000000000000000000000001 - encoding of 1 + 4 - 0000000000000000000000000000000000000000000000000000000000000002 - encoding of 2 + 5 - 0000000000000000000000000000000000000000000000000000000000000001 - count for [3] + 6 - 0000000000000000000000000000000000000000000000000000000000000003 - encoding of 3 + +Offset ``a`` points to the start of the content of the array ``[1, 2]`` which is line 2 (64 bytes); thus ``a = 0x0000000000000000000000000000000000000000000000000000000000000040``. + +Offset ``b`` points to the start of the content of the array ``[3]`` which is line 5 (160 bytes); thus ``b = 0x00000000000000000000000000000000000000000000000000000000000000a0``. + + +Then we encode the embedded strings of the second root array: + + - ``0x0000000000000000000000000000000000000000000000000000000000000003`` (number of characters in word ``"one"``) + - ``0x6f6e650000000000000000000000000000000000000000000000000000000000`` (utf8 representation of word ``"one"``) + - ``0x0000000000000000000000000000000000000000000000000000000000000003`` (number of characters in word ``"two"``) + - ``0x74776f0000000000000000000000000000000000000000000000000000000000`` (utf8 representation of word ``"two"``) + - ``0x0000000000000000000000000000000000000000000000000000000000000005`` (number of characters in word ``"three"``) + - ``0x7468726565000000000000000000000000000000000000000000000000000000`` (utf8 representation of word ``"three"``) + +In parallel to the first root array, since strings are dynamic elements we need to find their offsets ``c``, ``d`` and ``e``: + +.. code-block:: none + + 0 - c - offset for "one" + 1 - d - offset for "two" + 2 - e - offset for "three" + 3 - 0000000000000000000000000000000000000000000000000000000000000003 - count for "one" + 4 - 6f6e650000000000000000000000000000000000000000000000000000000000 - encoding of "one" + 5 - 0000000000000000000000000000000000000000000000000000000000000003 - count for "two" + 6 - 74776f0000000000000000000000000000000000000000000000000000000000 - encoding of "two" + 7 - 0000000000000000000000000000000000000000000000000000000000000005 - count for "three" + 8 - 7468726565000000000000000000000000000000000000000000000000000000 - encoding of "three" + +Offset ``c`` points to the start of the content of the string ``"one"`` which is line 3 (96 bytes); thus ``c = 0x0000000000000000000000000000000000000000000000000000000000000060``. + +Offset ``d`` points to the start of the content of the string ``"two"`` which is line 5 (160 bytes); thus ``d = 0x00000000000000000000000000000000000000000000000000000000000000a0``. + +Offset ``e`` points to the start of the content of the string ``"three"`` which is line 7 (224 bytes); thus ``e = 0x00000000000000000000000000000000000000000000000000000000000000e0``. + + +Note that the encodings of the embedded elements of the root arrays are not dependent on each other and have the same encodings for a function with a signature ``g(string[],uint[][])``. + +Then we encode the length of the first root array: + + - ``0x0000000000000000000000000000000000000000000000000000000000000002`` (number of elements in the first root array, 2; the elements themselves are ``[1, 2]`` and ``[3]``) + +Then we encode the length of the second root array: + + - ``0x0000000000000000000000000000000000000000000000000000000000000003`` (number of strings in the second root array, 3; the strings themselves are ``"one"``, ``"two"`` and ``"three"``) + +Finally we find the offsets ``f`` and ``g`` for their respective root dynamic arrays ``[[1, 2], [3]]`` and ``["one", "two", "three"]``, and assemble parts in the correct order: + +.. code-block:: none + + 0x2289b18c - function signature + 0 - f - offset of [[1, 2], [3]] + 1 - g - offset of ["one", "two", "three"] + 2 - 0000000000000000000000000000000000000000000000000000000000000002 - count for [[1, 2], [3]] + 3 - 0000000000000000000000000000000000000000000000000000000000000040 - offset of [1, 2] + 4 - 00000000000000000000000000000000000000000000000000000000000000a0 - offset of [3] + 5 - 0000000000000000000000000000000000000000000000000000000000000002 - count for [1, 2] + 6 - 0000000000000000000000000000000000000000000000000000000000000001 - encoding of 1 + 7 - 0000000000000000000000000000000000000000000000000000000000000002 - encoding of 2 + 8 - 0000000000000000000000000000000000000000000000000000000000000001 - count for [3] + 9 - 0000000000000000000000000000000000000000000000000000000000000003 - encoding of 3 + 10 - 0000000000000000000000000000000000000000000000000000000000000003 - count for ["one", "two", "three"] + 11 - 0000000000000000000000000000000000000000000000000000000000000060 - offset for "one" + 12 - 00000000000000000000000000000000000000000000000000000000000000a0 - offset for "two" + 13 - 00000000000000000000000000000000000000000000000000000000000000e0 - offset for "three" + 14 - 0000000000000000000000000000000000000000000000000000000000000003 - count for "one" + 15 - 6f6e650000000000000000000000000000000000000000000000000000000000 - encoding of "one" + 16 - 0000000000000000000000000000000000000000000000000000000000000003 - count for "two" + 17 - 74776f0000000000000000000000000000000000000000000000000000000000 - encoding of "two" + 18 - 0000000000000000000000000000000000000000000000000000000000000005 - count for "three" + 19 - 7468726565000000000000000000000000000000000000000000000000000000 - encoding of "three" + +Offset ``f`` points to the start of the content of the array ``[[1, 2], [3]]`` which is line 2 (64 bytes); thus ``f = 0x0000000000000000000000000000000000000000000000000000000000000040``. + +Offset ``g`` points to the start of the content of the array ``["one", "two", "three"]`` which is line 10 (320 bytes); thus ``g = 0x0000000000000000000000000000000000000000000000000000000000000140``. + +.. _abi_events: + Events ====== @@ -292,7 +421,7 @@ In effect, a log entry using this ABI is described as: - ``topics[n]``: ``EVENT_INDEXED_ARGS[n - 1]`` (``EVENT_INDEXED_ARGS`` is the series of ``EVENT_ARGS`` that are indexed); - ``data``: ``abi_serialise(EVENT_NON_INDEXED_ARGS)`` (``EVENT_NON_INDEXED_ARGS`` is the series of ``EVENT_ARGS`` that are not indexed, ``abi_serialise`` is the ABI serialisation function used for returning a series of typed values from a function, as described above). -For all fixed-length Solidity types, the ``EVENT_INDEXED_ARGS`` array contains the 32-byte encoded value directly. However, for *types of dynamic length*, which include ``string``, ``bytes``, and arrays, ``EVENT_INDEXED_ARGS`` will contain the *Keccak hash* of the encoded value, rather than the encoded value directly. This allows applications to efficiently query for values of dynamic-length types (by setting the hash of the encoded value as the topic), but leaves applications unable to decode indexed values they have not queried for. For dynamic-length types, application developers face a trade-off between fast search for predetermined values (if the argument is indexed) and legibility of arbitrary values (which requires that the arguments not be indexed). Developers may overcome this tradeoff and achieve both efficient search and arbitrary legibility by defining events with two arguments — one indexed, one not — intended to hold the same value. +For all fixed-length Solidity types, the ``EVENT_INDEXED_ARGS`` array contains the 32-byte encoded value directly. However, for *types of dynamic length*, which include ``string``, ``bytes``, and arrays, ``EVENT_INDEXED_ARGS`` will contain the *Keccak hash* of the packed encoded value (see :ref:`abi_packed_mode`), rather than the encoded value directly. This allows applications to efficiently query for values of dynamic-length types (by setting the hash of the encoded value as the topic), but leaves applications unable to decode indexed values they have not queried for. For dynamic-length types, application developers face a trade-off between fast search for predetermined values (if the argument is indexed) and legibility of arbitrary values (which requires that the arguments not be indexed). Developers may overcome this tradeoff and achieve both efficient search and arbitrary legibility by defining events with two arguments — one indexed, one not — intended to hold the same value. .. _abi_json: @@ -311,15 +440,19 @@ A function description is a JSON object with the fields: * ``components``: used for tuple types (more below). - ``outputs``: an array of objects similar to ``inputs``, can be omitted if function doesn't return anything; -- ``payable``: ``true`` if function accepts ether, defaults to ``false``; -- ``stateMutability``: a string with one of the following values: ``pure`` (:ref:`specified to not read blockchain state <pure-functions>`), ``view`` (:ref:`specified to not modify the blockchain state <view-functions>`), ``nonpayable`` and ``payable`` (same as ``payable`` above). -- ``constant``: ``true`` if function is either ``pure`` or ``view`` +- ``stateMutability``: a string with one of the following values: ``pure`` (:ref:`specified to not read blockchain state <pure-functions>`), ``view`` (:ref:`specified to not modify the blockchain state <view-functions>`), ``nonpayable`` (function does not accept Ether) and ``payable`` (function accepts Ether); +- ``payable``: ``true`` if function accepts Ether, ``false`` otherwise; +- ``constant``: ``true`` if function is either ``pure`` or ``view``, ``false`` otherwise. -``type`` can be omitted, defaulting to ``"function"``. +``type`` can be omitted, defaulting to ``"function"``, likewise ``payable`` and ``constant`` can be omitted, both defaulting to ``false``. Constructor and fallback function never have ``name`` or ``outputs``. Fallback function doesn't have ``inputs`` either. -Sending non-zero ether to non-payable function will throw. Don't do it. +.. warning:: + The fields ``constant`` and ``payable`` are deprecated and will be removed in the future. Instead, the ``stateMutability`` field can be used to determine the same properties. + +.. note:: + Sending non-zero Ether to non-payable function will revert the transaction. An event description is a JSON object with fairly similar fields: @@ -338,19 +471,19 @@ For example, :: - pragma solidity ^0.4.0; + pragma solidity >0.4.99 <0.6.0; contract Test { - function Test() public { b = 0x12345678901234567890123456789012; } + constructor() public { b = hex"12345678901234567890123456789012"; } event Event(uint indexed a, bytes32 b); event Event2(uint indexed a, bytes32 b); - function foo(uint a) public { Event(a, b); } + function foo(uint a) public { emit Event(a, b); } bytes32 b; } would result in the JSON: -.. code:: json +.. code-block:: json [{ "type":"event", @@ -385,19 +518,19 @@ As an example, the code :: - pragma solidity ^0.4.19; + pragma solidity >=0.4.19 <0.6.0; pragma experimental ABIEncoderV2; contract Test { struct S { uint a; uint[] b; T[] c; } struct T { uint x; uint y; } - function f(S s, T t, uint a) public { } - function g() public returns (S s, T t, uint a) {} + function f(S memory s, T memory t, uint a) public; + function g() public returns (S memory s, T memory t, uint a); } would result in the JSON: -.. code:: json +.. code-block:: json [ { @@ -457,19 +590,31 @@ would result in the JSON: .. _abi_packed_mode: +Strict Encoding Mode +==================== + +Strict encoding mode is the mode that leads to exactly the same encoding as defined in the formal specification above. +This means offsets have to be as small as possible while still not creating overlaps in the data areas and thus no gaps are +allowed. + +Usually, ABI decoders are written in a straigthforward way just following offset pointers, but some decoders +might enforce strict mode. The Solidity ABI decoder currently does not enforce strict mode, but the encoder +always creates data in strict mode. + Non-standard Packed Mode ======================== -Solidity supports a non-standard packed mode where: +Through ``abi.encodePacked()``, Solidity supports a non-standard packed mode where: -- no :ref:`function selector <abi_function_selector>` is encoded, - types shorter than 32 bytes are neither zero padded nor sign extended and - dynamic types are encoded in-place and without the length. -As an example encoding ``int1, bytes1, uint16, string`` with values ``-1, 0x42, 0x2424, "Hello, world!"`` results in :: +As an example encoding ``int8, bytes1, uint16, string`` with values ``-1, 0x42, 0x2424, "Hello, world!"`` results in: + +.. code-block:: none 0xff42242448656c6c6f2c20776f726c6421 - ^^ int1(-1) + ^^ int8(-1) ^^ bytes1(0x42) ^^^^ uint16(0x2424) ^^^^^^^^^^^^^^^^^^^^^^^^^^ string("Hello, world!") without a length field @@ -478,3 +623,8 @@ More specifically, each statically-sized type takes as many bytes as its range h and dynamically-sized types like ``string``, ``bytes`` or ``uint[]`` are encoded without their length field. This means that the encoding is ambiguous as soon as there are two dynamically-sized elements. + +If padding is needed, explicit type conversions can be used: ``abi.encodePacked(uint16(0x12)) == hex"0012"``. + +Since packed encoding is not used when calling functions, there is no special support +for prepending a function selector. diff --git a/docs/assembly.rst b/docs/assembly.rst index 443cb7da..5bb9825a 100644 --- a/docs/assembly.rst +++ b/docs/assembly.rst @@ -4,57 +4,82 @@ Solidity Assembly .. index:: ! assembly, ! asm, ! evmasm -Solidity defines an assembly language that can also be used without Solidity. -This assembly language can also be used as "inline assembly" inside Solidity -source code. We start with describing how to use inline assembly and how it -differs from standalone assembly and then specify assembly itself. +Solidity defines an assembly language that you can use without Solidity and also +as "inline assembly" inside Solidity source code. This guide starts with describing +how to use inline assembly, how it differs from standalone assembly, and +specifies assembly itself. .. _inline-assembly: Inline Assembly =============== -For more fine-grained control especially in order to enhance the language by writing libraries, -it is possible to interleave Solidity statements with inline assembly in a language close -to the one of the virtual machine. Due to the fact that the EVM is a stack machine, it is -often hard to address the correct stack slot and provide arguments to opcodes at the correct -point on the stack. Solidity's inline assembly tries to facilitate that and other issues -arising when writing manual assembly by the following features: +You can interleave Solidity statements with inline assembly in a language close +to the one of the virtual machine. This gives you more fine-grained control, +especially when you are enhancing the language by writing libraries. -* functional-style opcodes: ``mul(1, add(2, 3))`` instead of ``push1 3 push1 2 add push1 1 mul`` +As the EVM is a stack machine, it is often hard to address the correct stack slot +and provide arguments to opcodes at the correct point on the stack. Solidity's inline +assembly helps you do this, and with other issues that arise when writing manual assembly. + +Inline assembly has the following features: + +* functional-style opcodes: ``mul(1, add(2, 3))`` * assembly-local variables: ``let x := add(2, 3) let y := mload(0x40) x := add(x, y)`` * access to external variables: ``function f(uint x) public { assembly { x := sub(x, 1) } }`` -* labels: ``let x := 10 repeat: x := sub(x, 1) jumpi(repeat, eq(x, 0))`` * loops: ``for { let i := 0 } lt(i, x) { i := add(i, 1) } { y := mul(2, y) }`` * if statements: ``if slt(x, 0) { x := sub(0, x) }`` * switch statements: ``switch x case 0 { y := mul(x, 2) } default { y := 0 }`` * function calls: ``function f(x) -> y { switch x case 0 { y := 1 } default { y := mul(x, f(sub(x, 1))) } }`` -We now want to describe the inline assembly language in detail. - .. warning:: Inline assembly is a way to access the Ethereum Virtual Machine - at a low level. This discards several important safety - features of Solidity. + at a low level. This bypasses several important safety + features and checks of Solidity. You should only use it for + tasks that need it, and only if you are confident with using it. + +Syntax +------ + +Assembly parses comments, literals and identifiers in the same way as Solidity, so you can use the +usual ``//`` and ``/* */`` comments. Inline assembly is marked by ``assembly { ... }`` and inside +these curly braces, you can use the following (see the later sections for more details): + + - literals, i.e. ``0x123``, ``42`` or ``"abc"`` (strings up to 32 characters) + - opcodes in functional style, e.g. ``add(1, mlod(0))`` + - variable declarations, e.g. ``let x := 7``, ``let x := add(y, 3)`` or ``let x`` (initial value of empty (0) is assigned) + - identifiers (assembly-local variables and externals if used as inline assembly), e.g. ``add(3, x)``, ``sstore(x_slot, 2)`` + - assignments, e.g. ``x := add(y, 3)`` + - blocks where local variables are scoped inside, e.g. ``{ let x := 3 { let y := add(x, 1) } }`` + +The following features are only available for standalone assembly: + + - direct stack control via ``dup1``, ``swap1``, ... + - direct stack assignments (in "instruction style"), e.g. ``3 =: x`` + - labels, e.g. ``name:`` + - jump opcodes .. note:: - TODO: Write about how scoping rules of inline assembly are a bit different - and the complications that arise when for example using internal functions - of libraries. Furthermore, write about the symbols defined by the compiler. + Standalone assembly is supported for backwards compatibility but is not documented + here anymore. + +At the end of the ``assembly { ... }`` block, the stack must be balanced, +unless you require it otherwise. If it is not balanced, the compiler generates +a warning. 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 at all with "plain Solidity" and the -idea is that assembly libraries will be used to enhance the language in such ways. +load it into a ``bytes`` variable. This is not possible with "plain Solidity" and the +idea is that assembly libraries will be used to enhance the Solidity language. .. code:: - pragma solidity ^0.4.0; + pragma solidity >=0.4.0 <0.6.0; library GetCode { - function at(address _addr) public view returns (bytes o_code) { + function at(address _addr) public view returns (bytes memory o_code) { assembly { // retrieve the size of the code, this needs assembly let size := extcodesize(_addr) @@ -71,19 +96,17 @@ idea is that assembly libraries will be used to enhance the language in such way } } -Inline assembly could also be beneficial in cases where the optimizer fails to produce -efficient code. Please be aware that assembly is much more difficult to write because -the compiler does not perform checks, so you should use it for complex things only if -you really know what you are doing. +Inline assembly is also beneficial in cases where the optimizer fails to produce +efficient code, for example: .. code:: - pragma solidity ^0.4.16; + pragma solidity >=0.4.16 <0.6.0; library VectorSum { // This function is less efficient because the optimizer currently fails to // remove the bounds checks in array access. - function sumSolidity(uint[] _data) public view returns (uint o_sum) { + function sumSolidity(uint[] memory _data) public pure returns (uint o_sum) { for (uint i = 0; i < _data.length; ++i) o_sum += _data[i]; } @@ -91,7 +114,7 @@ you really know what you are doing. // We know that we only access the array in bounds, so we can avoid the check. // 0x20 needs to be added to an array because the first slot contains the // array length. - function sumAsm(uint[] _data) public view returns (uint o_sum) { + function sumAsm(uint[] memory _data) public pure returns (uint o_sum) { for (uint i = 0; i < _data.length; ++i) { assembly { o_sum := add(o_sum, mload(add(add(_data, 0x20), mul(i, 0x20)))) @@ -100,7 +123,7 @@ you really know what you are doing. } // Same as above, but accomplish the entire code within inline assembly. - function sumPureAsm(uint[] _data) public view returns (uint o_sum) { + function sumPureAsm(uint[] memory _data) public pure returns (uint o_sum) { assembly { // Load the length (first 32 bytes) let len := mload(_data) @@ -115,7 +138,7 @@ you really know what you are doing. // Iterate until the bound is not met. for - { let end := add(data, len) } + { let end := add(data, mul(len, 0x20)) } lt(data, end) { data := add(data, 0x20) } { @@ -126,22 +149,7 @@ you really know what you are doing. } -Syntax ------- - -Assembly parses comments, literals and identifiers exactly as Solidity, so you can use the -usual ``//`` and ``/* */`` comments. Inline assembly is marked by ``assembly { ... }`` and inside -these curly braces, the following can be used (see the later sections for more details) - - - literals, i.e. ``0x123``, ``42`` or ``"abc"`` (strings up to 32 characters) - - opcodes (in "instruction style"), e.g. ``mload sload dup1 sstore``, for a list see below - - opcodes in functional style, e.g. ``add(1, mlod(0))`` - - labels, e.g. ``name:`` - - variable declarations, e.g. ``let x := 7``, ``let x := add(y, 3)`` or ``let x`` (initial value of empty (0) is assigned) - - identifiers (labels or assembly-local variables and externals if used as inline assembly), e.g. ``jump(name)``, ``3 x add`` - - assignments (in "instruction style"), e.g. ``3 =: x`` - - assignments in functional style, e.g. ``x := add(y, 3)`` - - blocks where local variables are scoped inside, e.g. ``{ let x := 3 { let y := add(x, 1) } }`` +.. _opcodes: Opcodes ------- @@ -157,7 +165,7 @@ Opcodes marked with ``F``, ``H``, ``B`` or ``C`` are present since Frontier, Hom Constantinople is still in planning and all instructions marked as such will result in an invalid instruction exception. In the following, ``mem[a...b)`` signifies the bytes of memory starting at position ``a`` up to -(excluding) position ``b`` and ``storage[p]`` signifies the storage contents at position ``p``. +but not including position ``b`` and ``storage[p]`` signifies the storage contents at position ``p``. The opcodes ``pushi`` and ``jumpdest`` cannot be used directly. @@ -212,16 +220,14 @@ In the grammar, opcodes are represented as pre-defined identifiers. +-------------------------+-----+---+-----------------------------------------------------------------+ | sar(x, y) | | C | arithmetic shift right y by x bits | +-------------------------+-----+---+-----------------------------------------------------------------+ -| addmod(x, y, m) | | F | (x + y) % m with arbitrary precision arithmetics | +| addmod(x, y, m) | | F | (x + y) % m with arbitrary precision arithmetic | +-------------------------+-----+---+-----------------------------------------------------------------+ -| mulmod(x, y, m) | | F | (x * y) % m with arbitrary precision arithmetics | +| mulmod(x, y, m) | | F | (x * y) % m with arbitrary precision arithmetic | +-------------------------+-----+---+-----------------------------------------------------------------+ | signextend(i, x) | | F | sign extend from (i*8+7)th bit counting from least significant | +-------------------------+-----+---+-----------------------------------------------------------------+ | keccak256(p, n) | | F | keccak(mem[p...(p+n))) | +-------------------------+-----+---+-----------------------------------------------------------------+ -| sha3(p, n) | | F | keccak(mem[p...(p+n))) | -+-------------------------+-----+---+-----------------------------------------------------------------+ | jump(label) | `-` | F | jump to label / code position | +-------------------------+-----+---+-----------------------------------------------------------------+ | jumpi(label, cond) | `-` | F | jump to label if cond is nonzero | @@ -230,13 +236,13 @@ In the grammar, opcodes are represented as pre-defined identifiers. +-------------------------+-----+---+-----------------------------------------------------------------+ | pop(x) | `-` | F | remove the element pushed by x | +-------------------------+-----+---+-----------------------------------------------------------------+ -| dup1 ... dup16 | | F | copy ith stack slot to the top (counting from top) | +| dup1 ... dup16 | | F | copy nth stack slot to the top (counting from top) | +-------------------------+-----+---+-----------------------------------------------------------------+ -| swap1 ... swap16 | `*` | F | swap topmost and ith stack slot below it | +| swap1 ... swap16 | `*` | F | swap topmost and nth stack slot below it | +-------------------------+-----+---+-----------------------------------------------------------------+ -| mload(p) | | F | mem[p..(p+32)) | +| mload(p) | | F | mem[p...(p+32)) | +-------------------------+-----+---+-----------------------------------------------------------------+ -| mstore(p, v) | `-` | F | mem[p..(p+32)) := v | +| mstore(p, v) | `-` | F | mem[p...(p+32)) := v | +-------------------------+-----+---+-----------------------------------------------------------------+ | mstore8(p, v) | `-` | F | mem[p] := v & 0xff (only modifies a single byte) | +-------------------------+-----+---+-----------------------------------------------------------------+ @@ -274,16 +280,20 @@ In the grammar, opcodes are represented as pre-defined identifiers. +-------------------------+-----+---+-----------------------------------------------------------------+ | returndatacopy(t, f, s) | `-` | B | copy s bytes from returndata at position f to mem at position t | +-------------------------+-----+---+-----------------------------------------------------------------+ -| create(v, p, s) | | F | create new contract with code mem[p..(p+s)) and send v wei | +| extcodehash(a) | | C | code hash of address a | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| create(v, p, n) | | F | create new contract with code mem[p...(p+n)) and send v wei | | | | | and return the new address | +-------------------------+-----+---+-----------------------------------------------------------------+ -| create2(v, n, p, s) | | C | create new contract with code mem[p..(p+s)) at address | -| | | | keccak256(<address> . n . keccak256(mem[p..(p+s))) and send v | -| | | | wei and return the new address | +| create2(v, p, n, s) | | C | create new contract with code mem[p...(p+n)) at address | +| | | | keccak256(0xff . this . s . keccak256(mem[p...(p+n))) | +| | | | and send v wei and return the new address, where ``0xff`` is a | +| | | | 8 byte value, ``this`` is the current contract's address | +| | | | as a 20 byte value and ``s`` is a big-endian 256-bit value | +-------------------------+-----+---+-----------------------------------------------------------------+ -| call(g, a, v, in, | | F | call contract at address a with input mem[in..(in+insize)) | +| call(g, a, v, in, | | F | call contract at address a with input mem[in...(in+insize)) | | insize, out, outsize) | | | providing g gas and v wei and output area | -| | | | mem[out..(out+outsize)) returning 0 on error (eg. out of gas) | +| | | | mem[out...(out+outsize)) returning 0 on error (eg. out of gas) | | | | | and 1 on success | +-------------------------+-----+---+-----------------------------------------------------------------+ | callcode(g, a, v, in, | | F | identical to ``call`` but only use the code from a and stay | @@ -295,23 +305,23 @@ In the grammar, opcodes are represented as pre-defined identifiers. | staticcall(g, a, in, | | B | identical to ``call(g, a, 0, in, insize, out, outsize)`` but do | | insize, out, outsize) | | | not allow state modifications | +-------------------------+-----+---+-----------------------------------------------------------------+ -| return(p, s) | `-` | F | end execution, return data mem[p..(p+s)) | +| return(p, s) | `-` | F | end execution, return data mem[p...(p+s)) | +-------------------------+-----+---+-----------------------------------------------------------------+ -| revert(p, s) | `-` | B | end execution, revert state changes, return data mem[p..(p+s)) | +| revert(p, s) | `-` | B | end execution, revert state changes, return data mem[p...(p+s)) | +-------------------------+-----+---+-----------------------------------------------------------------+ | selfdestruct(a) | `-` | F | end execution, destroy current contract and send funds to a | +-------------------------+-----+---+-----------------------------------------------------------------+ | invalid | `-` | F | end execution with invalid instruction | +-------------------------+-----+---+-----------------------------------------------------------------+ -| log0(p, s) | `-` | F | log without topics and data mem[p..(p+s)) | +| log0(p, s) | `-` | F | log without topics and data mem[p...(p+s)) | +-------------------------+-----+---+-----------------------------------------------------------------+ -| log1(p, s, t1) | `-` | F | log with topic t1 and data mem[p..(p+s)) | +| log1(p, s, t1) | `-` | F | log with topic t1 and data mem[p...(p+s)) | +-------------------------+-----+---+-----------------------------------------------------------------+ -| log2(p, s, t1, t2) | `-` | F | log with topics t1, t2 and data mem[p..(p+s)) | +| log2(p, s, t1, t2) | `-` | F | log with topics t1, t2 and data mem[p...(p+s)) | +-------------------------+-----+---+-----------------------------------------------------------------+ -| log3(p, s, t1, t2, t3) | `-` | F | log with topics t1, t2, t3 and data mem[p..(p+s)) | +| log3(p, s, t1, t2, t3) | `-` | F | log with topics t1, t2, t3 and data mem[p...(p+s)) | +-------------------------+-----+---+-----------------------------------------------------------------+ -| log4(p, s, t1, t2, t3, | `-` | F | log with topics t1, t2, t3, t4 and data mem[p..(p+s)) | +| log4(p, s, t1, t2, t3, | `-` | F | log with topics t1, t2, t3, t4 and data mem[p...(p+s)) | | t4) | | | | +-------------------------+-----+---+-----------------------------------------------------------------+ | origin | | F | transaction sender | @@ -337,75 +347,66 @@ Literals You can use integer constants by typing them in decimal or hexadecimal notation and an appropriate ``PUSHi`` instruction will automatically be generated. The following creates code to add 2 and 3 resulting in 5 and then computes the bitwise and with the string "abc". +The final value is assigned to a local variable called ``x``. Strings are stored left-aligned and cannot be longer than 32 bytes. .. code:: - assembly { 2 3 add "abc" and } + assembly { let x := and("abc", add(3, 2)) } + Functional Style ----------------- -You can type opcode after opcode in the same way they will end up in bytecode. For example -adding ``3`` to the contents in memory at position ``0x80`` would be +For a sequence of opcodes, it is often hard to see what the actual +arguments for certain opcodes are. In the following example, +``3`` is added to the contents in memory at position ``0x80``. .. code:: 3 0x80 mload add 0x80 mstore -As it is often hard to see what the actual arguments for certain opcodes are, -Solidity inline assembly also provides a "functional style" notation where the same code -would be written as follows +Solidity inline assembly has a "functional style" notation where the same code +would be written as follows: .. code:: mstore(0x80, add(mload(0x80), 3)) -Functional style expressions cannot use instructional style internally, i.e. -``1 2 mstore(0x80, add)`` is not valid assembly, it has to be written as -``mstore(0x80, add(2, 1))``. For opcodes that do not take arguments, the -parentheses can be omitted. - -Note that the order of arguments is reversed in functional-style as opposed to the instruction-style -way. If you use functional-style, the first argument will end up on the stack top. +If you read the code from right to left, you end up with exactly the same +sequence of constants and opcodes, but it is much clearer where the +values end up. +If you care about the exact stack layout, just note that the +syntactically first argument for a function or opcode will be put at the +top of the stack. -Access to External Variables and Functions ------------------------------------------- +Access to External Variables, Functions and Libraries +----------------------------------------------------- -Solidity variables and other identifiers can be accessed by simply using their name. -For memory variables, this will push the address and not the value onto the -stack. Storage variables are different: Values in storage might not occupy a -full storage slot, so their "address" is composed of a slot and a byte-offset +You can access Solidity variables and other identifiers by using their name. +For variables stored in the memory data location, this pushes the address, and not the value +onto the stack. Variables stored in the storage data location are different, as they might not +occupy a full storage slot, so their "address" is composed of a slot and a byte-offset inside that slot. To retrieve the slot pointed to by the variable ``x``, you -used ``x_slot`` and to retrieve the byte-offset you used ``x_offset``. - -In assignments (see below), we can even use local Solidity variables to assign to. - -Functions external to inline assembly can also be accessed: The assembly will -push their entry label (with virtual function resolution applied). The calling semantics -in solidity are: - - - the caller pushes ``return label``, ``arg1``, ``arg2``, ..., ``argn`` - - the call returns with ``ret1``, ``ret2``, ..., ``retm`` +use ``x_slot``, and to retrieve the byte-offset you use ``x_offset``. -This feature is still a bit cumbersome to use, because the stack offset essentially -changes during the call, and thus references to local variables will be wrong. +Local Solidity variables are available for assignments, for example: .. code:: - pragma solidity ^0.4.11; + pragma solidity >=0.4.11 <0.6.0; contract C { uint b; - function f(uint x) public returns (uint r) { + function f(uint x) public view returns (uint r) { assembly { r := mul(x, sload(b_slot)) // ignore the offset, we know it is zero } } } -.. note:: +.. warning:: If you access variables of a type that spans less than 256 bits (for example ``uint64``, ``address``, ``bytes16`` or ``byte``), you cannot make any assumptions about bits not part of the @@ -418,57 +419,8 @@ changes during the call, and thus references to local variables will be wrong. Labels ------ -.. note:: - Labels are deprecated. Please use functions, loops, if or switch statements instead. - -Another problem in EVM assembly is that ``jump`` and ``jumpi`` use absolute addresses -which can change easily. Solidity inline assembly provides labels to make the use of -jumps easier. Note that labels are a low-level feature and it is possible to write -efficient assembly without labels, just using assembly functions, loops, if and switch instructions -(see below). The following code computes an element in the Fibonacci series. - -.. code:: - - { - let n := calldataload(4) - let a := 1 - let b := a - loop: - jumpi(loopend, eq(n, 0)) - a add swap1 - n := sub(n, 1) - jump(loop) - loopend: - mstore(0, a) - return(0, 0x20) - } - -Please note that automatically accessing stack variables can only work if the -assembler knows the current stack height. This fails to work if the jump source -and target have different stack heights. It is still fine to use such jumps, but -you should just not access any stack variables (even assembly variables) in that case. - -Furthermore, the stack height analyser goes through the code opcode by opcode -(and not according to control flow), so in the following case, the assembler -will have a wrong impression about the stack height at label ``two``: - -.. code:: - - { - let x := 8 - jump(two) - one: - // Here the stack height is 2 (because we pushed x and 7), - // but the assembler thinks it is 1 because it reads - // from top to bottom. - // Accessing the stack variable x here will lead to errors. - x := 9 - jump(three) - two: - 7 // push something onto the stack - jump(one) - three: - } +Support for labels has been removed in version 0.5.0 of Solidity. +Please use functions, loops, if or switch statements instead. Declaring Assembly-Local Variables ---------------------------------- @@ -482,7 +434,7 @@ be just ``0``, but it can also be a complex functional-style expression. .. code:: - pragma solidity ^0.4.16; + pragma solidity >=0.4.16 <0.6.0; contract C { function f(uint x) public view returns (uint b) { @@ -506,26 +458,19 @@ 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. -There are two kinds of assignments: functional-style and instruction-style. -For functional-style assignments (``variable := value``), you need to provide a value in a -functional-style expression that results in exactly one stack value -and for instruction-style (``=: variable``), the value is just taken from the stack top. -For both ways, the colon points to the name of the variable. The assignment -is performed by replacing the variable's value on the stack by the new value. +Variables can only be assigned expressions that result in exactly one value. +If you want to assign the values returned from a function that has +multiple return parameters, you have to provide multiple variables. .. code:: { - let v := 0 // functional-style assignment as part of variable declaration + let v := 0 let g := add(v, 2) - sload(10) - =: v // instruction style assignment, puts the result of sload(10) into v + function f() -> a, b { } + let c, d := f() } -.. note:: - Instruction-style assignment is deprecated. - - If -- @@ -642,12 +587,9 @@ Things to Avoid Inline assembly might have a quite high-level look, but it actually is extremely low-level. Function calls, loops, ifs and switches are converted by simple rewriting rules and after that, the only thing the assembler does for you is re-arranging -functional-style opcodes, managing jump labels, counting stack height for +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. Especially for those two last cases, it is important -to know that the assembler only counts stack height from top to bottom, not -necessarily following control flow. Furthermore, operations like swap will only -swap the contents of the stack but not the location of variables. +of their block is reached. Conventions in Solidity ----------------------- @@ -662,20 +604,31 @@ first. Solidity manages memory in a very simple way: There is a "free memory pointer" at position ``0x40`` in memory. If you want to allocate memory, just use the memory -from that point on and update the pointer accordingly. +starting from where this pointer points at and update it accordingly. +There is no guarantee that the memory has not been used before and thus +you cannot assume that its contents are zero bytes. +There is no built-in mechanism to release or free allocated memory. +Here is an assembly snippet that can be used for allocating memory:: + + function allocate(length) -> pos { + pos := mload(0x40) + mstore(0x40, add(pos, length)) + } The first 64 bytes of memory can be used as "scratch space" for short-term allocation. The 32 bytes after the free memory pointer (i.e. starting at ``0x60``) is meant to be zero permanently and is used as the initial value for empty dynamic memory arrays. +This means that the allocatable memory starts at ``0x80``, which is the initial value +of the free memory pointer. Elements in memory arrays in Solidity always occupy multiples of 32 bytes (yes, this is even true for ``byte[]``, but not for ``bytes`` and ``string``). Multi-dimensional memory arrays are pointers to memory arrays. The length of a dynamic array is stored at the -first slot of the array and then only the array elements follow. +first slot of the array and followed by the array elements. .. warning:: - Statically-sized memory arrays do not have a length field, but it will be added soon + Statically-sized memory arrays do not have a length field, but it might be added later to allow better convertibility between statically- and dynamically-sized arrays, so please do not rely on that. @@ -710,7 +663,7 @@ Scoping: An identifier that is declared (label, variable, function, assembly) is only visible in the block where it was declared (including nested blocks inside the current block). It is not legal to access local variables across function borders, even if they would be in scope. Shadowing is not allowed. -Local variables cannot be accessed before they were declared, but labels, +Local variables cannot be accessed before they were declared, but functions and assemblies can. Assemblies are special blocks that are used for e.g. returning runtime code or creating contracts. No identifier from an outer assembly is visible in a sub-assembly. @@ -721,7 +674,7 @@ Whenever a local variable is referenced, the code generator needs to know its current relative position in the stack and thus it needs to keep track of the current so-called stack height. Since all local variables are removed at the end of a block, the stack height before and after the block -should be the same. If this is not the case, a warning is issued. +should be the same. If this is not the case, compilation fails. Using ``switch``, ``for`` and functions, it should be possible to write complex code without using ``jump`` or ``jumpi`` manually. This makes it much @@ -738,7 +691,7 @@ Example: We will follow an example compilation from Solidity to assembly. We consider the runtime bytecode of the following Solidity program:: - pragma solidity ^0.4.16; + pragma solidity >=0.4.16 <0.6.0; contract C { function f(uint x) public pure returns (uint y) { @@ -751,7 +704,7 @@ We consider the runtime bytecode of the following Solidity program:: The following assembly will be generated:: { - mstore(0x40, 0x60) // store the "free memory pointer" + mstore(0x40, 0x80) // store the "free memory pointer" // function dispatcher switch div(calldataload(0), exp(2, 226)) case 0xb3de648b { diff --git a/docs/bugs.json b/docs/bugs.json index c1e377a4..28c0fe62 100644 --- a/docs/bugs.json +++ b/docs/bugs.json @@ -17,12 +17,13 @@ "check": {"ast-compact-json-path": "$..[?(@.nodeType === 'EventDefinition')]..[?(@.nodeType === 'UserDefinedTypeName' && @.typeDescriptions.typeString.startsWith('struct'))]"} }, { - "name": "PublicLibFunctionsDoNotReturnNestedArrays", - "summary": "Calls to public library functions (internal functions are safe) that return nested arrays return only zeroes.", - "description": "The compiler does not complain about public library functions (internal functions are safe) returning nested arrays, but it also does not return it correctly. Thus, the function caller receives only zeroes.", - "introduced": "0.4.11", + "name": "NestedArrayFunctionCallDecoder", + "summary": "Calling functions that return multi-dimensional fixed-size arrays can result in memory corruption.", + "description": "If Solidity code calls a function that returns a multi-dimensional fixed-size array, array elements are incorrectly interpreted as memory pointers and thus can cause memory corruption if the return values are accessed. Calling functions with multi-dimensional fixed-size arrays is unaffected as is returning fixed-size arrays from function calls. The regular expression only checks if such functions are present, not if they are called, which is required for the contract to be affected.", + "introduced": "0.1.4", "fixed": "0.4.22", - "severity": "low" + "severity": "medium", + "check": {"regex-source": "returns[^;{]*\\[\\s*[^\\] \\t\\r\\n\\v\\f][^\\]]*\\]\\s*\\[\\s*[^\\] \\t\\r\\n\\v\\f][^\\]]*\\][^{;]*[;{]"} }, { "name": "OneOfTwoConstructorsSkipped", @@ -32,15 +33,6 @@ "fixed": "0.4.23", "severity": "very low" }, - { - "name": "NestedArrayFunctionCallDecoder", - "summary": "Calling functions that return multi-dimensional fixed-size arrays can result in memory corruption.", - "description": "If Solidity code calls a function that returns a multi-dimensional fixed-size array, array elements are incorrectly interpreted as memory pointers and thus can cause memory corruption if the return values are accessed. Calling functions with multi-dimensional fixed-size arrays is unaffected as is returning fixed-size arrays from function calls. The regular expression only checks if such functions are present, not if they are called, which is required for the contract to be affected.", - "introduced": "0.1.4", - "fixed": "0.4.22", - "severity": "medium", - "check": {"regex-source": "returns[^;{]*\\[\\s*[^\\] \\t\\r\\n\\v\\f][^\\]]*\\]\\s*\\[\\s*[^\\] \\t\\r\\n\\v\\f][^\\]]*\\][^{;]*[;{]"} - }, { "name": "ZeroFunctionSelector", "summary": "It is possible to craft the name of a function such that it is executed instead of the fallback function in very specific circumstances.", diff --git a/docs/bugs.rst b/docs/bugs.rst index 8e3382c8..f7522183 100644 --- a/docs/bugs.rst +++ b/docs/bugs.rst @@ -57,15 +57,14 @@ conditions means that the optimizer has to be switched on to enable the bug. If no conditions are given, assume that the bug is present. check - This field contains different checks that can be used to determine - whether a smart contract + This field contains different checks that report whether the smart contract contains the bug or not. The first type of check are Javascript regular - expressions that are to be matched against the source code ("source-regex"). - If there is no match, then the bug is very likely + expressions that are to be matched against the source code ("source-regex") + if the bug is present. If there is no match, then the bug is very likely not present. If there is a match, the bug might be present. For improved accuracy, the checks should be applied to the source code after stripping comments. - The second type of check are patterns to be applied to the compact AST of + The second type of check are patterns to be checked on the compact AST of the Solidity program ("ast-compact-json-path"). The specified search query is a `JsonPath <https://github.com/json-path/JsonPath>`_ expression. If at least one path of the Solidity AST matches the query, the bug is diff --git a/docs/bugs_by_version.json b/docs/bugs_by_version.json index ef084660..2631b286 100644 --- a/docs/bugs_by_version.json +++ b/docs/bugs_by_version.json @@ -1,623 +1,616 @@ { "0.1.0": { "bugs": [ - "ExpExponentCleanup", - "ZeroFunctionSelector", - "ECRecoverMalformedInput", - "SkipEmptyStringLiteral", - "ConstantOptimizerSubtraction", - "IdentityPrecompileReturnIgnored", - "OptimizerStaleKnowledgeAboutSHA3", - "SendFailsForZeroEther", - "DynamicAllocationInfiniteLoop", - "OptimizerClearStateOnCodePathJoin", - "CleanBytesHigherOrderBits", - "ArrayAccessCleanHigherOrderBits", + "ExpExponentCleanup", + "ZeroFunctionSelector", + "ECRecoverMalformedInput", + "SkipEmptyStringLiteral", + "ConstantOptimizerSubtraction", + "IdentityPrecompileReturnIgnored", + "OptimizerStaleKnowledgeAboutSHA3", + "SendFailsForZeroEther", + "DynamicAllocationInfiniteLoop", + "OptimizerClearStateOnCodePathJoin", + "CleanBytesHigherOrderBits", + "ArrayAccessCleanHigherOrderBits", "AncientCompiler" - ], + ], "released": "2015-07-10" - }, + }, "0.1.1": { "bugs": [ - "ExpExponentCleanup", - "ZeroFunctionSelector", - "ECRecoverMalformedInput", - "SkipEmptyStringLiteral", - "ConstantOptimizerSubtraction", - "IdentityPrecompileReturnIgnored", - "OptimizerStaleKnowledgeAboutSHA3", - "SendFailsForZeroEther", - "DynamicAllocationInfiniteLoop", - "OptimizerClearStateOnCodePathJoin", - "CleanBytesHigherOrderBits", - "ArrayAccessCleanHigherOrderBits", + "ExpExponentCleanup", + "ZeroFunctionSelector", + "ECRecoverMalformedInput", + "SkipEmptyStringLiteral", + "ConstantOptimizerSubtraction", + "IdentityPrecompileReturnIgnored", + "OptimizerStaleKnowledgeAboutSHA3", + "SendFailsForZeroEther", + "DynamicAllocationInfiniteLoop", + "OptimizerClearStateOnCodePathJoin", + "CleanBytesHigherOrderBits", + "ArrayAccessCleanHigherOrderBits", "AncientCompiler" - ], + ], "released": "2015-08-04" - }, + }, "0.1.2": { "bugs": [ - "ExpExponentCleanup", - "ZeroFunctionSelector", - "ECRecoverMalformedInput", - "SkipEmptyStringLiteral", - "ConstantOptimizerSubtraction", - "IdentityPrecompileReturnIgnored", - "OptimizerStaleKnowledgeAboutSHA3", - "SendFailsForZeroEther", - "DynamicAllocationInfiniteLoop", - "OptimizerClearStateOnCodePathJoin", - "CleanBytesHigherOrderBits", - "ArrayAccessCleanHigherOrderBits", + "ExpExponentCleanup", + "ZeroFunctionSelector", + "ECRecoverMalformedInput", + "SkipEmptyStringLiteral", + "ConstantOptimizerSubtraction", + "IdentityPrecompileReturnIgnored", + "OptimizerStaleKnowledgeAboutSHA3", + "SendFailsForZeroEther", + "DynamicAllocationInfiniteLoop", + "OptimizerClearStateOnCodePathJoin", + "CleanBytesHigherOrderBits", + "ArrayAccessCleanHigherOrderBits", "AncientCompiler" - ], + ], "released": "2015-08-20" - }, + }, "0.1.3": { "bugs": [ - "ExpExponentCleanup", - "ZeroFunctionSelector", - "ECRecoverMalformedInput", - "SkipEmptyStringLiteral", - "ConstantOptimizerSubtraction", - "IdentityPrecompileReturnIgnored", - "OptimizerStaleKnowledgeAboutSHA3", - "SendFailsForZeroEther", - "DynamicAllocationInfiniteLoop", - "OptimizerClearStateOnCodePathJoin", - "CleanBytesHigherOrderBits", - "ArrayAccessCleanHigherOrderBits", + "ExpExponentCleanup", + "ZeroFunctionSelector", + "ECRecoverMalformedInput", + "SkipEmptyStringLiteral", + "ConstantOptimizerSubtraction", + "IdentityPrecompileReturnIgnored", + "OptimizerStaleKnowledgeAboutSHA3", + "SendFailsForZeroEther", + "DynamicAllocationInfiniteLoop", + "OptimizerClearStateOnCodePathJoin", + "CleanBytesHigherOrderBits", + "ArrayAccessCleanHigherOrderBits", "AncientCompiler" - ], + ], "released": "2015-09-25" - }, + }, "0.1.4": { "bugs": [ - "ExpExponentCleanup", - "NestedArrayFunctionCallDecoder", - "ZeroFunctionSelector", - "ECRecoverMalformedInput", - "SkipEmptyStringLiteral", - "ConstantOptimizerSubtraction", - "IdentityPrecompileReturnIgnored", - "OptimizerStaleKnowledgeAboutSHA3", - "SendFailsForZeroEther", - "DynamicAllocationInfiniteLoop", - "OptimizerClearStateOnCodePathJoin", - "CleanBytesHigherOrderBits", - "ArrayAccessCleanHigherOrderBits", + "ExpExponentCleanup", + "NestedArrayFunctionCallDecoder", + "ZeroFunctionSelector", + "ECRecoverMalformedInput", + "SkipEmptyStringLiteral", + "ConstantOptimizerSubtraction", + "IdentityPrecompileReturnIgnored", + "OptimizerStaleKnowledgeAboutSHA3", + "SendFailsForZeroEther", + "DynamicAllocationInfiniteLoop", + "OptimizerClearStateOnCodePathJoin", + "CleanBytesHigherOrderBits", + "ArrayAccessCleanHigherOrderBits", "AncientCompiler" - ], + ], "released": "2015-09-30" - }, + }, "0.1.5": { "bugs": [ - "ExpExponentCleanup", - "NestedArrayFunctionCallDecoder", - "ZeroFunctionSelector", - "ECRecoverMalformedInput", - "SkipEmptyStringLiteral", - "ConstantOptimizerSubtraction", - "IdentityPrecompileReturnIgnored", - "OptimizerStaleKnowledgeAboutSHA3", - "SendFailsForZeroEther", - "DynamicAllocationInfiniteLoop", - "OptimizerClearStateOnCodePathJoin", - "CleanBytesHigherOrderBits", - "ArrayAccessCleanHigherOrderBits", + "ExpExponentCleanup", + "NestedArrayFunctionCallDecoder", + "ZeroFunctionSelector", + "ECRecoverMalformedInput", + "SkipEmptyStringLiteral", + "ConstantOptimizerSubtraction", + "IdentityPrecompileReturnIgnored", + "OptimizerStaleKnowledgeAboutSHA3", + "SendFailsForZeroEther", + "DynamicAllocationInfiniteLoop", + "OptimizerClearStateOnCodePathJoin", + "CleanBytesHigherOrderBits", + "ArrayAccessCleanHigherOrderBits", "AncientCompiler" - ], + ], "released": "2015-10-07" - }, + }, "0.1.6": { "bugs": [ - "ExpExponentCleanup", - "NestedArrayFunctionCallDecoder", - "ZeroFunctionSelector", - "ECRecoverMalformedInput", - "SkipEmptyStringLiteral", - "ConstantOptimizerSubtraction", - "IdentityPrecompileReturnIgnored", - "HighOrderByteCleanStorage", - "OptimizerStaleKnowledgeAboutSHA3", - "SendFailsForZeroEther", - "DynamicAllocationInfiniteLoop", - "OptimizerClearStateOnCodePathJoin", - "CleanBytesHigherOrderBits", - "ArrayAccessCleanHigherOrderBits", + "ExpExponentCleanup", + "NestedArrayFunctionCallDecoder", + "ZeroFunctionSelector", + "ECRecoverMalformedInput", + "SkipEmptyStringLiteral", + "ConstantOptimizerSubtraction", + "IdentityPrecompileReturnIgnored", + "HighOrderByteCleanStorage", + "OptimizerStaleKnowledgeAboutSHA3", + "SendFailsForZeroEther", + "DynamicAllocationInfiniteLoop", + "OptimizerClearStateOnCodePathJoin", + "CleanBytesHigherOrderBits", + "ArrayAccessCleanHigherOrderBits", "AncientCompiler" - ], + ], "released": "2015-10-16" - }, + }, "0.1.7": { "bugs": [ - "ExpExponentCleanup", - "NestedArrayFunctionCallDecoder", - "ZeroFunctionSelector", - "ECRecoverMalformedInput", - "SkipEmptyStringLiteral", - "ConstantOptimizerSubtraction", - "IdentityPrecompileReturnIgnored", - "HighOrderByteCleanStorage", - "OptimizerStaleKnowledgeAboutSHA3", - "SendFailsForZeroEther", - "DynamicAllocationInfiniteLoop", - "OptimizerClearStateOnCodePathJoin", - "CleanBytesHigherOrderBits", - "ArrayAccessCleanHigherOrderBits", + "ExpExponentCleanup", + "NestedArrayFunctionCallDecoder", + "ZeroFunctionSelector", + "ECRecoverMalformedInput", + "SkipEmptyStringLiteral", + "ConstantOptimizerSubtraction", + "IdentityPrecompileReturnIgnored", + "HighOrderByteCleanStorage", + "OptimizerStaleKnowledgeAboutSHA3", + "SendFailsForZeroEther", + "DynamicAllocationInfiniteLoop", + "OptimizerClearStateOnCodePathJoin", + "CleanBytesHigherOrderBits", + "ArrayAccessCleanHigherOrderBits", "AncientCompiler" - ], + ], "released": "2015-11-17" - }, + }, "0.2.0": { "bugs": [ - "ExpExponentCleanup", - "NestedArrayFunctionCallDecoder", - "ZeroFunctionSelector", - "ECRecoverMalformedInput", - "SkipEmptyStringLiteral", - "ConstantOptimizerSubtraction", - "IdentityPrecompileReturnIgnored", - "HighOrderByteCleanStorage", - "OptimizerStaleKnowledgeAboutSHA3", - "SendFailsForZeroEther", - "DynamicAllocationInfiniteLoop", - "OptimizerClearStateOnCodePathJoin", - "CleanBytesHigherOrderBits", - "ArrayAccessCleanHigherOrderBits", + "ExpExponentCleanup", + "NestedArrayFunctionCallDecoder", + "ZeroFunctionSelector", + "ECRecoverMalformedInput", + "SkipEmptyStringLiteral", + "ConstantOptimizerSubtraction", + "IdentityPrecompileReturnIgnored", + "HighOrderByteCleanStorage", + "OptimizerStaleKnowledgeAboutSHA3", + "SendFailsForZeroEther", + "DynamicAllocationInfiniteLoop", + "OptimizerClearStateOnCodePathJoin", + "CleanBytesHigherOrderBits", + "ArrayAccessCleanHigherOrderBits", "AncientCompiler" - ], + ], "released": "2015-12-02" - }, + }, "0.2.1": { "bugs": [ - "ExpExponentCleanup", - "NestedArrayFunctionCallDecoder", - "ZeroFunctionSelector", - "ECRecoverMalformedInput", - "SkipEmptyStringLiteral", - "ConstantOptimizerSubtraction", - "IdentityPrecompileReturnIgnored", - "HighOrderByteCleanStorage", - "OptimizerStaleKnowledgeAboutSHA3", - "SendFailsForZeroEther", - "DynamicAllocationInfiniteLoop", - "OptimizerClearStateOnCodePathJoin", - "CleanBytesHigherOrderBits", - "ArrayAccessCleanHigherOrderBits", + "ExpExponentCleanup", + "NestedArrayFunctionCallDecoder", + "ZeroFunctionSelector", + "ECRecoverMalformedInput", + "SkipEmptyStringLiteral", + "ConstantOptimizerSubtraction", + "IdentityPrecompileReturnIgnored", + "HighOrderByteCleanStorage", + "OptimizerStaleKnowledgeAboutSHA3", + "SendFailsForZeroEther", + "DynamicAllocationInfiniteLoop", + "OptimizerClearStateOnCodePathJoin", + "CleanBytesHigherOrderBits", + "ArrayAccessCleanHigherOrderBits", "AncientCompiler" - ], + ], "released": "2016-01-30" - }, + }, "0.2.2": { "bugs": [ - "ExpExponentCleanup", - "NestedArrayFunctionCallDecoder", - "ZeroFunctionSelector", - "ECRecoverMalformedInput", - "SkipEmptyStringLiteral", - "ConstantOptimizerSubtraction", - "IdentityPrecompileReturnIgnored", - "HighOrderByteCleanStorage", - "OptimizerStaleKnowledgeAboutSHA3", - "SendFailsForZeroEther", - "DynamicAllocationInfiniteLoop", - "OptimizerClearStateOnCodePathJoin", - "CleanBytesHigherOrderBits", - "ArrayAccessCleanHigherOrderBits", + "ExpExponentCleanup", + "NestedArrayFunctionCallDecoder", + "ZeroFunctionSelector", + "ECRecoverMalformedInput", + "SkipEmptyStringLiteral", + "ConstantOptimizerSubtraction", + "IdentityPrecompileReturnIgnored", + "HighOrderByteCleanStorage", + "OptimizerStaleKnowledgeAboutSHA3", + "SendFailsForZeroEther", + "DynamicAllocationInfiniteLoop", + "OptimizerClearStateOnCodePathJoin", + "CleanBytesHigherOrderBits", + "ArrayAccessCleanHigherOrderBits", "AncientCompiler" - ], + ], "released": "2016-02-17" - }, + }, "0.3.0": { "bugs": [ - "ExpExponentCleanup", - "NestedArrayFunctionCallDecoder", - "ZeroFunctionSelector", - "DelegateCallReturnValue", - "ECRecoverMalformedInput", - "SkipEmptyStringLiteral", - "ConstantOptimizerSubtraction", - "IdentityPrecompileReturnIgnored", - "HighOrderByteCleanStorage", - "OptimizerStaleKnowledgeAboutSHA3", - "SendFailsForZeroEther", - "DynamicAllocationInfiniteLoop", - "OptimizerClearStateOnCodePathJoin", - "CleanBytesHigherOrderBits", + "ExpExponentCleanup", + "NestedArrayFunctionCallDecoder", + "ZeroFunctionSelector", + "DelegateCallReturnValue", + "ECRecoverMalformedInput", + "SkipEmptyStringLiteral", + "ConstantOptimizerSubtraction", + "IdentityPrecompileReturnIgnored", + "HighOrderByteCleanStorage", + "OptimizerStaleKnowledgeAboutSHA3", + "SendFailsForZeroEther", + "DynamicAllocationInfiniteLoop", + "OptimizerClearStateOnCodePathJoin", + "CleanBytesHigherOrderBits", "ArrayAccessCleanHigherOrderBits" - ], + ], "released": "2016-03-11" - }, + }, "0.3.1": { "bugs": [ - "ExpExponentCleanup", - "NestedArrayFunctionCallDecoder", - "ZeroFunctionSelector", - "DelegateCallReturnValue", - "ECRecoverMalformedInput", - "SkipEmptyStringLiteral", - "ConstantOptimizerSubtraction", - "IdentityPrecompileReturnIgnored", - "HighOrderByteCleanStorage", - "OptimizerStaleKnowledgeAboutSHA3", - "SendFailsForZeroEther", - "DynamicAllocationInfiniteLoop", - "OptimizerClearStateOnCodePathJoin", + "ExpExponentCleanup", + "NestedArrayFunctionCallDecoder", + "ZeroFunctionSelector", + "DelegateCallReturnValue", + "ECRecoverMalformedInput", + "SkipEmptyStringLiteral", + "ConstantOptimizerSubtraction", + "IdentityPrecompileReturnIgnored", + "HighOrderByteCleanStorage", + "OptimizerStaleKnowledgeAboutSHA3", + "SendFailsForZeroEther", + "DynamicAllocationInfiniteLoop", + "OptimizerClearStateOnCodePathJoin", "CleanBytesHigherOrderBits" - ], + ], "released": "2016-03-31" - }, + }, "0.3.2": { "bugs": [ - "ExpExponentCleanup", - "NestedArrayFunctionCallDecoder", - "ZeroFunctionSelector", - "DelegateCallReturnValue", - "ECRecoverMalformedInput", - "SkipEmptyStringLiteral", - "ConstantOptimizerSubtraction", - "IdentityPrecompileReturnIgnored", - "HighOrderByteCleanStorage", - "OptimizerStaleKnowledgeAboutSHA3", - "SendFailsForZeroEther", - "DynamicAllocationInfiniteLoop", - "OptimizerClearStateOnCodePathJoin", + "ExpExponentCleanup", + "NestedArrayFunctionCallDecoder", + "ZeroFunctionSelector", + "DelegateCallReturnValue", + "ECRecoverMalformedInput", + "SkipEmptyStringLiteral", + "ConstantOptimizerSubtraction", + "IdentityPrecompileReturnIgnored", + "HighOrderByteCleanStorage", + "OptimizerStaleKnowledgeAboutSHA3", + "SendFailsForZeroEther", + "DynamicAllocationInfiniteLoop", + "OptimizerClearStateOnCodePathJoin", "CleanBytesHigherOrderBits" - ], + ], "released": "2016-04-18" - }, + }, "0.3.3": { "bugs": [ - "ExpExponentCleanup", - "NestedArrayFunctionCallDecoder", - "ZeroFunctionSelector", - "DelegateCallReturnValue", - "ECRecoverMalformedInput", - "SkipEmptyStringLiteral", - "ConstantOptimizerSubtraction", - "IdentityPrecompileReturnIgnored", - "HighOrderByteCleanStorage", - "OptimizerStaleKnowledgeAboutSHA3", - "SendFailsForZeroEther", - "DynamicAllocationInfiniteLoop", + "ExpExponentCleanup", + "NestedArrayFunctionCallDecoder", + "ZeroFunctionSelector", + "DelegateCallReturnValue", + "ECRecoverMalformedInput", + "SkipEmptyStringLiteral", + "ConstantOptimizerSubtraction", + "IdentityPrecompileReturnIgnored", + "HighOrderByteCleanStorage", + "OptimizerStaleKnowledgeAboutSHA3", + "SendFailsForZeroEther", + "DynamicAllocationInfiniteLoop", "OptimizerClearStateOnCodePathJoin" - ], + ], "released": "2016-05-27" - }, + }, "0.3.4": { "bugs": [ - "ExpExponentCleanup", - "NestedArrayFunctionCallDecoder", - "ZeroFunctionSelector", - "DelegateCallReturnValue", - "ECRecoverMalformedInput", - "SkipEmptyStringLiteral", - "ConstantOptimizerSubtraction", - "IdentityPrecompileReturnIgnored", - "HighOrderByteCleanStorage", - "OptimizerStaleKnowledgeAboutSHA3", - "SendFailsForZeroEther", - "DynamicAllocationInfiniteLoop", + "ExpExponentCleanup", + "NestedArrayFunctionCallDecoder", + "ZeroFunctionSelector", + "DelegateCallReturnValue", + "ECRecoverMalformedInput", + "SkipEmptyStringLiteral", + "ConstantOptimizerSubtraction", + "IdentityPrecompileReturnIgnored", + "HighOrderByteCleanStorage", + "OptimizerStaleKnowledgeAboutSHA3", + "SendFailsForZeroEther", + "DynamicAllocationInfiniteLoop", "OptimizerClearStateOnCodePathJoin" - ], + ], "released": "2016-05-31" - }, + }, "0.3.5": { "bugs": [ - "ExpExponentCleanup", - "NestedArrayFunctionCallDecoder", - "ZeroFunctionSelector", - "DelegateCallReturnValue", - "ECRecoverMalformedInput", - "SkipEmptyStringLiteral", - "ConstantOptimizerSubtraction", - "IdentityPrecompileReturnIgnored", - "HighOrderByteCleanStorage", - "OptimizerStaleKnowledgeAboutSHA3", - "SendFailsForZeroEther", - "DynamicAllocationInfiniteLoop", + "ExpExponentCleanup", + "NestedArrayFunctionCallDecoder", + "ZeroFunctionSelector", + "DelegateCallReturnValue", + "ECRecoverMalformedInput", + "SkipEmptyStringLiteral", + "ConstantOptimizerSubtraction", + "IdentityPrecompileReturnIgnored", + "HighOrderByteCleanStorage", + "OptimizerStaleKnowledgeAboutSHA3", + "SendFailsForZeroEther", + "DynamicAllocationInfiniteLoop", "OptimizerClearStateOnCodePathJoin" - ], + ], "released": "2016-06-10" - }, + }, "0.3.6": { "bugs": [ - "ExpExponentCleanup", - "NestedArrayFunctionCallDecoder", - "ZeroFunctionSelector", - "DelegateCallReturnValue", - "ECRecoverMalformedInput", - "SkipEmptyStringLiteral", - "ConstantOptimizerSubtraction", - "IdentityPrecompileReturnIgnored", - "HighOrderByteCleanStorage", - "OptimizerStaleKnowledgeAboutSHA3", + "ExpExponentCleanup", + "NestedArrayFunctionCallDecoder", + "ZeroFunctionSelector", + "DelegateCallReturnValue", + "ECRecoverMalformedInput", + "SkipEmptyStringLiteral", + "ConstantOptimizerSubtraction", + "IdentityPrecompileReturnIgnored", + "HighOrderByteCleanStorage", + "OptimizerStaleKnowledgeAboutSHA3", "SendFailsForZeroEther" - ], + ], "released": "2016-08-10" - }, + }, "0.4.0": { "bugs": [ - "ExpExponentCleanup", - "NestedArrayFunctionCallDecoder", - "ZeroFunctionSelector", - "DelegateCallReturnValue", - "ECRecoverMalformedInput", - "SkipEmptyStringLiteral", - "ConstantOptimizerSubtraction", - "IdentityPrecompileReturnIgnored", - "HighOrderByteCleanStorage", - "OptimizerStaleKnowledgeAboutSHA3", + "ExpExponentCleanup", + "NestedArrayFunctionCallDecoder", + "ZeroFunctionSelector", + "DelegateCallReturnValue", + "ECRecoverMalformedInput", + "SkipEmptyStringLiteral", + "ConstantOptimizerSubtraction", + "IdentityPrecompileReturnIgnored", + "HighOrderByteCleanStorage", + "OptimizerStaleKnowledgeAboutSHA3", "LibrariesNotCallableFromPayableFunctions" - ], + ], "released": "2016-09-08" - }, + }, "0.4.1": { "bugs": [ - "ExpExponentCleanup", - "NestedArrayFunctionCallDecoder", - "ZeroFunctionSelector", - "DelegateCallReturnValue", - "ECRecoverMalformedInput", - "SkipEmptyStringLiteral", - "ConstantOptimizerSubtraction", - "IdentityPrecompileReturnIgnored", - "HighOrderByteCleanStorage", - "OptimizerStaleKnowledgeAboutSHA3", + "ExpExponentCleanup", + "NestedArrayFunctionCallDecoder", + "ZeroFunctionSelector", + "DelegateCallReturnValue", + "ECRecoverMalformedInput", + "SkipEmptyStringLiteral", + "ConstantOptimizerSubtraction", + "IdentityPrecompileReturnIgnored", + "HighOrderByteCleanStorage", + "OptimizerStaleKnowledgeAboutSHA3", "LibrariesNotCallableFromPayableFunctions" - ], + ], "released": "2016-09-09" - }, + }, "0.4.10": { "bugs": [ - "ExpExponentCleanup", - "NestedArrayFunctionCallDecoder", - "ZeroFunctionSelector", - "DelegateCallReturnValue", - "ECRecoverMalformedInput", - "SkipEmptyStringLiteral", + "ExpExponentCleanup", + "NestedArrayFunctionCallDecoder", + "ZeroFunctionSelector", + "DelegateCallReturnValue", + "ECRecoverMalformedInput", + "SkipEmptyStringLiteral", "ConstantOptimizerSubtraction" - ], + ], "released": "2017-03-15" - }, + }, "0.4.11": { "bugs": [ - "ExpExponentCleanup", - "PublicLibFunctionsDoNotReturnNestedArrays", - "NestedArrayFunctionCallDecoder", - "ZeroFunctionSelector", - "DelegateCallReturnValue", - "ECRecoverMalformedInput", + "ExpExponentCleanup", + "NestedArrayFunctionCallDecoder", + "ZeroFunctionSelector", + "DelegateCallReturnValue", + "ECRecoverMalformedInput", "SkipEmptyStringLiteral" - ], + ], "released": "2017-05-03" - }, + }, "0.4.12": { "bugs": [ - "ExpExponentCleanup", - "PublicLibFunctionsDoNotReturnNestedArrays", - "NestedArrayFunctionCallDecoder", - "ZeroFunctionSelector", - "DelegateCallReturnValue", + "ExpExponentCleanup", + "NestedArrayFunctionCallDecoder", + "ZeroFunctionSelector", + "DelegateCallReturnValue", "ECRecoverMalformedInput" - ], + ], "released": "2017-07-03" - }, + }, "0.4.13": { "bugs": [ - "ExpExponentCleanup", - "PublicLibFunctionsDoNotReturnNestedArrays", - "NestedArrayFunctionCallDecoder", - "ZeroFunctionSelector", - "DelegateCallReturnValue", + "ExpExponentCleanup", + "NestedArrayFunctionCallDecoder", + "ZeroFunctionSelector", + "DelegateCallReturnValue", "ECRecoverMalformedInput" - ], + ], "released": "2017-07-06" - }, + }, "0.4.14": { "bugs": [ - "ExpExponentCleanup", - "PublicLibFunctionsDoNotReturnNestedArrays", - "NestedArrayFunctionCallDecoder", - "ZeroFunctionSelector", + "ExpExponentCleanup", + "NestedArrayFunctionCallDecoder", + "ZeroFunctionSelector", "DelegateCallReturnValue" - ], + ], "released": "2017-07-31" - }, + }, "0.4.15": { "bugs": [ - "ExpExponentCleanup", - "PublicLibFunctionsDoNotReturnNestedArrays", - "NestedArrayFunctionCallDecoder", + "ExpExponentCleanup", + "NestedArrayFunctionCallDecoder", "ZeroFunctionSelector" - ], + ], "released": "2017-08-08" - }, + }, "0.4.16": { "bugs": [ - "ExpExponentCleanup", - "PublicLibFunctionsDoNotReturnNestedArrays", - "NestedArrayFunctionCallDecoder", + "ExpExponentCleanup", + "NestedArrayFunctionCallDecoder", "ZeroFunctionSelector" - ], + ], "released": "2017-08-24" - }, + }, "0.4.17": { "bugs": [ - "ExpExponentCleanup", - "EventStructWrongData", - "PublicLibFunctionsDoNotReturnNestedArrays", - "NestedArrayFunctionCallDecoder", + "ExpExponentCleanup", + "EventStructWrongData", + "NestedArrayFunctionCallDecoder", "ZeroFunctionSelector" - ], + ], "released": "2017-09-21" - }, + }, "0.4.18": { "bugs": [ - "ExpExponentCleanup", - "EventStructWrongData", - "PublicLibFunctionsDoNotReturnNestedArrays", + "ExpExponentCleanup", + "EventStructWrongData", "NestedArrayFunctionCallDecoder" - ], + ], "released": "2017-10-18" - }, + }, "0.4.19": { "bugs": [ - "ExpExponentCleanup", - "EventStructWrongData", - "PublicLibFunctionsDoNotReturnNestedArrays", + "ExpExponentCleanup", + "EventStructWrongData", "NestedArrayFunctionCallDecoder" - ], + ], "released": "2017-11-30" - }, + }, "0.4.2": { "bugs": [ - "ExpExponentCleanup", - "NestedArrayFunctionCallDecoder", - "ZeroFunctionSelector", - "DelegateCallReturnValue", - "ECRecoverMalformedInput", - "SkipEmptyStringLiteral", - "ConstantOptimizerSubtraction", - "IdentityPrecompileReturnIgnored", - "HighOrderByteCleanStorage", + "ExpExponentCleanup", + "NestedArrayFunctionCallDecoder", + "ZeroFunctionSelector", + "DelegateCallReturnValue", + "ECRecoverMalformedInput", + "SkipEmptyStringLiteral", + "ConstantOptimizerSubtraction", + "IdentityPrecompileReturnIgnored", + "HighOrderByteCleanStorage", "OptimizerStaleKnowledgeAboutSHA3" - ], + ], "released": "2016-09-17" - }, + }, "0.4.20": { "bugs": [ - "ExpExponentCleanup", - "EventStructWrongData", - "PublicLibFunctionsDoNotReturnNestedArrays", + "ExpExponentCleanup", + "EventStructWrongData", "NestedArrayFunctionCallDecoder" - ], + ], "released": "2018-02-14" - }, + }, "0.4.21": { "bugs": [ - "ExpExponentCleanup", - "EventStructWrongData", - "PublicLibFunctionsDoNotReturnNestedArrays", + "ExpExponentCleanup", + "EventStructWrongData", "NestedArrayFunctionCallDecoder" - ], + ], "released": "2018-03-07" - }, + }, "0.4.22": { "bugs": [ - "ExpExponentCleanup", - "EventStructWrongData", + "ExpExponentCleanup", + "EventStructWrongData", "OneOfTwoConstructorsSkipped" - ], + ], "released": "2018-04-16" - }, + }, "0.4.23": { "bugs": [ - "ExpExponentCleanup", + "ExpExponentCleanup", "EventStructWrongData" - ], + ], "released": "2018-04-19" - }, + }, "0.4.24": { "bugs": [ - "ExpExponentCleanup", + "ExpExponentCleanup", "EventStructWrongData" - ], + ], "released": "2018-05-16" - }, + }, "0.4.25": { - "bugs": [], + "bugs": [], "released": "2018-09-12" - }, + }, "0.4.3": { "bugs": [ - "ExpExponentCleanup", - "NestedArrayFunctionCallDecoder", - "ZeroFunctionSelector", - "DelegateCallReturnValue", - "ECRecoverMalformedInput", - "SkipEmptyStringLiteral", - "ConstantOptimizerSubtraction", - "IdentityPrecompileReturnIgnored", + "ExpExponentCleanup", + "NestedArrayFunctionCallDecoder", + "ZeroFunctionSelector", + "DelegateCallReturnValue", + "ECRecoverMalformedInput", + "SkipEmptyStringLiteral", + "ConstantOptimizerSubtraction", + "IdentityPrecompileReturnIgnored", "HighOrderByteCleanStorage" - ], + ], "released": "2016-10-25" - }, + }, "0.4.4": { "bugs": [ - "ExpExponentCleanup", - "NestedArrayFunctionCallDecoder", - "ZeroFunctionSelector", - "DelegateCallReturnValue", - "ECRecoverMalformedInput", - "SkipEmptyStringLiteral", - "ConstantOptimizerSubtraction", + "ExpExponentCleanup", + "NestedArrayFunctionCallDecoder", + "ZeroFunctionSelector", + "DelegateCallReturnValue", + "ECRecoverMalformedInput", + "SkipEmptyStringLiteral", + "ConstantOptimizerSubtraction", "IdentityPrecompileReturnIgnored" - ], + ], "released": "2016-10-31" - }, + }, "0.4.5": { "bugs": [ - "ExpExponentCleanup", - "NestedArrayFunctionCallDecoder", - "ZeroFunctionSelector", - "DelegateCallReturnValue", - "ECRecoverMalformedInput", - "SkipEmptyStringLiteral", - "ConstantOptimizerSubtraction", - "IdentityPrecompileReturnIgnored", + "ExpExponentCleanup", + "NestedArrayFunctionCallDecoder", + "ZeroFunctionSelector", + "DelegateCallReturnValue", + "ECRecoverMalformedInput", + "SkipEmptyStringLiteral", + "ConstantOptimizerSubtraction", + "IdentityPrecompileReturnIgnored", "OptimizerStateKnowledgeNotResetForJumpdest" - ], + ], "released": "2016-11-21" - }, + }, "0.4.6": { "bugs": [ - "ExpExponentCleanup", - "NestedArrayFunctionCallDecoder", - "ZeroFunctionSelector", - "DelegateCallReturnValue", - "ECRecoverMalformedInput", - "SkipEmptyStringLiteral", - "ConstantOptimizerSubtraction", + "ExpExponentCleanup", + "NestedArrayFunctionCallDecoder", + "ZeroFunctionSelector", + "DelegateCallReturnValue", + "ECRecoverMalformedInput", + "SkipEmptyStringLiteral", + "ConstantOptimizerSubtraction", "IdentityPrecompileReturnIgnored" - ], + ], "released": "2016-11-22" - }, + }, "0.4.7": { "bugs": [ - "ExpExponentCleanup", - "NestedArrayFunctionCallDecoder", - "ZeroFunctionSelector", - "DelegateCallReturnValue", - "ECRecoverMalformedInput", - "SkipEmptyStringLiteral", + "ExpExponentCleanup", + "NestedArrayFunctionCallDecoder", + "ZeroFunctionSelector", + "DelegateCallReturnValue", + "ECRecoverMalformedInput", + "SkipEmptyStringLiteral", "ConstantOptimizerSubtraction" - ], + ], "released": "2016-12-15" - }, + }, "0.4.8": { "bugs": [ - "ExpExponentCleanup", - "NestedArrayFunctionCallDecoder", - "ZeroFunctionSelector", - "DelegateCallReturnValue", - "ECRecoverMalformedInput", - "SkipEmptyStringLiteral", + "ExpExponentCleanup", + "NestedArrayFunctionCallDecoder", + "ZeroFunctionSelector", + "DelegateCallReturnValue", + "ECRecoverMalformedInput", + "SkipEmptyStringLiteral", "ConstantOptimizerSubtraction" - ], + ], "released": "2017-01-13" - }, + }, "0.4.9": { "bugs": [ - "ExpExponentCleanup", - "NestedArrayFunctionCallDecoder", - "ZeroFunctionSelector", - "DelegateCallReturnValue", - "ECRecoverMalformedInput", - "SkipEmptyStringLiteral", + "ExpExponentCleanup", + "NestedArrayFunctionCallDecoder", + "ZeroFunctionSelector", + "DelegateCallReturnValue", + "ECRecoverMalformedInput", + "SkipEmptyStringLiteral", "ConstantOptimizerSubtraction" - ], + ], "released": "2017-01-31" + }, + "0.5.0": { + "bugs": [], + "released": "2018-11-13" } }
\ No newline at end of file diff --git a/docs/common-patterns.rst b/docs/common-patterns.rst index 739e136f..84c18936 100644 --- a/docs/common-patterns.rst +++ b/docs/common-patterns.rst @@ -13,11 +13,11 @@ Withdrawal from Contracts The recommended method of sending funds after an effect is using the withdrawal pattern. Although the most intuitive method of sending Ether, as a result of an effect, is a -direct ``send`` call, this is not recommended as it +direct ``transfer`` call, this is not recommended as it introduces a potential security risk. You may read more about this on the :ref:`security_considerations` page. -This is an example of the withdrawal pattern in practice in +The following is an example of the withdrawal pattern in practice in a contract where the goal is to send the most money to the contract in order to become the "richest", inspired by `King of the Ether <https://www.kingoftheether.com/>`_. @@ -28,7 +28,7 @@ become the new richest. :: - pragma solidity ^0.4.11; + pragma solidity >0.4.99 <0.6.0; contract WithdrawalContract { address public richest; @@ -36,7 +36,7 @@ become the new richest. mapping (address => uint) pendingWithdrawals; - function WithdrawalContract() public payable { + constructor() public payable { richest = msg.sender; mostSent = msg.value; } @@ -65,13 +65,13 @@ This is as opposed to the more intuitive sending pattern: :: - pragma solidity ^0.4.11; + pragma solidity >0.4.99 <0.6.0; contract SendContract { - address public richest; + address payable public richest; uint public mostSent; - function SendContract() public payable { + constructor() public payable { richest = msg.sender; mostSent = msg.value; } @@ -93,7 +93,7 @@ Notice that, in this example, an attacker could trap the contract into an unusable state by causing ``richest`` to be the address of a contract that has a fallback function which fails (e.g. by using ``revert()`` or by just -consuming more than the 2300 gas stipend). That way, +consuming more than the 2300 gas stipend transferred to them). That way, whenever ``transfer`` is called to deliver funds to the "poisoned" contract, it will fail and thus also ``becomeRichest`` will fail, with the contract being stuck forever. @@ -130,7 +130,7 @@ restrictions highly readable. :: - pragma solidity ^0.4.22; + pragma solidity >=0.4.22 <0.6.0; contract AccessRestriction { // These will be assigned at the construction @@ -198,7 +198,7 @@ restrictions highly readable. ); _; if (msg.value > _amount) - msg.sender.send(msg.value - _amount); + msg.sender.transfer(msg.value - _amount); } function forceOwnerChange(address _newOwner) @@ -282,7 +282,7 @@ function finishes. :: - pragma solidity ^0.4.22; + pragma solidity >=0.4.22 <0.6.0; contract StateMachine { enum Stages { diff --git a/docs/conf.py b/docs/conf.py index 7e107f2a..233ff7b6 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -24,9 +24,11 @@ import re def setup(sphinx): thisdir = os.path.dirname(os.path.realpath(__file__)) sys.path.insert(0, thisdir + '/utils') - from SolidityLexer import SolidityLexer + from pygments_lexer_solidity import SolidityLexer sphinx.add_lexer('Solidity', SolidityLexer()) + sphinx.add_stylesheet('css/custom.css') + # -- General configuration ------------------------------------------------ # If your documentation needs a minimal Sphinx version, state it here. @@ -112,7 +114,7 @@ highlight_language = 'Solidity' # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. -html_theme = 'default' +html_theme = 'sphinx_rtd_theme' # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the @@ -141,7 +143,7 @@ html_theme = 'default' # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". -html_static_path = [] +html_static_path = ['_static'] # Add any extra paths that contain custom files (such as robots.txt or # .htaccess) here, relative to this directory. These files are copied diff --git a/docs/contracts.rst b/docs/contracts.rst index b73fe2ca..c1c51e56 100644 --- a/docs/contracts.rst +++ b/docs/contracts.rst @@ -1,5 +1,7 @@ .. index:: ! contract +.. _contracts: + ########## Contracts ########## @@ -20,15 +22,20 @@ Contracts can be created "from outside" via Ethereum transactions or from within IDEs, such as `Remix <https://remix.ethereum.org/>`_, make the creation process seamless using UI elements. -Creating contracts programatically on Ethereum is best done via using the JavaScript API `web3.js <https://github.com/ethereum/web3.js>`_. -As of today it has a method called `web3.eth.Contract <https://web3js.readthedocs.io/en/1.0/web3-eth-contract.html#new-contract>`_ +Creating contracts programmatically on Ethereum is best done via using the JavaScript API `web3.js <https://github.com/ethereum/web3.js>`_. +It has a function called `web3.eth.Contract <https://web3js.readthedocs.io/en/1.0/web3-eth-contract.html#new-contract>`_ to facilitate contract creation. -When a contract is created, its constructor (a function declared with the -``constructor`` keyword) is executed once. -A constructor is optional. Only one constructor is allowed, and this means +When a contract is created, its constructor_ (a function declared with the ``constructor`` keyword) is executed once. + +A constructor is optional. Only one constructor is allowed, which means overloading is not supported. +After the constructor has executed, the final code of the contract is deployed to the +blockchain. This code includes all public and external functions and all functions +that are reachable from there through function calls. The deployed code does not +include the constructor code or internal functions only called from the constructor. + .. index:: constructor;arguments Internally, constructor arguments are passed :ref:`ABI encoded <ABI>` after the code of @@ -40,10 +47,10 @@ This means that cyclic creation dependencies are impossible. :: - pragma solidity ^0.4.22; + pragma solidity >=0.4.22 <0.6.0; contract OwnedToken { - // TokenCreator is a contract type that is defined below. + // `TokenCreator` is a contract type that is defined below. // It is fine to reference it as long as it is not used // to create a new contract. TokenCreator creator; @@ -54,14 +61,18 @@ This means that cyclic creation dependencies are impossible. // creator and the assigned name. constructor(bytes32 _name) public { // State variables are accessed via their name - // and not via e.g. this.owner. This also applies - // to functions and especially in the constructors, - // you can only call them like that ("internally"), - // because the contract itself does not exist yet. + // and not via e.g. `this.owner`. Functions can + // be accessed directly or through `this.f`, + // but the latter provides an external view + // to the function. Especially in the constructor, + // you should not access functions externally, + // because the function does not exist yet. + // See the next section for details. owner = msg.sender; + // We do an explicit type conversion from `address` // to `TokenCreator` and assume that the type of - // the calling contract is TokenCreator, there is + // the calling contract is `TokenCreator`, there is // no real way to check that. creator = TokenCreator(msg.sender); name = _name; @@ -70,7 +81,7 @@ This means that cyclic creation dependencies are impossible. function changeName(bytes32 newName) public { // Only the creator can alter the name -- // the comparison is possible since contracts - // are implicitly convertible to addresses. + // are explicitly convertible to addresses. if (msg.sender == address(creator)) name = newName; } @@ -78,11 +89,12 @@ This means that cyclic creation dependencies are impossible. function transfer(address newOwner) public { // Only the current owner can transfer the token. if (msg.sender != owner) return; - // We also want to ask the creator if the transfer - // is fine. Note that this calls a function of the - // contract defined below. If the call fails (e.g. - // due to out-of-gas), the execution here stops - // immediately. + + // We ask the creator contract if the transfer + // should proceed by using a function of the + // `TokenCreator` contract defined below. If + // the call fails (e.g. due to out-of-gas), + // the execution also fails here. if (creator.isTokenTransferOK(owner, newOwner)) owner = newOwner; } @@ -93,27 +105,28 @@ This means that cyclic creation dependencies are impossible. public returns (OwnedToken tokenAddress) { - // Create a new Token contract and return its address. - // From the JavaScript side, the return type is simply + // Create a new `Token` contract and return its address. + // From the JavaScript side, the return type is // `address`, as this is the closest type available in // the ABI. return new OwnedToken(name); } - function changeName(OwnedToken tokenAddress, bytes32 name) public { + function changeName(OwnedToken tokenAddress, bytes32 name) public { // Again, the external type of `tokenAddress` is // simply `address`. tokenAddress.changeName(name); } + // Perform checks to determine if transferring a token to the + // `OwnedToken` contract should proceed function isTokenTransferOK(address currentOwner, address newOwner) public - view + pure returns (bool ok) { - // Check some arbitrary condition. - address tokenAddress = msg.sender; - return (keccak256(newOwner) & 0xff) == (bytes20(tokenAddress) & 0xff); + // Check an arbitrary condition to see if transfer should proceed + return keccak256(abi.encodePacked(currentOwner, newOwner))[0] == 0x7f; } } @@ -131,22 +144,21 @@ a "message call") and external ones that do), there are four types of visibilities for functions and state variables. -Functions can be specified as being ``external``, -``public``, ``internal`` or ``private``, where the default is -``public``. For state variables, ``external`` is not possible -and the default is ``internal``. +Functions have to be specified as being ``external``, +``public``, ``internal`` or ``private``. +For state variables, ``external`` is not possible. ``external``: - External functions are part of the contract - interface, which means they can be called from other contracts and + External functions are part of the contract interface, + which means they can be called from other contracts and via transactions. An external function ``f`` cannot be called internally (i.e. ``f()`` does not work, but ``this.f()`` works). External functions are sometimes more efficient when they receive large arrays of data. ``public``: - Public functions are part of the contract - interface and can be either called internally or via + Public functions are part of the contract interface + and can be either called internally or via messages. For public state variables, an automatic getter function (see below) is generated. @@ -162,7 +174,7 @@ and the default is ``internal``. .. note:: Everything that is inside a contract is visible to - all external observers. Making something ``private`` + all observers external to the blockchain. Making something ``private`` only prevents other contracts from accessing and modifying the information, but it will still be visible to the whole world outside of the blockchain. @@ -173,7 +185,7 @@ return parameter list for functions. :: - pragma solidity ^0.4.16; + pragma solidity >=0.4.16 <0.6.0; contract C { function f(uint a) private pure returns (uint b) { return a + 1; } @@ -187,19 +199,18 @@ In the following example, ``D``, can call ``c.getData()`` to retrieve the value :: - // This will not compile - - pragma solidity ^0.4.0; + pragma solidity >=0.4.0 <0.6.0; contract C { uint private data; - function f(uint a) private returns(uint b) { return a + 1; } + function f(uint a) private pure returns(uint b) { return a + 1; } function setData(uint a) public { data = a; } - function getData() public returns(uint) { return data; } - function compute(uint a, uint b) internal returns (uint) { return a+b; } + function getData() public view returns(uint) { return data; } + function compute(uint a, uint b) internal pure returns (uint) { return a + b; } } + // This will not compile contract D { function readData() public { C c = new C(); @@ -227,12 +238,12 @@ The compiler automatically creates getter functions for all **public** state variables. For the contract given below, the compiler will generate a function called ``data`` that does not take any arguments and returns a ``uint``, the value of the state -variable ``data``. The initialization of state variables can -be done at declaration. +variable ``data``. State variables can be initialized +when they are declared. :: - pragma solidity ^0.4.0; + pragma solidity >=0.4.0 <0.6.0; contract C { uint public data = 42; @@ -240,33 +251,64 @@ be done at declaration. contract Caller { C c = new C(); - function f() public { - uint local = c.data(); + function f() public view returns (uint) { + return c.data(); } } The getter functions have external visibility. If the symbol is accessed internally (i.e. without ``this.``), -it is evaluated as a state variable. If it is accessed externally -(i.e. with ``this.``), it is evaluated as a function. +it evaluates to a state variable. If it is accessed externally +(i.e. with ``this.``), it evaluates to a function. :: - pragma solidity ^0.4.0; + pragma solidity >=0.4.0 <0.6.0; contract C { uint public data; - function x() public { + function x() public returns (uint) { data = 3; // internal access - uint val = this.data(); // external access + return this.data(); // external access } } -The next example is a bit more complex: +If you have a ``public`` state variable of array type, then you can only retrieve +single elements of the array via the generated getter function. This mechanism +exists to avoid high gas costs when returning an entire array. You can use +arguments to specify which individual element to return, for example +``data(0)``. If you want to return an entire array in one call, then you need +to write a function, for example: + +:: + + pragma solidity >=0.4.0 <0.6.0; + + contract arrayExample { + // public state variable + uint[] public myArray; + + // Getter function generated by the compiler + /* + function myArray(uint i) returns (uint) { + return myArray[i]; + } + */ + + // function that returns entire array + function getArray() returns (uint[] memory) { + return myArray; + } + } + +Now you can use ``getArray()`` to retrieve the entire array, instead of +``myArray(i)``, which returns a single element per call. + +The next example is more complex: :: - pragma solidity ^0.4.0; + pragma solidity >=0.4.0 <0.6.0; contract Complex { struct Data { @@ -277,16 +319,16 @@ The next example is a bit more complex: mapping (uint => mapping(bool => Data[])) public data; } -It will generate a function of the following form:: +It generates a function of the following form. The mapping in the struct is omitted +because there is no good way to provide the key for the mapping: + +:: function data(uint arg1, bool arg2, uint arg3) public returns (uint a, bytes3 b) { a = data[arg1][arg2][arg3].a; b = data[arg1][arg2][arg3].b; } -Note that the mapping in the struct is omitted because there -is no good way to provide the key for the mapping. - .. index:: ! function;modifier .. _modifiers: @@ -301,11 +343,11 @@ inheritable properties of contracts and may be overridden by derived contracts. :: - pragma solidity ^0.4.22; + pragma solidity >0.4.99 <0.6.0; contract owned { - function owned() public { owner = msg.sender; } - address owner; + constructor() public { owner = msg.sender; } + address payable owner; // This contract only defines a modifier but does not use // it: it will be used in derived contracts. @@ -346,7 +388,7 @@ inheritable properties of contracts and may be overridden by derived contracts. mapping (address => bool) registeredAddresses; uint price; - function Register(uint initialPrice) public { price = initialPrice; } + constructor(uint initialPrice) public { price = initialPrice; } // It is important to also provide the // `payable` keyword here, otherwise the function will @@ -377,7 +419,8 @@ inheritable properties of contracts and may be overridden by derived contracts. /// The `return 7` statement assigns 7 to the return value but still /// executes the statement `locked = false` in the modifier. function f() public noReentrancy returns (uint) { - require(msg.sender.call()); + (bool success,) = msg.sender.call(""); + require(success); return 7; } } @@ -406,13 +449,13 @@ Constant State Variables State variables can be declared as ``constant``. In this case, they have to be assigned from an expression which is a constant at compile time. Any expression -that accesses storage, blockchain data (e.g. ``now``, ``this.balance`` or +that accesses storage, blockchain data (e.g. ``now``, ``address(this).balance`` or ``block.number``) or -execution data (``msg.value`` or ``gasleft()``) or make calls to external contracts are disallowed. Expressions +execution data (``msg.value`` or ``gasleft()``) or makes calls to external contracts is disallowed. Expressions that might have a side-effect on memory allocation are allowed, but those that might have a side-effect on other memory objects are not. The built-in functions ``keccak256``, ``sha256``, ``ripemd160``, ``ecrecover``, ``addmod`` and ``mulmod`` -are allowed (even though they do call external contracts). +are allowed (even though, with the exception of ``keccak256``, they do call external contracts). The reason behind allowing side-effects on the memory allocator is that it should be possible to construct complex objects like e.g. lookup-tables. @@ -426,7 +469,7 @@ value types and strings. :: - pragma solidity ^0.4.0; + pragma solidity >=0.4.0 <0.6.0; contract C { uint constant x = 32**22 + 8; @@ -451,6 +494,15 @@ View Functions Functions can be declared ``view`` in which case they promise not to modify the state. +.. note:: + If the compiler's EVM target is Byzantium or newer (default) the opcode + ``STATICCALL`` is used for ``view`` functions which enforces the state + to stay unmodified as part of the EVM execution. For library ``view`` functions + ``DELEGATECALL`` is used, because there is no combined ``DELEGATECALL`` and ``STATICCALL``. + This means library ``view`` functions do not have run-time checks that prevent state + modifications. This should not impact security negatively because library code is + usually known at compile-time and the static checker performs compile-time checks. + The following statements are considered modifying the state: #. Writing to state variables. @@ -464,7 +516,7 @@ The following statements are considered modifying the state: :: - pragma solidity ^0.4.16; + pragma solidity >0.4.99 <0.6.0; contract C { function f(uint a, uint b) public view returns (uint) { @@ -473,20 +525,18 @@ The following statements are considered modifying the state: } .. note:: - ``constant`` on functions is an alias to ``view``, but this is deprecated and will be dropped in version 0.5.0. + ``constant`` on functions used to be an alias to ``view``, but this was dropped in version 0.5.0. .. note:: - Getter methods are marked ``view``. + Getter methods are automatically marked ``view``. .. note:: - If invalid explicit type conversions are used, state modifications are possible - even though a ``view`` function was called. - You can switch the compiler to use ``STATICCALL`` when calling such functions and thus - prevent modifications to the state on the level of the EVM by adding - ``pragma experimental "v0.5.0";`` - -.. warning:: - The compiler does not enforce yet that a ``view`` method is not modifying state. It raises a warning though. + Prior to version 0.5.0, the compiler did not use the ``STATICCALL`` opcode + for ``view`` functions. + This enabled state modifications in ``view`` functions through the use of + invalid explicit type conversions. + By using ``STATICCALL`` for ``view`` functions, modifications to the + state are prevented on the level of the EVM. .. index:: ! pure function, function;pure @@ -497,17 +547,21 @@ Pure Functions Functions can be declared ``pure`` in which case they promise not to read from or modify the state. +.. note:: + If the compiler's EVM target is Byzantium or newer (default) the opcode ``STATICCALL`` is used, + which does not guarantee that the state is not read, but at least that it is not modified. + In addition to the list of state modifying statements explained above, the following are considered reading from the state: #. Reading from state variables. -#. Accessing ``this.balance`` or ``<address>.balance``. +#. Accessing ``address(this).balance`` or ``<address>.balance``. #. Accessing any of the members of ``block``, ``tx``, ``msg`` (with the exception of ``msg.sig`` and ``msg.data``). #. Calling any function not marked ``pure``. #. Using inline assembly that contains certain opcodes. :: - pragma solidity ^0.4.16; + pragma solidity >0.4.99 <0.6.0; contract C { function f(uint a, uint b) public pure returns (uint) { @@ -516,11 +570,12 @@ In addition to the list of state modifying statements explained above, the follo } .. note:: - If invalid explicit type conversions are used, state modifications are possible - even though a ``pure`` function was called. - You can switch the compiler to use ``STATICCALL`` when calling such functions and thus - prevent modifications to the state on the level of the EVM by adding - ``pragma experimental "v0.5.0";`` + Prior to version 0.5.0, the compiler did not use the ``STATICCALL`` opcode + for ``pure`` functions. + This enabled state modifications in ``pure`` functions through the use of + invalid explicit type conversions. + By using ``STATICCALL`` for ``pure`` functions, modifications to the + state are prevented on the level of the EVM. .. warning:: It is not possible to prevent functions from reading the state at the level @@ -528,7 +583,11 @@ In addition to the list of state modifying statements explained above, the follo (i.e. only ``view`` can be enforced at the EVM level, ``pure`` can not). .. warning:: - Before version 0.4.17 the compiler didn't enforce that ``pure`` is not reading the state. + Before version 0.4.17 the compiler did not enforce that ``pure`` is not reading the state. + It is a compile-time type check, which can be circumvented doing invalid explicit conversions + between contract types, because the compiler can verify that the type of the contract does + not do state-changing operations, but it cannot check that the contract that will be called + at runtime is actually of that type. .. index:: ! fallback function, function;fallback @@ -538,7 +597,7 @@ Fallback Function ================= A contract can have exactly one unnamed function. This function cannot have -arguments and cannot return anything. +arguments, cannot return anything and has to have ``external`` visibility. It is executed on a call to the contract if none of the other functions match the given function identifier (or if no data was supplied at all). @@ -548,7 +607,10 @@ Ether (without data). Additionally, in order to receive Ether, the fallback func must be marked ``payable``. If no such function exists, the contract cannot receive Ether through regular transactions. -In the worst case, the fallback function can only rely on 2300 gas being available (for example when send or transfer is used), leaving not much room to perform other operations except basic logging. The following operations will consume more gas than the 2300 gas stipend: +In the worst case, the fallback function can only rely on 2300 gas being +available (for example when `send` or `transfer` is used), leaving little +room to perform other operations except basic logging. The following operations +will consume more gas than the 2300 gas stipend: - Writing to storage - Creating a contract @@ -562,11 +624,17 @@ Like any function, the fallback function can execute complex operations as long any payload supplied with the call. .. warning:: + The fallback function is also executed if the caller meant to call + a function that is not available. If you want to implement the fallback + function only to receive ether, you should add a check + like ``require(msg.data.length == 0)`` to prevent invalid calls. + +.. warning:: Contracts that receive Ether directly (without a function call, i.e. using ``send`` or ``transfer``) but do not define a fallback function throw an exception, sending back the Ether (this was different before Solidity v0.4.0). So if you want your contract to receive Ether, - you have to implement a fallback function. + you have to implement a payable fallback function. .. warning:: A contract without a payable fallback function can receive Ether as a recipient of a `coinbase transaction` (aka `miner block reward`) @@ -574,11 +642,11 @@ Like any function, the fallback function can execute complex operations as long A contract cannot react to such Ether transfers and thus also cannot reject them. This is a design choice of the EVM and Solidity cannot work around it. - It also means that ``this.balance`` can be higher than the sum of some manual accounting implemented in a contract (i.e. having a counter updated in the fallback function). + It also means that ``address(this).balance`` can be higher than the sum of some manual accounting implemented in a contract (i.e. having a counter updated in the fallback function). :: - pragma solidity ^0.4.0; + pragma solidity >0.4.99 <0.6.0; contract Test { // This function is called for all messages sent to @@ -586,7 +654,7 @@ Like any function, the fallback function can execute complex operations as long // Sending Ether to this contract will cause an exception, // because the fallback function does not have the `payable` // modifier. - function() public { x = 1; } + function() external { x = 1; } uint x; } @@ -594,19 +662,23 @@ Like any function, the fallback function can execute complex operations as long // This contract keeps all Ether sent to it with no way // to get it back. contract Sink { - function() public payable { } + function() external payable { } } contract Caller { - function callTest(Test test) public { - test.call(0xabcdef01); // hash does not exist + function callTest(Test test) public returns (bool) { + (bool success,) = address(test).call(abi.encodeWithSignature("nonExistingFunction()")); + require(success); // results in test.x becoming == 1. - // The following will not compile, but even - // if someone sends ether to that contract, - // the transaction will fail and reject the - // Ether. - //test.send(2 ether); + // address(test) will not allow to call ``send`` directly, since ``test`` has no payable + // fallback function. It has to be converted to the ``address payable`` type via an + // intermediate conversion to ``uint160`` to even allow calling ``send`` on it. + address payable testPayable = address(uint160(address(test))); + + // If someone sends ether to that contract, + // the transfer will fail, i.e. this returns false here. + return testPayable.send(2 ether); } } @@ -617,21 +689,24 @@ Like any function, the fallback function can execute complex operations as long Function Overloading ==================== -A Contract can have multiple functions of the same name but with different arguments. -This also applies to inherited functions. The following example shows overloading of the -``f`` function in the scope of contract ``A``. +A contract can have multiple functions of the same name but with different parameter +types. +This process is called "overloading" and also applies to inherited functions. +The following example shows overloading of the function +``f`` in the scope of contract ``A``. :: - pragma solidity ^0.4.16; + pragma solidity >=0.4.16 <0.6.0; contract A { function f(uint _in) public pure returns (uint out) { - out = 1; + out = _in; } - function f(uint _in, bytes32 _key) public pure returns (uint out) { - out = 2; + function f(uint _in, bool _really) public pure returns (uint out) { + if (_really) + out = _in; } } @@ -640,9 +715,9 @@ externally visible functions differ by their Solidity types but not by their ext :: - // This will not compile - pragma solidity ^0.4.16; + pragma solidity >=0.4.16 <0.6.0; + // This will not compile contract A { function f(B _in) public pure returns (B out) { out = _in; @@ -673,7 +748,7 @@ candidate, resolution fails. :: - pragma solidity ^0.4.16; + pragma solidity >=0.4.16 <0.6.0; contract A { function f(uint8 _in) public pure returns (uint8 out) { @@ -697,47 +772,64 @@ converted to ``uint8``. Events ****** -Events allow the convenient usage of the EVM logging facilities, -which in turn can be used to "call" JavaScript callbacks in the user interface -of a dapp, which listen for these events. +Solidity events give an abstraction on top of the EVM's logging functionality. +Applications can subscribe and listen to these events through the RPC interface of an Ethereum client. -Events are -inheritable members of contracts. When they are called, they cause the +Events are inheritable members of contracts. When you call them, they cause the arguments to be stored in the transaction's log - a special data structure -in the blockchain. These logs are associated with the address of -the contract and will be incorporated into the blockchain -and stay there as long as a block is accessible (forever as of -Frontier and Homestead, but this might change with Serenity). Log and -event data is not accessible from within contracts (not even from -the contract that created them). - -SPV proofs for logs are possible, so if an external entity supplies -a contract with such a proof, it can check that the log actually -exists inside the blockchain. But be aware that block headers have to be supplied because -the contract can only see the last 256 block hashes. - -Up to three parameters can -receive the attribute ``indexed`` which will cause the respective arguments -to be searched for: It is possible to filter for specific values of -indexed arguments in the user interface. - -If arrays (including ``string`` and ``bytes``) are used as indexed arguments, the -Keccak-256 hash of it is stored as topic instead. - -The hash of the signature of the event is one of the topics except if you -declared the event with ``anonymous`` specifier. This means that it is -not possible to filter for specific anonymous events by name. +in the blockchain. These logs are associated with the address of the contract, +are incorporated into the blockchain, and stay there as long as a block is +accessible (forever as of the Frontier and Homestead releases, but this might +change with Serenity). The Log and its event data is not accessible from within +contracts (not even from the contract that created them). + +It is possible to request a simple payment verification (SPV) for logs, so if +an external entity supplies a contract with such a verification, it can check +that the log actually exists inside the blockchain. You have to supply block headers +because the contract can only see the last 256 block hashes. + +You can add the attribute ``indexed`` to up to three parameters which adds them +to a special data structure known as :ref:`"topics" <abi_events>` instead of +the data part of the log. If you use arrays (including ``string`` and ``bytes``) +as indexed arguments, its Keccak-256 hash is stored as a topic instead, this is +because a topic can only hold a single word (32 bytes). + +All parameters without the ``indexed`` attribute are :ref:`ABI-encoded <ABI>` +into the data part of the log. + +Topics allow you to search for events, for example when filtering a sequence of +blocks for certain events. You can also filter events by the address of the +contract that emitted the event. + +For example, the code below uses the web3.js ``subscribe("logs")`` +`method <https://web3js.readthedocs.io/en/1.0/web3-eth-subscribe.html#subscribe-logs>`_ to filter +logs that match a topic with a certain address value: + +.. code-block:: javascript + + var options = { + fromBlock: 0, + address: web3.eth.defaultAccount, + topics: ["0x0000000000000000000000000000000000000000000000000000000000000000", null, null] + }; + web3.eth.subscribe('logs', options, function (error, result) { + if (!error) + console.log(result); + }) + .on("data", function (log) { + console.log(log); + }) + .on("changed", function (log) { + }); -All non-indexed arguments will be stored in the data part of the log. -.. note:: - Indexed arguments will not be stored themselves. You can only - search for the values, but it is impossible to retrieve the - values themselves. +The hash of the signature of the event is one of the topics, except if you +declared the event with the ``anonymous`` specifier. This means that it is +not possible to filter for specific anonymous events by name. :: - pragma solidity ^0.4.0; + pragma solidity >=0.4.21 <0.6.0; contract ClientReceipt { event Deposit( @@ -756,7 +848,7 @@ All non-indexed arguments will be stored in the data part of the log. } } -The use in the JavaScript API would be as follows: +The use in the JavaScript API is as follows: :: @@ -768,19 +860,35 @@ The use in the JavaScript API would be as follows: // watch for changes event.watch(function(error, result){ - // result will contain various information - // including the argumets given to the `Deposit` - // call. + // result contains non-indexed arguments and topics + // given to the `Deposit` call. if (!error) console.log(result); }); + // Or pass a callback to start watching immediately var event = clientReceipt.Deposit(function(error, result) { if (!error) console.log(result); }); +The output of the above looks like the following (trimmed): + +.. code-block:: json + + { + "returnValues": { + "_from": "0x1111…FFFFCCCC", + "_id": "0x50…sd5adb20", + "_value": "0x420042" + }, + "raw": { + "data": "0x7f…91385", + "topics": ["0xfd4…b4ead7", "0x7f…1a91385"] + } + } + .. index:: ! log Low-Level Interface to Logs @@ -794,16 +902,16 @@ as topics. The event call above can be performed in the same way as :: - pragma solidity ^0.4.10; + pragma solidity >=0.4.10 <0.6.0; contract C { function f() public payable { - bytes32 _id = 0x420042; + uint256 _id = 0x420042; log3( bytes32(msg.value), bytes32(0x50cb9fe53daa9737b786ab3646f04d0150dc50ef4e75f59509d83667ad5adb20), - bytes32(msg.sender), - _id + bytes32(uint256(msg.sender)), + bytes32(_id) ); } } @@ -829,23 +937,24 @@ Solidity supports multiple inheritance by copying code including polymorphism. All function calls are virtual, which means that the most derived function is called, except when the contract name is explicitly given. -When a contract inherits from multiple contracts, only a single +When a contract inherits from other contracts, only a single contract is created on the blockchain, and the code from all the base contracts is copied into the created contract. The general inheritance system is very similar to `Python's <https://docs.python.org/3/tutorial/classes.html#inheritance>`_, -especially concerning multiple inheritance. +especially concerning multiple inheritance, but there are also +some :ref:`differences <multi-inheritance>`. Details are given in the following example. :: - pragma solidity ^0.4.22; + pragma solidity >0.4.99 <0.6.0; contract owned { - constructor() { owner = msg.sender; } - address owner; + constructor() public { owner = msg.sender; } + address payable owner; } // Use `is` to derive from another contract. Derived @@ -853,7 +962,7 @@ Details are given in the following example. // internal functions and state variables. These cannot be // accessed externally via `this`, though. contract mortal is owned { - function kill() { + function kill() public { if (msg.sender == owner) selfdestruct(owner); } } @@ -875,7 +984,7 @@ Details are given in the following example. // also a base class of `mortal`, yet there is only a single // instance of `owned` (as for virtual inheritance in C++). contract named is owned, mortal { - constructor(bytes32 name) { + constructor(bytes32 name) public { Config config = Config(0xD5f9D8D94886E70b06E474c3fB14Fd43E2f23970); NameReg(config.lookup(1)).register(name); } @@ -913,11 +1022,11 @@ Note that above, we call ``mortal.kill()`` to "forward" the destruction request. The way this is done is problematic, as seen in the following example:: - pragma solidity ^0.4.22; + pragma solidity >=0.4.22 <0.6.0; contract owned { constructor() public { owner = msg.sender; } - address owner; + address payable owner; } contract mortal is owned { @@ -942,11 +1051,11 @@ derived override, but this function will bypass ``Base1.kill``, basically because it does not even know about ``Base1``. The way around this is to use ``super``:: - pragma solidity ^0.4.22; + pragma solidity >=0.4.22 <0.6.0; contract owned { constructor() public { owner = msg.sender; } - address owner; + address payable owner; } contract mortal is owned { @@ -980,16 +1089,33 @@ virtual method lookup. .. index:: ! constructor +.. _constructor: + Constructors ============ -A constructor is an optional function declared with the ``constructor`` keyword which is executed upon contract creation. -Constructor functions can be either ``public`` or ``internal``. If there is no constructor, the contract will assume the -default constructor: ``contructor() public {}``. +A constructor is an optional function declared with the ``constructor`` keyword +which is executed upon contract creation, and where you can run contract +initialisation code. + +Before the constructor code is executed, state variables are initialised to +their specified value if you initialise them inline, or zero if you do not. + +After the constructor has run, the final code of the contract is deployed +to the blockchain. The deployment of +the code costs additional gas linear to the length of the code. +This code includes all functions that are part of the public interface +and all functions that are reachable from there through function calls. +It does not include the constructor code or internal functions that are +only called from the constructor. + +Constructor functions can be either ``public`` or ``internal``. If there is no +constructor, the contract will assume the default constructor, which is +equivalent to ``constructor() public {}``. For example: :: - pragma solidity ^0.4.22; + pragma solidity >0.4.99 <0.6.0; contract A { uint public a; @@ -1005,24 +1131,9 @@ default constructor: ``contructor() public {}``. A constructor set as ``internal`` causes the contract to be marked as :ref:`abstract <abstract-contract>`. -.. note :: - Prior to version 0.4.22, constructors were defined as functions with the same name as the contract. This syntax is now deprecated. - -:: - - pragma solidity ^0.4.11; - - contract A { - uint public a; - - function A(uint _a) internal { - a = _a; - } - } - - contract B is A(1) { - function B() public {} - } +.. warning :: + Prior to version 0.4.22, constructors were defined as functions with the same name as the contract. + This syntax was deprecated and is not allowed anymore in version 0.5.0. .. index:: ! base;constructor @@ -1034,61 +1145,70 @@ The constructors of all the base contracts will be called following the linearization rules explained below. If the base constructors have arguments, derived contracts need to specify all of them. This can be done in two ways:: - pragma solidity ^0.4.22; + pragma solidity >=0.4.22 <0.6.0; contract Base { uint x; constructor(uint _x) public { x = _x; } } + // Either directly specify in the inheritance list... contract Derived1 is Base(7) { - constructor(uint _y) public {} + constructor() public {} } + // or through a "modifier" of the derived constructor. contract Derived2 is Base { constructor(uint _y) Base(_y * _y) public {} } One way is directly in the inheritance list (``is Base(7)``). The other is in -the way a modifier would be invoked as part of the header of +the way a modifier is invoked as part of the derived constructor (``Base(_y * _y)``). The first way to do it is more convenient if the constructor argument is a constant and defines the behaviour of the contract or describes it. The second way has to be used if the constructor arguments of the base depend on those of the derived contract. Arguments have to be given either in the -inheritance list or in modifier-style in the derived constuctor. +inheritance list or in modifier-style in the derived constructor. Specifying arguments in both places is an error. -If a derived contract doesn't specify the arguments to all of its base +If a derived contract does not specify the arguments to all of its base contracts' constructors, it will be abstract. .. index:: ! inheritance;multiple, ! linearization, ! C3 linearization +.. _multi-inheritance: + Multiple Inheritance and Linearization ====================================== Languages that allow multiple inheritance have to deal with several problems. One is the `Diamond Problem <https://en.wikipedia.org/wiki/Multiple_inheritance#The_diamond_problem>`_. Solidity is similar to Python in that it uses "`C3 Linearization <https://en.wikipedia.org/wiki/C3_linearization>`_" -to force a specific order in the DAG of base classes. This +to force a specific order in the directed acyclic graph (DAG) of base classes. This results in the desirable property of monotonicity but disallows some inheritance graphs. Especially, the order in which the base classes are given in the ``is`` directive is important: You have to list the direct base contracts in the order from "most base-like" to "most derived". -Note that this order is different from the one used in Python. +Note that this order is the reverse of the one used in Python. + +Another simplifying way to explain this is that when a function is called that +is defined multiple times in different contracts, the given bases +are searched from right to left (left to right in Python) in a depth-first manner, +stopping at the first match. If a base contract has already been searched, it is skipped. + In the following code, Solidity will give the error "Linearization of inheritance graph impossible". :: - // This will not compile - - pragma solidity ^0.4.0; + pragma solidity >=0.4.0 <0.6.0; contract X {} contract A is X {} + // This will not compile contract C is A, X {} The reason for this is that ``C`` requests ``X`` to override ``A`` @@ -1096,6 +1216,8 @@ The reason for this is that ``C`` requests ``X`` to override ``A`` requests to override ``X``, which is a contradiction that cannot be resolved. + + Inheriting Different Kinds of Members of the Same Name ====================================================== @@ -1113,7 +1235,7 @@ Abstract Contracts Contracts are marked as abstract when at least one of their functions lacks an implementation as in the following example (note that the function declaration header is terminated by ``;``):: - pragma solidity ^0.4.0; + pragma solidity >=0.4.0 <0.6.0; contract Feline { function utterance() public returns (bytes32); @@ -1121,7 +1243,7 @@ Contracts are marked as abstract when at least one of their functions lacks an i Such contracts cannot be compiled (even if they contain implemented functions alongside non-implemented functions), but they can be used as base contracts:: - pragma solidity ^0.4.0; + pragma solidity >=0.4.0 <0.6.0; contract Feline { function utterance() public returns (bytes32); @@ -1150,42 +1272,48 @@ Abstract contracts are useful in the same way that defining methods in an interf .. index:: ! contract;interface, ! interface contract +.. _interfaces: + ********** Interfaces ********** Interfaces are similar to abstract contracts, but they cannot have any functions implemented. There are further restrictions: -#. Cannot inherit other contracts or interfaces. -#. Cannot define constructor. -#. Cannot define variables. -#. Cannot define structs. -#. Cannot define enums. +- They cannot inherit other contracts or interfaces. +- All declared functions must be external. +- They cannot declare a constructor. +- They cannot declare state variables. Some of these restrictions might be lifted in the future. Interfaces are basically limited to what the Contract ABI can represent, and the conversion between the ABI and -an Interface should be possible without any information loss. +an interface should be possible without any information loss. Interfaces are denoted by their own keyword: :: - pragma solidity ^0.4.11; + pragma solidity >=0.4.11 <0.6.0; interface Token { - function transfer(address recipient, uint amount) public; + enum TokenType { Fungible, NonFungible } + struct Coin { string obverse; string reverse; } + function transfer(address recipient, uint amount) external; } Contracts can inherit interfaces as they would inherit other contracts. +Types defined inside interfaces and other contract-like structures +can be accessed from other contracts: ``Token.TokenType`` or ``Token.Coin``. + .. index:: ! library, callcode, delegatecall .. _libraries: -************ +********* Libraries -************ +********* Libraries are similar to contracts, but their purpose is that they are deployed only once at a specific address and their code is reused using the ``DELEGATECALL`` @@ -1199,7 +1327,14 @@ would have no way to name them, otherwise). Library functions can only be called directly (i.e. without the use of ``DELEGATECALL``) if they do not modify the state (i.e. if they are ``view`` or ``pure`` functions), because libraries are assumed to be stateless. In particular, it is -not possible to destroy a library unless Solidity's type system is circumvented. +not possible to destroy a library. + +.. note:: + Until version 0.4.20, it was possible to destroy libraries by + circumventing Solidity's type system. Starting from that version, + libraries contain a :ref:`mechanism<call-protection>` that + disallows state-modifying functions + to be called directly (i.e. without ``DELEGATECALL``). Libraries can be seen as implicit base contracts of the contracts that use them. They will not be explicitly visible in the inheritance hierarchy, but calls @@ -1208,20 +1343,20 @@ contracts (``L.f()`` if ``L`` is the name of the library). Furthermore, ``internal`` functions of libraries are visible in all contracts, just as if the library were a base contract. Of course, calls to internal functions use the internal calling convention, which means that all internal types -can be passed and memory types will be passed by reference and not copied. +can be passed and types :ref:`stored in memory <data-location>` will be passed by reference and not copied. To realize this in the EVM, code of internal library functions and all functions called from therein will at compile time be pulled into the calling contract, and a regular ``JUMP`` call will be used instead of a ``DELEGATECALL``. .. index:: using for, set -The following example illustrates how to use libraries (but +The following example illustrates how to use libraries (but manual method be sure to check out :ref:`using for <using-for>` for a more advanced example to implement a set). :: - pragma solidity ^0.4.22; + pragma solidity >=0.4.22 <0.6.0; library Set { // We define a new struct datatype that will be used to @@ -1283,31 +1418,31 @@ parameters and in any position. The calls to ``Set.contains``, ``Set.insert`` and ``Set.remove`` are all compiled as calls (``DELEGATECALL``) to an external -contract/library. If you use libraries, take care that an +contract/library. If you use libraries, be aware that an actual external function call is performed. ``msg.sender``, ``msg.value`` and ``this`` will retain their values in this call, though (prior to Homestead, because of the use of ``CALLCODE``, ``msg.sender`` and ``msg.value`` changed, though). -The following example shows how to use memory types and +The following example shows how to use :ref:`types stored in memory <data-location>` and internal functions in libraries in order to implement custom types without the overhead of external function calls: :: - pragma solidity ^0.4.16; + pragma solidity >=0.4.16 <0.6.0; library BigInt { struct bigint { uint[] limbs; } - function fromUint(uint x) internal pure returns (bigint r) { + function fromUint(uint x) internal pure returns (bigint memory r) { r.limbs = new uint[](1); r.limbs[0] = x; } - function add(bigint _a, bigint _b) internal pure returns (bigint r) { + function add(bigint memory _a, bigint memory _b) internal pure returns (bigint memory r) { r.limbs = new uint[](max(_a.limbs.length, _b.limbs.length)); uint carry = 0; for (uint i = 0; i < r.limbs.length; ++i) { @@ -1322,6 +1457,7 @@ custom types without the overhead of external function calls: if (carry > 0) { // too bad, we have to add a limb uint[] memory newLimbs = new uint[](r.limbs.length + 1); + uint i; for (i = 0; i < r.limbs.length; ++i) newLimbs[i] = r.limbs[i]; newLimbs[i] = carry; @@ -1329,7 +1465,7 @@ custom types without the overhead of external function calls: } } - function limb(bigint _a, uint _limb) internal pure returns (uint) { + function limb(bigint memory _a, uint _limb) internal pure returns (uint) { return _limb < _a.limbs.length ? _a.limbs[_limb] : 0; } @@ -1342,9 +1478,10 @@ custom types without the overhead of external function calls: using BigInt for BigInt.bigint; function f() public pure { - var x = BigInt.fromUint(7); - var y = BigInt.fromUint(uint(-1)); - var z = x.add(y); + BigInt.bigint memory x = BigInt.fromUint(7); + BigInt.bigint memory y = BigInt.fromUint(uint(-1)); + BigInt.bigint memory z = x.add(y); + assert(z.limb(1) > 0); } } @@ -1359,6 +1496,14 @@ will contain placeholders of the form ``__Set______`` (where manually by replacing all those 40 symbols by the hex encoding of the address of the library contract. +.. note:: + Manually linking libraries on the generated bytecode is discouraged, because + it is restricted to 36 characters. + You should ask the compiler to link the libraries at the time + a contract is compiled by either using + the ``--libraries`` option of ``solc`` or the ``libraries`` key if you use + the standard-JSON interface to the compiler. + Restrictions for libraries in comparison to contracts: - No state variables @@ -1367,6 +1512,8 @@ Restrictions for libraries in comparison to contracts: (These might be lifted at a later point.) +.. _call-protection: + Call Protection For Libraries ============================= @@ -1402,28 +1549,29 @@ Using For The directive ``using A for B;`` can be used to attach library functions (from the library ``A``) to any type (``B``). These functions will receive the object they are called on -as their first parameter (like the ``self`` variable in -Python). +as their first parameter (like the ``self`` variable in Python). The effect of ``using A for *;`` is that the functions from -the library ``A`` are attached to any type. +the library ``A`` are attached to *any* type. -In both situations, all functions, even those where the -type of the first parameter does not match the type of -the object, are attached. The type is checked at the +In both situations, *all* functions in the library are attached, +even those where the type of the first parameter does not +match the type of the object. The type is checked at the point the function is called and function overload resolution is performed. -The ``using A for B;`` directive is active for the current -scope, which is limited to a contract for now but will -be lifted to the global scope later, so that by including -a module, its data types including library functions are +The ``using A for B;`` directive is active only within the current +contract, including within all of its functions, and has no effect +outside of the contract in which it is used. The directive +may only be used inside a contract, not inside any of its functions. + +By including a library, its data types including library functions are available without having to add further code. Let us rewrite the set example from the :ref:`libraries` in this way:: - pragma solidity ^0.4.16; + pragma solidity >=0.4.16 <0.6.0; // This is the same code as before, just without comments library Set { @@ -1473,7 +1621,7 @@ Let us rewrite the set example from the It is also possible to extend elementary types in that way:: - pragma solidity ^0.4.16; + pragma solidity >=0.4.16 <0.6.0; library Search { function indexOf(uint[] storage self, uint value) diff --git a/docs/contributing.rst b/docs/contributing.rst index 6717a8b9..11f95206 100644 --- a/docs/contributing.rst +++ b/docs/contributing.rst @@ -16,9 +16,11 @@ In particular, we need help in the following areas: <https://gitter.im/ethereum/solidity>`_ * Fixing and responding to `Solidity's GitHub issues <https://github.com/ethereum/solidity/issues>`_, especially those tagged as - `up-for-grabs <https://github.com/ethereum/solidity/issues?q=is%3Aopen+is%3Aissue+label%3Aup-for-grabs>`_ which are + `good first issue <https://github.com/ethereum/solidity/labels/good%20first%20issue>`_ which are meant as introductory issues for external contributors. +Please note that this project is released with a `Contributor Code of Conduct <https://raw.githubusercontent.com/ethereum/solidity/develop/CODE_OF_CONDUCT.md>`_. By participating in this project - in the issues, pull requests, or Gitter channels - you agree to abide by its terms. + How to Report Issues ==================== @@ -45,15 +47,18 @@ in addition to *what* you did (unless it is a tiny change). If you need to pull in any changes from ``develop`` after making your fork (for example, to resolve potential merge conflicts), please avoid using ``git merge`` -and instead, ``git rebase`` your branch. +and instead, ``git rebase`` your branch. This will help us review your change +more easily. -Additionally, if you are writing a new feature, please ensure you write appropriate -Boost test cases and place them under ``test/``. +Additionally, if you are writing a new feature, please ensure you add appropriate +test cases under ``test/`` (see below). However, if you are making a larger change, please consult with the `Solidity Development Gitter channel -<https://gitter.im/ethereum/solidity-dev>`_ (different from the one mentioned above, this on is +<https://gitter.im/ethereum/solidity-dev>`_ (different from the one mentioned above, this one is focused on compiler and language development instead of language use) first. +New features and bugfixes should be added to the ``Changelog.md`` file: please +follow the style of previous entries, when applicable. Finally, please make sure you respect the `coding style <https://raw.githubusercontent.com/ethereum/solidity/develop/CODING_STYLE.md>`_ @@ -65,36 +70,52 @@ Thank you for your help! Running the compiler tests ========================== -Solidity includes different types of tests. They are included in the application -called ``soltest``. Some of them require the ``cpp-ethereum`` client in testing mode, +There is a script at ``scripts/tests.sh`` which executes most of the tests and +runs ``aleth`` automatically if it is in the path, but does not download it, +so it most likely will not work right away. Please read on for the details. + +Solidity includes different types of tests. Most of them are bundled in the application +called ``soltest``. Some of them require the ``aleth`` client in testing mode, some others require ``libz3`` to be installed. -``soltest`` reads test contracts that are annotated with expected results -stored in ``./test/libsolidity/syntaxTests``. In order for soltest to find these -tests the root test directory has to be specified using the ``--testpath`` command -line option, e.g. ``./build/test/soltest -- --testpath ./test``. +To run a basic set of tests that neither require ``aleth`` nor ``libz3``, run +``./scripts/soltest.sh --no-ipc --no-smt``. This script will run ``build/test/soltest`` +internally. + +.. note :: -To disable the z3 tests, use ``./build/test/soltest -- --no-smt --testpath ./test`` and -to run a subset of the tests that do not require ``cpp-ethereum``, use -``./build/test/soltest -- --no-ipc --testpath ./test``. + Those working in a Windows environment wanting to run the above basic sets without aleth or libz3 in Git Bash, you would have to do: ``./build/test/RelWithDebInfo/soltest.exe -- --no-ipc --no-smt``. + If you're running this in plain Command Prompt, use ``.\build\test\RelWithDebInfo\soltest.exe -- --no-ipc --no-smt``. -For all other tests, you need to install `cpp-ethereum <https://github.com/ethereum/cpp-ethereum/releases/download/solidityTester/eth>`_ and run it in testing mode: ``eth --test -d /tmp/testeth``. +The option ``--no-smt`` disables the tests that require ``libz3`` and +``--no-ipc`` disables those that require ``aleth``. -Then you run the actual tests: ``./build/test/soltest -- --ipcpath /tmp/testeth/geth.ipc --testpath ./test``. +If you want to run the ipc tests (those test the semantics of the generated code), +you need to install `aleth <https://github.com/ethereum/cpp-ethereum/releases/download/solidityTester/aleth_2018-06-20_artful>`_ and run it in testing mode: ``aleth --test -d /tmp/testeth`` (make sure to rename it). + +Then you run the actual tests: ``./scripts/soltest.sh --ipcpath /tmp/testeth/geth.ipc``. To run a subset of tests, filters can be used: -``soltest -t TestSuite/TestName -- --ipcpath /tmp/testeth/geth.ipc --testpath ./test``, +``./scripts/soltest.sh -t TestSuite/TestName --ipcpath /tmp/testeth/geth.ipc``, where ``TestName`` can be a wildcard ``*``. -Alternatively, there is a testing script at ``scripts/test.sh`` which executes all tests and runs -``cpp-ethereum`` automatically if it is in the path (but does not download it). +The script ``scripts/tests.sh`` also runs commandline tests and compilation tests +in addition to those found in ``soltest``. + +The CI even runs some additional tests (including ``solc-js`` and testing third party Solidity frameworks) that require compiling the Emscripten target. + +.. note :: -Travis CI even runs some additional tests (including ``solc-js`` and testing third party Solidity frameworks) that require compiling the Emscripten target. + Some versions of ``aleth`` cannot be used for testing. We suggest using the same version that is used by the Solidity continuous integration tests. + Currently the CI uses ``d661ac4fec0aeffbedcdc195f67f5ded0c798278`` of ``aleth``. Writing and running syntax tests -------------------------------- -As mentioned above, syntax tests are stored in individual contracts. These files must contain annotations, stating the expected result(s) of the respective test. +Syntax tests check that the compiler generates the correct error messages for invalid code +and properly accepts valid code. +They are stored in individual files inside ``tests/libsolidity/syntaxTests``. +These files must contain annotations, stating the expected result(s) of the respective test. The test suite will compile and check them against the given expectations. Example: ``./test/libsolidity/syntaxTests/double_stateVariable_declaration.sol`` @@ -106,15 +127,17 @@ Example: ``./test/libsolidity/syntaxTests/double_stateVariable_declaration.sol`` uint128 variable; } // ---- - // DeclarationError: Identifier already declared. + // DeclarationError: (36-52): Identifier already declared. -A syntax test must contain at least the contract under test itself, followed by the seperator ``----``. The additional comments above are used to describe the -expected compiler errors or warnings. This section can be empty in case that the contract should compile without any errors or warnings. +A syntax test must contain at least the contract under test itself, followed by the separator ``// ----``. The following comments are used to describe the +expected compiler errors or warnings. The number range denotes the location in the source where the error occurred. +In case the contract should compile without any errors or warning, the section after the separator has to be empty +and the separator can be left out completely. -In the above example, the state variable ``variable`` was declared twice, which is not allowed. This will result in a ``DeclarationError`` stating that the identifer was already declared. +In the above example, the state variable ``variable`` was declared twice, which is not allowed. This will result in a ``DeclarationError`` stating that the identifier was already declared. The tool that is being used for those tests is called ``isoltest`` and can be found under ``./test/tools/``. It is an interactive tool which allows -editing of failing contracts using your prefered text editor. Let's try to break this test by removing the second declaration of ``variable``: +editing of failing contracts using your preferred text editor. Let's try to break this test by removing the second declaration of ``variable``: :: @@ -122,7 +145,7 @@ editing of failing contracts using your prefered text editor. Let's try to break uint256 variable; } // ---- - // DeclarationError: Identifier already declared. + // DeclarationError: (36-52): Identifier already declared. Running ``./test/isoltest`` again will result in a test failure: @@ -135,16 +158,16 @@ Running ``./test/isoltest`` again will result in a test failure: } Expected result: - DeclarationError: Identifier already declared. + DeclarationError: (36-52): Identifier already declared. Obtained result: Success -which prints the expected result next to the obtained result, but also provides a way to change edit / update / skip the current contract or to even quit. -``isoltest`` offers several options for failing tests: +``isoltest`` prints the expected result next to the obtained result, but also provides a way to change edit / update / skip the current contract or to even quit. +It offers several options for failing tests: -- edit: ``isoltest`` will try to open the editor that was specified before using ``isoltest --editor /path/to/editor``. If no path was set, this will result in a runtime error. In case an editor was specified, this will open it such that the contract can be adjusted. -- update: Updates the contract under test. This will either remove the annotation which contains the exception not met or will add missing expectations. The test will then be run again. +- edit: ``isoltest`` tries to open the contract in an editor so you can adjust it. It either uses the editor given on the command line (as ``isoltest --editor /path/to/editor``), in the environment variable ``EDITOR`` or just ``/usr/bin/editor`` (in this order). +- update: Updates the contract under test. This either removes the annotation which contains the exception not met or adds missing expectations. The test will then be run again. - skip: Skips the execution of this particular test. - quit: Quits ``isoltest``. @@ -167,22 +190,23 @@ and re-run the test. It will now pass again: .. note:: - Please choose a name for the contract file, that is self-explainatory in the sense of what is been tested, e.g. ``double_variable_declaration.sol``. - Do not put more than one contract into a single file. ``isoltest`` is currently not able to recognize them individually. + Please choose a name for the contract file that explains what it tests, e.g. ``double_variable_declaration.sol``. + Do not put more than one contract into a single file, unless you are testing inheritance or cross-contract calls. + Each file should test one aspect of your new feature. Running the Fuzzer via AFL ========================== Fuzzing is a technique that runs programs on more or less random inputs to find exceptional execution -states (segmentation faults, exceptions, etc). Modern fuzzers are clever and do a directed search +states (segmentation faults, exceptions, etc). Modern fuzzers are clever and run a directed search inside the input. We have a specialized binary called ``solfuzzer`` which takes source code as input and fails whenever it encounters an internal compiler error, segmentation fault or similar, but -does not fail if e.g. the code contains an error. This way, internal problems in the compiler -can be found by fuzzing tools. +does not fail if e.g., the code contains an error. This way, fuzzing tools can find internal problems in the compiler. We mainly use `AFL <http://lcamtuf.coredump.cx/afl/>`_ for fuzzing. You need to download and -build AFL manually. Next, build Solidity (or just the ``solfuzzer`` binary) with AFL as your compiler: +install the AFL packages from your repositories (afl, afl-clang) or build them manually. +Next, build Solidity (or just the ``solfuzzer`` binary) with AFL as your compiler: :: @@ -192,7 +216,49 @@ build AFL manually. Next, build Solidity (or just the ``solfuzzer`` binary) with cmake .. -DCMAKE_C_COMPILER=path/to/afl-gcc -DCMAKE_CXX_COMPILER=path/to/afl-g++ make solfuzzer -Next, you need some example source files. This will make it much easer for the fuzzer +At this stage you should be able to see a message similar to the following: + +:: + + Scanning dependencies of target solfuzzer + [ 98%] Building CXX object test/tools/CMakeFiles/solfuzzer.dir/fuzzer.cpp.o + afl-cc 2.52b by <lcamtuf@google.com> + afl-as 2.52b by <lcamtuf@google.com> + [+] Instrumented 1949 locations (64-bit, non-hardened mode, ratio 100%). + [100%] Linking CXX executable solfuzzer + +If the instrumentation messages did not appear, try switching the cmake flags pointing to AFL's clang binaries: + +:: + + # if previously failed + make clean + cmake .. -DCMAKE_C_COMPILER=path/to/afl-clang -DCMAKE_CXX_COMPILER=path/to/afl-clang++ + make solfuzzer + +Otherwise, upon execution the fuzzer halts with an error saying binary is not instrumented: + +:: + + afl-fuzz 2.52b by <lcamtuf@google.com> + ... (truncated messages) + [*] Validating target binary... + + [-] Looks like the target binary is not instrumented! The fuzzer depends on + compile-time instrumentation to isolate interesting test cases while + mutating the input data. For more information, and for tips on how to + instrument binaries, please see /usr/share/doc/afl-doc/docs/README. + + When source code is not available, you may be able to leverage QEMU + mode support. Consult the README for tips on how to enable this. + (It is also possible to use afl-fuzz as a traditional, "dumb" fuzzer. + For that, you can use the -n option - but expect much worse results.) + + [-] PROGRAM ABORT : No instrumentation detected + Location : check_binary(), afl-fuzz.c:6920 + + +Next, you need some example source files. This makes it much easier for the fuzzer to find errors. You can either copy some files from the syntax tests or extract test files from the documentation or the other tests: @@ -207,8 +273,8 @@ from the documentation or the other tests: The AFL documentation states that the corpus (the initial input files) should not be too large. The files themselves should not be larger than 1 kB and there should be -at most one input file per functionality, so better start with a small number of -input files. There is also a tool called ``afl-cmin`` that can trim input files +at most one input file per functionality, so better start with a small number of. +There is also a tool called ``afl-cmin`` that can trim input files that result in similar behaviour of the binary. Now run the fuzzer (the ``-m`` extends the size of memory to 60 MB): @@ -217,20 +283,20 @@ Now run the fuzzer (the ``-m`` extends the size of memory to 60 MB): afl-fuzz -m 60 -i /tmp/test_cases -o /tmp/fuzzer_reports -- /path/to/solfuzzer -The fuzzer will create source files that lead to failures in ``/tmp/fuzzer_reports``. +The fuzzer creates source files that lead to failures in ``/tmp/fuzzer_reports``. Often it finds many similar source files that produce the same error. You can use the tool ``scripts/uniqueErrors.sh`` to filter out the unique errors. Whiskers ======== -*Whiskers* is a templating system similar to `Mustache <https://mustache.github.io>`_. It is used by the +*Whiskers* is a string templating system similar to `Mustache <https://mustache.github.io>`_. It is used by the compiler in various places to aid readability, and thus maintainability and verifiability, of the code. -The syntax comes with a substantial difference to Mustache: the template markers ``{{`` and ``}}`` are +The syntax comes with a substantial difference to Mustache. The template markers ``{{`` and ``}}`` are replaced by ``<`` and ``>`` in order to aid parsing and avoid conflicts with :ref:`inline-assembly` (The symbols ``<`` and ``>`` are invalid in inline assembly, while ``{`` and ``}`` are used to delimit blocks). -Another limitation is that lists are only resolved one depth and they will not recurse. This may change in the future. +Another limitation is that lists are only resolved one depth and they do not recurse. This may change in the future. A rough specification is the following: diff --git a/docs/control-structures.rst b/docs/control-structures.rst index 7849d15a..5e3b722b 100644 --- a/docs/control-structures.rst +++ b/docs/control-structures.rst @@ -2,7 +2,7 @@ Expressions and Control Structures ################################## -.. index:: ! parameter, parameter;input, parameter;output +.. index:: ! parameter, parameter;input, parameter;output, parameter;multiple Input Parameters and Output Parameters ====================================== @@ -14,20 +14,26 @@ parameters as output. Input Parameters ---------------- -The input parameters are declared the same way as variables are. As an -exception, unused parameters can omit the variable name. +The input parameters are declared the same way as variables are. +The name of unused parameters can be omitted. For example, suppose we want our contract to accept one kind of external calls with two integers, we would write something like:: - pragma solidity ^0.4.16; + pragma solidity >=0.4.16 <0.6.0; contract Simple { - function taker(uint _a, uint _b) public pure { - // do something with _a and _b. + uint sum; + function taker(uint _a, uint _b) public { + sum = _a + _b; } } +Input parameters can be used just as any other local variable +can be used, they can also be assigned to. + +.. index:: return array, return string, array, string, array of strings, dynamic array, variably sized array, return struct, struct + Output Parameters ----------------- @@ -36,10 +42,10 @@ The output parameters can be declared with the same syntax after the the sum and the product of the two given integers, then we would write:: - pragma solidity ^0.4.16; + pragma solidity >=0.4.16 <0.6.0; contract Simple { - function arithmetics(uint _a, uint _b) + function arithmetic(uint _a, uint _b) public pure returns (uint o_sum, uint o_product) @@ -50,24 +56,29 @@ write:: } The names of output parameters can be omitted. -The output values can also be specified using ``return`` statements. -The ``return`` statements are also capable of returning multiple -values, see :ref:`multi-return`. -Return parameters are initialized to zero; if they are not explicitly -set, they stay to be zero. +The return values can be specified using ``return`` statements, +which are also capable of :ref:`returning multiple values<multi-return>`. +Return parameters can be used as any other local variable and they +are zero-initialized; if they are not explicitly +set, they stay zero. -Input parameters and output parameters can be used as expressions in -the function body. There, they are also usable in the left-hand side -of assignment. + +.. note:: + You cannot return some types from non-internal functions, notably + multi-dimensional dynamic arrays and structs. If you enable the + new experimental ``ABIEncoderV2`` feature by adding ``pragma experimental + ABIEncoderV2;`` to your source file then more types are available, but + ``mapping`` types are still limited to inside a single contract and you + cannot transfer them. .. index:: if, else, while, do/while, for, break, continue, return, switch, goto Control Structures =================== -Most of the control structures from JavaScript are available in Solidity -except for ``switch`` and ``goto``. So -there is: ``if``, ``else``, ``while``, ``do``, ``for``, ``break``, ``continue``, ``return``, ``? :``, with +Most of the control structures known from curly-braces languages are available in Solidity: + +There is: ``if``, ``else``, ``while``, ``do``, ``for``, ``break``, ``continue``, ``return``, with the usual semantics known from C or JavaScript. Parentheses can *not* be omitted for conditionals, but curly brances can be omitted @@ -99,10 +110,10 @@ Internal Function Calls Functions of the current contract can be called directly ("internally"), also recursively, as seen in this nonsensical example:: - pragma solidity ^0.4.16; + pragma solidity >=0.4.16 <0.6.0; contract C { - function g(uint a) public pure returns (uint ret) { return f(); } + function g(uint a) public pure returns (uint ret) { return a + f(); } function f() internal pure returns (uint ret) { return g(7) + f(); } } @@ -111,22 +122,31 @@ the effect that the current memory is not cleared, i.e. passing memory reference to internally-called functions is very efficient. Only functions of the same contract can be called internally. +You should still avoid excessive recursion, as every internal function call +uses up at least one stack slot and there are at most 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. -Please note that function calls on ``this`` cannot be used in the constructor, as the -actual contract has not been created yet. +Please note that function calls on ``this`` cannot be used in the constructor, +as the actual contract has not been created yet. Functions of other contracts have to be called externally. For an external call, all function arguments have to be copied to memory. -When calling functions of other contracts, the amount of Wei sent with the call and -the gas can be specified with special options ``.value()`` and ``.gas()``, respectively:: +.. note:: + A function call from one contract to another does not create its own transaction, + it is a message call as part of the overall transaction. + +When calling functions of other contracts, you can specify the amount of Wei or gas sent with the call with the special options ``.value()`` and ``.gas()``, respectively. Any Wei you send to the contract is added to the total balance of the contract: - pragma solidity ^0.4.0; + +:: + + pragma solidity >=0.4.0 <0.6.0; contract InfoFeed { function info() public payable returns (uint ret) { return 42; } @@ -134,23 +154,15 @@ the gas can be specified with special options ``.value()`` and ``.gas()``, respe contract Consumer { InfoFeed feed; - function setFeed(address addr) public { feed = InfoFeed(addr); } + function setFeed(InfoFeed addr) public { feed = addr; } function callFeed() public { feed.info.value(10).gas(800)(); } } -The modifier ``payable`` has to be used for ``info``, because otherwise, the `.value()` -option would not be available. +You need to use the modifier ``payable`` with the ``info`` function because +otherwise, the ``.value()`` option would not be available. -Note that the expression ``InfoFeed(addr)`` performs an explicit type conversion stating -that "we know that the type of the contract at the given address is ``InfoFeed``" and -this does not execute a constructor. Explicit type conversions have to be -handled with extreme caution. Never call a function on a contract where you -are not sure about its type. - -We could also have used ``function setFeed(InfoFeed _feed) { feed = _feed; }`` directly. -Be careful about the fact that ``feed.info.value(10).gas(800)`` -only (locally) sets the value and amount of gas sent with the function call and only the -parentheses at the end perform the actual call. +.. 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. Function calls cause exceptions if the called contract does not exist (in the sense that the account does not contain code) or if the called contract itself @@ -158,8 +170,8 @@ throws an exception or goes out of gas. .. warning:: Any interaction with another contract imposes a potential danger, especially - if the source code of the contract is not known in advance. The current - contract hands over control to the called contract and that may potentially + if the source code of the contract is not known in advance. The + current contract hands over control to the called contract and that may potentially do just about anything. Even if the called contract inherits from a known parent contract, the inheriting contract is only required to have a correct interface. The implementation of the contract, however, can be completely arbitrary and thus, @@ -174,24 +186,26 @@ throws an exception or goes out of gas. Named Calls and Anonymous Function Parameters --------------------------------------------- -Function call arguments can also be given by name, in any order, +Function call arguments can be given by name, in any order, if they are enclosed in ``{ }`` as can be seen in the following example. The argument list has to coincide by name with the list of parameters from the function declaration, but can be in arbitrary order. :: - pragma solidity ^0.4.0; + pragma solidity >=0.4.0 <0.6.0; contract C { - function f(uint key, uint value) public { - // ... + mapping(uint => uint) data; + + function f() public { + set({value: 2, key: 3}); } - function g() public { - // named arguments - f({value: 2, key: 3}); + function set(uint key, uint value) public { + data[key] = value; } + } Omitted Function Parameter Names @@ -202,7 +216,7 @@ Those parameters will still be present on the stack, but they are inaccessible. :: - pragma solidity ^0.4.16; + pragma solidity >=0.4.16 <0.6.0; contract C { // omitted name for parameter @@ -219,17 +233,17 @@ Those parameters will still be present on the stack, but they are inaccessible. Creating Contracts via ``new`` ============================== -A contract can create a new contract using the ``new`` keyword. The full -code of the contract being created has to be known in advance, so recursive -creation-dependencies are not possible. +A contract can create other contracts using the ``new`` keyword. The full +code of the contract being created has to be known when the creating contract +is compiled so recursive creation-dependencies are not possible. :: - pragma solidity ^0.4.0; + pragma solidity >0.4.99 <0.6.0; contract D { - uint x; - function D(uint a) public payable { + uint public x; + constructor(uint a) public payable { x = a; } } @@ -239,15 +253,17 @@ creation-dependencies are not possible. function createD(uint arg) public { D newD = new D(arg); + newD.x(); } function createAndEndowD(uint arg, uint amount) public payable { // Send ether along with the creation D newD = (new D).value(amount)(arg); + newD.x(); } } -As seen in the example, it is possible to forward Ether while creating +As seen in the example, it is possible to send Ether while creating an instance of ``D`` using the ``.value()`` option, but it is not possible to limit the amount of gas. If the creation fails (due to out-of-stack, not enough balance or other problems), @@ -272,12 +288,15 @@ Assignment Destructuring Assignments and Returning Multiple Values ------------------------------------------------------- -Solidity internally allows tuple types, i.e. a list of objects of potentially different types whose size is a constant at compile-time. Those tuples can be used to return multiple values at the same time. -These can then either be assigned to newly declared variables or to pre-existing variables (or LValues in general): +Solidity internally allows tuple types, i.e. a list of objects of potentially different types whose number is a constant at compile-time. Those tuples can be used to return multiple values at the same time. +These can then either be assigned to newly declared variables or to pre-existing variables (or LValues in general). + +Tuples are not proper types in Solidity, they can only be used to form syntactic +groupings of expressions. :: - pragma solidity >0.4.23 <0.5.0; + pragma solidity >0.4.23 <0.6.0; contract C { uint[] data; @@ -287,29 +306,33 @@ These can then either be assigned to newly declared variables or to pre-existing } function g() public { - // Variables declared with type and assigned from the returned tuple. - (uint x, bool b, uint y) = f(); + // Variables declared with type and assigned from the returned tuple, + // not all elements have to be specified (but the number must match). + (uint x, , uint y) = f(); // Common trick to swap values -- does not work for non-value storage types. (x, y) = (y, x); // Components can be left out (also for variable declarations). - (data.length,,) = f(); // Sets the length to 7 - // Components can only be left out at the left-hand-side of assignments, with - // one exception: - (x,) = (1,); - // (1,) is the only way to specify a 1-component tuple, because (1) is - // equivalent to 1. + (data.length, , ) = f(); // Sets the length to 7 } } +It is not possible to mix variable declarations and non-declaration assignments, +i.e. the following is not valid: ``(x, uint y) = (1, 2);`` + .. note:: - Prior to version 0.4.24 it was possible to assign to tuples of smaller size, either + Prior to version 0.5.0 it was possible to assign to tuples of smaller size, either filling up on the left or on the right side (which ever was empty). This is - now deprecated, both sides have to have the same number of components. + now disallowed, so both sides have to have the same number of components. + +.. warning:: + Be careful when assigning to multiple variables at the same time when + reference types are involved, because it could lead to unexpected + copying behaviour. Complications for Arrays and Structs ------------------------------------ -The semantics of assignment are a bit more complicated for non-value types like arrays and structs. +The semantics of assignments are a bit more complicated for non-value types like arrays and structs. Assigning *to* a state variable always creates an independent copy. On the other hand, assigning to a local variable creates an independent copy only for elementary types, i.e. static types that fit into 32 bytes. If structs or arrays (including ``bytes`` and ``string``) are assigned from a state variable to a local variable, the local variable holds a reference to the original state variable. A second assignment to the local variable does not modify the state but only changes the reference. Assignments to members (or elements) of the local variable *do* change the state. .. index:: ! scoping, declarations, default value @@ -325,101 +348,31 @@ is ``false``. The default value for the ``uint`` or ``int`` types is ``0``. For element will be initialized to the default value corresponding to its type. Finally, for dynamically-sized arrays, ``bytes`` and ``string``, the default value is an empty array or string. -A variable declared anywhere within a function will be in scope for the *entire function*, regardless of where it is declared -(this will change soon, see below). -This happens because Solidity inherits its scoping rules from JavaScript. -This is in contrast to many languages where variables are only scoped where they are declared until the end of the semantic block. -As a result, the following code is illegal and cause the compiler to throw an error, ``Identifier already declared``: - -:: - - // This will not compile - - pragma solidity ^0.4.16; - - contract ScopingErrors { - function scoping() public { - uint i = 0; - - while (i++ < 1) { - uint same1 = 0; - } - - while (i++ < 2) { - uint same1 = 0;// Illegal, second declaration of same1 - } - } - - function minimalScoping() public { - { - uint same2 = 0; - } - - { - uint same2 = 0;// Illegal, second declaration of same2 - } - } - - function forLoopScoping() public { - for (uint same3 = 0; same3 < 1; same3++) { - } - - for (uint same3 = 0; same3 < 1; same3++) {// Illegal, second declaration of same3 - } - } - } - -In addition to this, if a variable is declared, it will be initialized at the beginning of the function to its default value. -As a result, the following code is legal, despite being poorly written: - -:: - - pragma solidity ^0.4.0; - - contract C { - function foo() public pure returns (uint) { - // baz is implicitly initialized as 0 - uint bar = 5; - if (true) { - bar += baz; - } else { - uint baz = 10;// never executes - } - return bar;// returns 5 - } - } - -Scoping starting from Version 0.5.0 ------------------------------------ - -Starting from version 0.5.0, Solidity will change to the more widespread scoping rules of C99 +Scoping in Solidity follows the widespread scoping rules of C99 (and many other languages): Variables are visible from the point right after their declaration -until the end of a ``{ }``-block. As an exception to this rule, variables declared in the +until the end of the smallest ``{ }``-block that contains the declaration. As an exception to this rule, variables declared in the initialization part of a for-loop are only visible until the end of the for-loop. Variables and other items declared outside of a code block, for example functions, contracts, -user-defined types, etc., do not change their scoping behaviour. This means you can +user-defined types, etc., are visible even before they were declared. This means you can use state variables before they are declared and call functions recursively. -These rules are already introduced now as an experimental feature. - As a consequence, the following examples will compile without warnings, since -the two variables have the same name but disjoint scopes. In non-0.5.0-mode, -they have the same scope (the function ``minimalScoping``) and thus it does -not compile there. +the two variables have the same name but disjoint scopes. :: - pragma solidity ^0.4.0; - pragma experimental "v0.5.0"; + pragma solidity >0.4.99 <0.6.0; contract C { function minimalScoping() pure public { { - uint same2 = 0; + uint same; + same = 1; } { - uint same2 = 0; + uint same; + same = 3; } } } @@ -430,8 +383,8 @@ In any case, you will get a warning about the outer variable being shadowed. :: - pragma solidity ^0.4.0; - pragma experimental "v0.5.0"; + pragma solidity >0.4.99 <0.6.0; + // This will report a warning contract C { function f() pure public returns (uint) { uint x = 1; @@ -443,7 +396,26 @@ In any case, you will get a warning about the outer variable being shadowed. } } -.. index:: ! exception, ! throw, ! assert, ! require, ! revert +.. warning:: + Before version 0.5.0 Solidity followed the same scoping rules as JavaScript, that is, a variable declared anywhere within a function would be in scope + for the entire function, regardless where it was declared. The following example shows a code snippet that used + to compile but leads to an error starting from version 0.5.0. + + :: + + pragma solidity >0.4.99 <0.6.0; + // This will not compile + contract C { + function f() pure public returns (uint) { + x = 2; + uint x; + return x; + } + } + +.. index:: ! exception, ! throw, ! assert, ! require, ! revert, ! errors + +.. _assert-and-require: Error handling: Assert, Require, Revert and Exceptions ====================================================== @@ -458,17 +430,17 @@ If used properly, analysis tools can evaluate your contract to identify the cond There are two other ways to trigger exceptions: The ``revert`` function can be used to flag an error and revert the current call. It is possible to provide a string message containing details about the error that will be passed back to the caller. -The deprecated keyword ``throw`` can also be used as an alternative to ``revert()`` (but only without error message). .. note:: - From version 0.4.13 the ``throw`` keyword is deprecated and will be phased out in the future. + There used to be a keyword called ``throw`` with the same semantics as ``revert()`` which + was deprecated in version 0.4.13 and removed in version 0.5.0. When exceptions happen in a sub-call, they "bubble up" (i.e. exceptions are rethrown) automatically. Exceptions to this rule are ``send`` -and the low-level functions ``call``, ``delegatecall`` and ``callcode`` -- those return ``false`` in case +and the low-level functions ``call``, ``delegatecall`` and ``staticcall`` -- those return ``false`` as their first return value in case of an exception instead of "bubbling up". .. warning:: - The low-level ``call``, ``delegatecall`` and ``callcode`` will return success if the called account is non-existent, as part of the design of EVM. Existence must be checked prior to calling if desired. + The low-level functions ``call``, ``delegatecall`` and ``staticcall`` return ``true`` as their first return value if the called account is non-existent, as part of the design of EVM. Existence must be checked prior to calling if desired. Catching exceptions is not yet possible. @@ -478,18 +450,18 @@ a message string for ``require``, but not for ``assert``. :: - pragma solidity ^0.4.22; + pragma solidity >0.4.99 <0.6.0; contract Sharer { - function sendHalf(address addr) public payable returns (uint balance) { + function sendHalf(address payable addr) public payable returns (uint balance) { require(msg.value % 2 == 0, "Even value required."); - uint balanceBeforeTransfer = this.balance; + uint balanceBeforeTransfer = address(this).balance; addr.transfer(msg.value / 2); // Since transfer throws an exception on failure and // cannot call back here, there should be no way for us to // still have half of the money. - assert(this.balance == balanceBeforeTransfer - msg.value / 2); - return this.balance; + assert(address(this).balance == balanceBeforeTransfer - msg.value / 2); + return address(this).balance; } } @@ -505,9 +477,8 @@ An ``assert``-style exception is generated in the following situations: A ``require``-style exception is generated in the following situations: -#. Calling ``throw``. #. Calling ``require`` with an argument that evaluates to ``false``. -#. If you call a function via a message call but it does not finish properly (i.e. it runs out of gas, has no matching function, or throws an exception itself), except when a low level operation ``call``, ``send``, ``delegatecall`` or ``callcode`` is used. The low level operations never throw exceptions but indicate failures by returning ``false``. +#. If you call a function via a message call but it does not finish properly (i.e. it runs out of gas, has no matching function, or throws an exception itself), except when a low level operation ``call``, ``send``, ``delegatecall``, ``callcode`` or ``staticcall`` is used. The low level operations never throw exceptions but indicate failures by returning ``false``. #. If you create a contract using the ``new`` keyword but the contract creation does not finish properly (see above for the definition of "not finish properly"). #. If you perform an external function call targeting a contract that contains no code. #. If your contract receives Ether via a public function without ``payable`` modifier (including the constructor and the fallback function). @@ -525,10 +496,10 @@ The following example shows how an error string can be used together with revert :: - pragma solidity ^0.4.22; + pragma solidity >0.4.99 <0.6.0; contract VendingMachine { - function buy(uint amount) payable { + function buy(uint amount) public payable { if (amount > msg.value / 2 ether) revert("Not enough Ether provided."); // Alternative way to do it: diff --git a/docs/frequently-asked-questions.rst b/docs/frequently-asked-questions.rst index ca5a1aee..8b655b0d 100644 --- a/docs/frequently-asked-questions.rst +++ b/docs/frequently-asked-questions.rst @@ -9,36 +9,11 @@ This list was originally compiled by `fivedogit <mailto:fivedogit@gmail.com>`_. Basic Questions *************** -Is it possible to do something on a specific block number? (e.g. publish a contract or execute a transaction) -============================================================================================================= - -Transactions are not guaranteed to happen on the next block or any future -specific block, since it is up to the miners to include transactions and not up -to the submitter of the transaction. This applies to function calls/transactions and contract -creation transactions. - -If you want to schedule future calls of your contract, you can use the -`alarm clock <http://www.ethereum-alarm-clock.com/>`_. - What is the transaction "payload"? ================================== This is just the bytecode "data" sent along with the request. -Is there a decompiler available? -================================ - -There is no exact decompiler to Solidity, but -`Porosity <https://github.com/comaeio/porosity>`_ is close. -Because some information like variable names, comments, and -source code formatting is lost in the compilation process, -it is not possible to completely recover the original source code. - -Bytecode can be disassembled to opcodes, a service that is provided by -several blockchain explorers. - -Contracts on the blockchain should have their original source -code published if they are to be used by third parties. Create a contract that can be killed and return funds ===================================================== @@ -63,39 +38,24 @@ has it (which includes `Remix <https://remix.ethereum.org/>`_), then ``contractname.kill.sendTransaction({from:eth.coinbase})``, just the same as my examples. -Can you return an array or a ``string`` from a solidity function call? -====================================================================== - -Yes. See `array_receiver_and_returner.sol <https://github.com/fivedogit/solidity-baby-steps/blob/master/contracts/60_array_receiver_and_returner.sol>`_. - -What is problematic, though, is returning any variably-sized data (e.g. a -variably-sized array like ``uint[]``) from a fuction **called from within Solidity**. -This is a limitation of the EVM and will be solved with the next protocol update. - -Returning variably-sized data as part of an external transaction or call is fine. - Is it possible to in-line initialize an array like so: ``string[] myarray = ["a", "b"];`` ========================================================================================= Yes. However it should be noted that this currently only works with statically sized memory arrays. You can even create an inline memory -array in the return statement. Pretty cool, huh? +array in the return statement. Example:: - pragma solidity ^0.4.16; + pragma solidity >=0.4.16 <0.6.0; contract C { - function f() public pure returns (uint8[5]) { + function f() public pure returns (uint8[5] memory) { string[4] memory adaArr = ["This", "is", "an", "array"]; - return ([1, 2, 3, 4, 5]); + adaArr[0] = "That"; + return [1, 2, 3, 4, 5]; } } -Can a contract function return a ``struct``? -============================================ - -Yes, but only in ``internal`` function calls. - If I return an ``enum``, I only get integer values in web3.js. How to get the named values? =========================================================================================== @@ -111,7 +71,7 @@ should be noted that you must declare them as static memory arrays. Examples:: - pragma solidity ^0.4.0; + pragma solidity >=0.4.0 <0.6.0; contract C { struct S { @@ -133,20 +93,6 @@ How do structs work? See `struct_and_for_loop_tester.sol <https://github.com/fivedogit/solidity-baby-steps/blob/master/contracts/65_struct_and_for_loop_tester.sol>`_. -How do for loops work? -====================== - -Very similar to JavaScript. There is one point to watch out for, though: - -If you use ``for (var i = 0; i < a.length; i ++) { a[i] = i; }``, then -the type of ``i`` will be inferred only from ``0``, whose type is ``uint8``. -This means that if ``a`` has more than ``255`` elements, your loop will -not terminate because ``i`` can only hold values up to ``255``. - -Better use ``for (uint i = 0; i < a.length...`` - -See `struct_and_for_loop_tester.sol <https://github.com/fivedogit/solidity-baby-steps/blob/master/contracts/65_struct_and_for_loop_tester.sol>`_. - What are some examples of basic string manipulation (``substring``, ``indexOf``, ``charAt``, etc)? ================================================================================================== @@ -156,7 +102,7 @@ which will be extended in the future. In addition, Arachnid has written `solidit For now, if you want to modify a string (even when you only want to know its length), you should always convert it to a ``bytes`` first:: - pragma solidity ^0.4.0; + pragma solidity >=0.4.0 <0.6.0; contract C { string s; @@ -174,7 +120,17 @@ you should always convert it to a ``bytes`` first:: Can I concatenate two strings? ============================== -You have to do it manually for now. +Yes, you can use ``abi.encodePacked``:: + + pragma solidity >=0.4.0 <0.6.0; + + library ConcatHelper { + function concat(bytes memory a, bytes memory b) + internal pure returns (bytes memory) { + return abi.encodePacked(a, b); + } + } + Why is the low-level function ``.call()`` less favorable than instantiating a contract with a variable (``ContractB b;``) and executing its functions (``b.doSomething();``)? ============================================================================================================================================================================= @@ -187,11 +143,6 @@ arguments for you. See `ping.sol <https://github.com/fivedogit/solidity-baby-steps/blob/master/contracts/45_ping.sol>`_ and `pong.sol <https://github.com/fivedogit/solidity-baby-steps/blob/master/contracts/45_pong.sol>`_. -Is unused gas automatically refunded? -===================================== - -Yes and it is immediate, i.e. done as part of the transaction. - When returning a value of say ``uint`` type, is it possible to return an ``undefined`` or "null"-like value? ============================================================================================================ @@ -203,7 +154,7 @@ situation. If you do not want to throw, you can return a pair:: - pragma solidity >0.4.23 <0.5.0; + pragma solidity >0.4.23 <0.6.0; contract C { uint[] counters; @@ -221,9 +172,10 @@ If you do not want to throw, you can return a pair:: function checkCounter(uint index) public view { (uint counter, bool error) = getCounter(index); if (error) { - // ... + // Handle the error } else { - // ... + // Do something with counter. + require(counter > 7, "Invalid counter value"); } } } @@ -242,118 +194,6 @@ It gets added to the total balance of the contract, just like when you send ethe You can only send ether along to a function that has the ``payable`` modifier, otherwise an exception is thrown. -Is it possible to get a tx receipt for a transaction executed contract-to-contract? -=================================================================================== - -No, a function call from one contract to another does not create its own transaction, -you have to look in the overall transaction. This is also the reason why several -block explorer do not show Ether sent between contracts correctly. - -What is the ``memory`` keyword? What does it do? -================================================ - -The Ethereum Virtual Machine has three areas where it can store items. - -The first is "storage", where all the contract state variables reside. -Every contract has its own storage and it is persistent between function calls -and quite expensive to use. - -The second is "memory", this is used to hold temporary values. It -is erased between (external) function calls and is cheaper to use. - -The third one is the stack, which is used to hold small local variables. -It is almost free to use, but can only hold a limited amount of values. - -For almost all types, you cannot specify where they should be stored, because -they are copied everytime they are used. - -The types where the so-called storage location is important are structs -and arrays. If you e.g. pass such variables in function calls, their -data is not copied if it can stay in memory or stay in storage. -This means that you can modify their content in the called function -and these modifications will still be visible in the caller. - -There are defaults for the storage location depending on which type -of variable it concerns: - -* state variables are always in storage -* function arguments are in memory by default -* local variables of struct, array or mapping type reference storage by default -* local variables of value type (i.e. neither array, nor struct nor mapping) are stored in the stack - -Example:: - - pragma solidity ^0.4.0; - - contract C { - uint[] data1; - uint[] data2; - - function appendOne() public { - append(data1); - } - - function appendTwo() public { - append(data2); - } - - function append(uint[] storage d) internal { - d.push(1); - } - } - -The function ``append`` can work both on ``data1`` and ``data2`` and its modifications will be -stored permanently. If you remove the ``storage`` keyword, the default -is to use ``memory`` for function arguments. This has the effect that -at the point where ``append(data1)`` or ``append(data2)`` is called, an -independent copy of the state variable is created in memory and -``append`` operates on this copy (which does not support ``.push`` - but that -is another issue). The modifications to this independent copy do not -carry back to ``data1`` or ``data2``. - -A common mistake is to declare a local variable and assume that it will -be created in memory, although it will be created in storage:: - - /// THIS CONTRACT CONTAINS AN ERROR - - pragma solidity ^0.4.0; - - contract C { - uint someVariable; - uint[] data; - - function f() public { - uint[] x; - x.push(2); - data = x; - } - } - -The type of the local variable ``x`` is ``uint[] storage``, but since -storage is not dynamically allocated, it has to be assigned from -a state variable before it can be used. So no space in storage will be -allocated for ``x``, but instead it functions only as an alias for -a pre-existing variable in storage. - -What will happen is that the compiler interprets ``x`` as a storage -pointer and will make it point to the storage slot ``0`` by default. -This has the effect that ``someVariable`` (which resides at storage -slot ``0``) is modified by ``x.push(2)``. - -The correct way to do this is the following:: - - pragma solidity ^0.4.0; - - contract C { - uint someVariable; - uint[] data; - - function f() public { - uint[] x = data; - x.push(2); - } - } - ****************** Advanced Questions ****************** @@ -375,13 +215,6 @@ The key point is that the calling contract needs to know about the function it i See `ping.sol <https://github.com/fivedogit/solidity-baby-steps/blob/master/contracts/45_ping.sol>`_ and `pong.sol <https://github.com/fivedogit/solidity-baby-steps/blob/master/contracts/45_pong.sol>`_. -Get contract to do something when it is first mined -=================================================== - -Use the constructor. Anything inside it will be executed when the contract is first mined. - -See `replicator.sol <https://github.com/fivedogit/solidity-baby-steps/blob/master/contracts/50_replicator.sol>`_. - How do you create 2-dimensional arrays? ======================================= @@ -414,7 +247,7 @@ This is a very interesting question. Suppose that we have a contract field set u User user2 = user1; } -In this case, the mapping of the struct being copied over into the userList is ignored as there is no "list of mapped keys". +In this case, the mapping of the struct being copied over into ``user2`` is ignored as there is no "list of mapped keys". Therefore it is not possible to find out which values should be copied over. How do I initialize a contract with only a specific amount of wei? @@ -426,26 +259,20 @@ In the case of a ``contract A`` calling a new instance of ``contract B``, parent You will need to make sure that you have both contracts aware of each other's presence and that ``contract B`` has a ``payable`` constructor. In this example:: - pragma solidity ^0.4.0; + pragma solidity >0.4.99 <0.6.0; contract B { - function B() public payable {} + constructor() public payable {} } contract A { - address child; + B child; function test() public { child = (new B).value(10)(); //construct a new B with 10 wei } } -Can a contract function accept a two-dimensional array? -======================================================= - -This is not yet implemented for external calls and dynamic arrays - -you can only use one level of dynamic arrays. - What is the relationship between ``bytes32`` and ``string``? Why is it that ``bytes32 somevar = "stringliteral";`` works and what does the saved 32-byte hex value mean? ======================================================================================================================================================================== @@ -474,7 +301,7 @@ Can a contract pass an array (static size) or string or ``bytes`` (dynamic size) Sure. Take care that if you cross the memory / storage boundary, independent copies will be created:: - pragma solidity ^0.4.16; + pragma solidity >=0.4.16 <0.6.0; contract C { uint[20] x; @@ -484,7 +311,7 @@ independent copies will be created:: h(x); } - function g(uint[20] y) internal pure { + function g(uint[20] memory y) internal pure { y[2] = 3; } @@ -494,10 +321,9 @@ independent copies will be created:: } The call to ``g(x)`` will not have an effect on ``x`` because it needs -to create an independent copy of the storage value in memory -(the default storage location is memory). On the other hand, -``h(x)`` successfully modifies ``x`` because only a reference -and not a copy is passed. +to create an independent copy of the storage value in memory. +On the other hand, ``h(x)`` successfully modifies ``x`` because only +a reference and not a copy is passed. Sometimes, when I try to change the length of an array with ex: ``arrayname.length = 7;`` I get a compiler error ``Value must be an lvalue``. Why? ================================================================================================================================================== @@ -512,15 +338,14 @@ contract level) with ``arrayname.length = <some new length>;``. If you get the :: - // This will not compile - - pragma solidity ^0.4.18; + pragma solidity >=0.4.18 <0.6.0; + // This will not compile contract C { int8[] dynamicStorageArray; int8[5] fixedStorageArray; - function f() { + function f() public { int8[] memory memArr; // Case 1 memArr.length++; // illegal @@ -547,29 +372,7 @@ case in C or Java). Is it possible to return an array of strings (``string[]``) from a Solidity function? ===================================================================================== -Not yet, as this requires two levels of dynamic arrays (``string`` is a dynamic array itself). - -If you issue a call for an array, it is possible to retrieve the whole array? Or must you write a helper function for that? -=========================================================================================================================== - -The automatic :ref:`getter function<getter-functions>` for a public state variable of array type only returns -individual elements. If you want to return the complete array, you have to -manually write a function to do that. - - -What could have happened if an account has storage value(s) but no code? Example: http://test.ether.camp/account/5f740b3a43fbb99724ce93a879805f4dc89178b5 -========================================================================================================================================================== - -The last thing a constructor does is returning the code of the contract. -The gas costs for this depend on the length of the code and it might be -that the supplied gas is not enough. This situation is the only one -where an "out of gas" exception does not revert changes to the state, -i.e. in this case the initialisation of the state variables. - -https://github.com/ethereum/wiki/wiki/Subtleties - -After a successful CREATE operation's sub-execution, if the operation returns x, 5 * len(x) gas is subtracted from the remaining gas before the contract is created. If the remaining gas is less than 5 * len(x), then no gas is subtracted, the code of the created contract becomes the empty string, but this is not treated as an exceptional condition - no reverts happen. - +Only when ``pragma experimental "ABIEncoderV2";`` is used. What does the following strange check do in the Custom Token contract? ====================================================================== @@ -585,6 +388,25 @@ does not fit inside this range, it is truncated. These truncations can have above is necessary to avoid certain attacks. +Why are explicit conversions between fixed-size bytes types and integer types failing? +====================================================================================== + +Since version 0.5.0 explicit conversions between fixed-size byte arrays and integers are only allowed, +if both types have the same size. This prevents unexpected behaviour when truncating or padding. +Such conversions are still possible, but intermediate casts are required that make the desired +truncation and padding convention explicit. See :ref:`types-conversion-elementary-types` for a full +explanation and examples. + + +Why can number literals not be converted to fixed-size bytes types? +=================================================================== + +Since version 0.5.0 only hexadecimal number literals can be converted to fixed-size bytes +types and only if the number of hex digits matches the size of the type. See :ref:`types-conversion-literals` +for a full explanation and examples. + + + More Questions? =============== diff --git a/docs/grammar.txt b/docs/grammar.txt index 0dda4f49..b9c8ddb9 100644 --- a/docs/grammar.txt +++ b/docs/grammar.txt @@ -50,6 +50,7 @@ TypeName = ElementaryTypeName | Mapping | ArrayTypeName | FunctionTypeName + | ( 'address' 'payable' ) UserDefinedTypeName = Identifier ( '.' Identifier )* @@ -57,8 +58,8 @@ Mapping = 'mapping' '(' ElementaryTypeName '=>' TypeName ')' ArrayTypeName = TypeName '[' Expression? ']' FunctionTypeName = 'function' FunctionTypeParameterList ( 'internal' | 'external' | StateMutability )* ( 'returns' FunctionTypeParameterList )? -StorageLocation = 'memory' | 'storage' -StateMutability = 'pure' | 'constant' | 'view' | 'payable' +StorageLocation = 'memory' | 'storage' | 'calldata' +StateMutability = 'pure' | 'view' | 'payable' Block = '{' Statement* '}' Statement = IfStatement | WhileStatement | ForStatement | Block | InlineAssemblyStatement | @@ -78,8 +79,7 @@ Break = 'break' Return = 'return' Expression? Throw = 'throw' EmitStatement = 'emit' FunctionCall -VariableDefinition = ('var' IdentifierList | VariableDeclaration | '(' VariableDeclaration? (',' VariableDeclaration? )* ')' ) ( '=' Expression )? -IdentifierList = '(' ( Identifier? ',' )* Identifier? ')' +VariableDefinition = (VariableDeclaration | '(' VariableDeclaration? (',' VariableDeclaration? )* ')' ) ( '=' Expression )? // Precedence by order (see github.com/ethereum/solidity/pull/732) Expression @@ -140,8 +140,7 @@ TupleExpression = '(' ( Expression? ( ',' Expression? )* )? ')' ElementaryTypeNameExpression = ElementaryTypeName -ElementaryTypeName = 'address' | 'bool' | 'string' | 'var' - | Int | Uint | Byte | Fixed | Ufixed +ElementaryTypeName = 'address' | 'bool' | 'string' | Int | Uint | Byte | Fixed | Ufixed Int = 'int' | 'int8' | 'int16' | 'int24' | 'int32' | 'int40' | 'int48' | 'int56' | 'int64' | 'int72' | 'int80' | 'int88' | 'int96' | 'int104' | 'int112' | 'int120' | 'int128' | 'int136' | 'int144' | 'int152' | 'int160' | 'int168' | 'int176' | 'int184' | 'int192' | 'int200' | 'int208' | 'int216' | 'int224' | 'int232' | 'int240' | 'int248' | 'int256' @@ -155,8 +154,9 @@ Ufixed = 'ufixed' | ( 'ufixed' [0-9]+ 'x' [0-9]+ ) InlineAssemblyBlock = '{' AssemblyItem* '}' -AssemblyItem = Identifier | FunctionalAssemblyExpression | InlineAssemblyBlock | AssemblyLocalBinding | AssemblyAssignment | AssemblyLabel | NumberLiteral | StringLiteral | HexLiteral -AssemblyLocalBinding = 'let' Identifier ':=' FunctionalAssemblyExpression -AssemblyAssignment = ( Identifier ':=' FunctionalAssemblyExpression ) | ( '=:' Identifier ) +AssemblyItem = Identifier | FunctionalAssemblyExpression | InlineAssemblyBlock | AssemblyVariableDeclaration | AssemblyAssignment | AssemblyLabel | NumberLiteral | StringLiteral | HexLiteral +AssemblyExpression = Identifier | FunctionalAssemblyExpression | NumberLiteral | StringLiteral | HexLiteral +AssemblyVariableDeclaration = 'let' Identifier ':=' AssemblyExpression +AssemblyAssignment = ( Identifier ':=' AssemblyExpression ) | ( '=:' Identifier ) AssemblyLabel = Identifier ':' FunctionalAssemblyExpression = Identifier '(' AssemblyItem? ( ',' AssemblyItem )* ')' diff --git a/docs/index.rst b/docs/index.rst index 80b0d6e7..17abf4b2 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -6,22 +6,25 @@ Solidity :alt: Solidity logo :align: center -Solidity is a contract-oriented, high-level language for implementing smart contracts. -It was influenced by C++, Python and JavaScript -and is designed to target the Ethereum Virtual Machine (EVM). +Solidity is an object-oriented, high-level language for implementing smart +contracts. Smart contracts are programs which govern the behaviour of accounts +within the Ethereum state. + +Solidity was influenced by C++, Python and JavaScript and is designed to target +the Ethereum Virtual Machine (EVM). Solidity is statically typed, supports inheritance, libraries and complex user-defined types among other features. -As you will see, it is possible to create contracts for voting, -crowdfunding, blind auctions, multi-signature wallets and more. +With Solidity you can create contracts for uses such as voting, crowdfunding, blind auctions, +and multi-signature wallets. .. note:: The best way to try out Solidity right now is using `Remix <https://remix.ethereum.org/>`_ (it can take a while to load, please be patient). Remix is a web browser - based IDE that allows you to write Solidity smart contracts, then deploy - and run the smart contracts. + based IDE that allows you to write Solidity smart contracts, then deploy + and run the smart contracts. .. warning:: Since software is written by humans, it can have bugs. Thus, also @@ -34,105 +37,14 @@ crowdfunding, blind auctions, multi-signature wallets and more. Translations ------------ -This documentation is translated into several languages by community volunteers, but the English version stands as a reference. +This documentation is translated into several languages by community volunteers +with varying degrees of completeness and up-to-dateness. The English version stands as a reference. * `Simplified Chinese <http://solidity-cn.readthedocs.io>`_ (in progress) * `Spanish <https://solidity-es.readthedocs.io>`_ * `Russian <https://github.com/ethereum/wiki/wiki/%5BRussian%5D-%D0%A0%D1%83%D0%BA%D0%BE%D0%B2%D0%BE%D0%B4%D1%81%D1%82%D0%B2%D0%BE-%D0%BF%D0%BE-Solidity>`_ (rather outdated) * `Korean <http://solidity-kr.readthedocs.io>`_ (in progress) - - -Useful links ------------- - -* `Ethereum <https://ethereum.org>`_ - -* `Changelog <https://github.com/ethereum/solidity/blob/develop/Changelog.md>`_ - -* `Story Backlog <https://www.pivotaltracker.com/n/projects/1189488>`_ - -* `Source Code <https://github.com/ethereum/solidity/>`_ - -* `Ethereum Stackexchange <https://ethereum.stackexchange.com/>`_ - -* `Gitter Chat <https://gitter.im/ethereum/solidity/>`_ - -Available Solidity Integrations -------------------------------- - -* `Remix <https://remix.ethereum.org/>`_ - Browser-based IDE with integrated compiler and Solidity runtime environment without server-side components. - -* `IntelliJ IDEA plugin <https://plugins.jetbrains.com/plugin/9475-intellij-solidity>`_ - Solidity plugin for IntelliJ IDEA (and all other JetBrains IDEs) - -* `Visual Studio Extension <https://visualstudiogallery.msdn.microsoft.com/96221853-33c4-4531-bdd5-d2ea5acc4799/>`_ - Solidity plugin for Microsoft Visual Studio that includes the Solidity compiler. - -* `Package for SublimeText — Solidity language syntax <https://packagecontrol.io/packages/Ethereum/>`_ - Solidity syntax highlighting for SublimeText editor. - -* `Etheratom <https://github.com/0mkara/etheratom>`_ - Plugin for the Atom editor that features syntax highlighting, compilation and a runtime environment (Backend node & VM compatible). - -* `Atom Solidity Linter <https://atom.io/packages/linter-solidity>`_ - Plugin for the Atom editor that provides Solidity linting. - -* `Atom Solium Linter <https://atom.io/packages/linter-solium>`_ - Configurable Solidty linter for Atom using Solium as a base. - -* `Solium <https://github.com/duaraghav8/Solium/>`_ - Linter to identify and fix style and security issues in Solidity. - -* `Solhint <https://github.com/protofire/solhint>`_ - Solidity linter that provides security, style guide and best practice rules for smart contract validation. - -* `Visual Studio Code extension <http://juan.blanco.ws/solidity-contracts-in-visual-studio-code/>`_ - Solidity plugin for Microsoft Visual Studio Code that includes syntax highlighting and the Solidity compiler. - -* `Emacs Solidity <https://github.com/ethereum/emacs-solidity/>`_ - Plugin for the Emacs editor providing syntax highlighting and compilation error reporting. - -* `Vim Solidity <https://github.com/tomlion/vim-solidity/>`_ - Plugin for the Vim editor providing syntax highlighting. - -* `Vim Syntastic <https://github.com/scrooloose/syntastic>`_ - Plugin for the Vim editor providing compile checking. - -Discontinued: - -* `Mix IDE <https://github.com/ethereum/mix/>`_ - Qt based IDE for designing, debugging and testing solidity smart contracts. - -* `Ethereum Studio <https://live.ether.camp/>`_ - Specialized web IDE that also provides shell access to a complete Ethereum environment. - -Solidity Tools --------------- - -* `Dapp <https://dapp.readthedocs.io>`_ - Build tool, package manager, and deployment assistant for Solidity. - -* `Solidity REPL <https://github.com/raineorshine/solidity-repl>`_ - Try Solidity instantly with a command-line Solidity console. - -* `solgraph <https://github.com/raineorshine/solgraph>`_ - Visualize Solidity control flow and highlight potential security vulnerabilities. - -* `evmdis <https://github.com/Arachnid/evmdis>`_ - EVM Disassembler that performs static analysis on the bytecode to provide a higher level of abstraction than raw EVM operations. - -* `Doxity <https://github.com/DigixGlobal/doxity>`_ - Documentation Generator for Solidity. - -Third-Party Solidity Parsers and Grammars ------------------------------------------ - -* `solidity-parser <https://github.com/ConsenSys/solidity-parser>`_ - Solidity parser for JavaScript - -* `Solidity Grammar for ANTLR 4 <https://github.com/federicobond/solidity-antlr4>`_ - Solidity grammar for the ANTLR 4 parser generator +* `French <http://solidity-fr.readthedocs.io>`_ (in progress) Language Documentation ---------------------- @@ -142,11 +54,11 @@ in Solidity followed by the basics about :ref:`blockchains <blockchain-basics>` and the :ref:`Ethereum Virtual Machine <the-ethereum-virtual-machine>`. The next section will explain several *features* of Solidity by giving -useful :ref:`example contracts <voting>` +useful :ref:`example contracts <voting>`. Remember that you can always try out the contracts `in your browser <https://remix.ethereum.org>`_! -The last and most extensive section will cover all aspects of Solidity in depth. +The fourth and most extensive section will cover all aspects of Solidity in depth. If you still have questions, you can try searching or asking on the `Ethereum Stackexchange <https://ethereum.stackexchange.com/>`_ @@ -166,12 +78,14 @@ Contents solidity-by-example.rst solidity-in-depth.rst security-considerations.rst + resources.rst using-the-compiler.rst metadata.rst abi-spec.rst - julia.rst + yul.rst style-guide.rst common-patterns.rst bugs.rst contributing.rst frequently-asked-questions.rst + lll.rst diff --git a/docs/installing-solidity.rst b/docs/installing-solidity.rst index 05ee0748..f8de0e8d 100644 --- a/docs/installing-solidity.rst +++ b/docs/installing-solidity.rst @@ -22,7 +22,7 @@ Remix `Access Remix online <https://remix.ethereum.org/>`_, you don't need to install anything. If you want to use it without connection to the Internet, go to -https://github.com/ethereum/browser-solidity/tree/gh-pages and download the .ZIP file as +https://github.com/ethereum/remix-live/tree/gh-pages and download the ``.zip`` file as explained on that page. Further options on this page detail installing commandline Solidity compiler software @@ -35,22 +35,24 @@ npm / Node.js ============= Use `npm` for a convenient and portable way to install `solcjs`, a Solidity compiler. The -`solcjs` program has fewer features than all options further down this page. Our +`solcjs` program has fewer features than the ways to access the compiler described +further down this page. The :ref:`commandline-compiler` documentation assumes you are using -the full-featured compiler, `solc`. So if you install `solcjs` from `npm` then you will -stop reading the documentation here and then continue to `solc-js <https://github.com/ethereum/solc-js>`_. +the full-featured compiler, `solc`. The usage of `solcjs` is documented inside its own +`repository <https://github.com/ethereum/solc-js>`_. Note: The solc-js project is derived from the C++ -`solc` by using Emscripten. `solc-js` can be used in JavaScript projects directly (such as Remix). +`solc` by using Emscripten which means that both use the same compiler source code. +`solc-js` can be used in JavaScript projects directly (such as Remix). Please refer to the solc-js repository for instructions. -.. code:: bash +.. code-block:: bash npm install -g solc .. note:: - The commandline is named `solcjs`. + The commandline executable is named `solcjs`. The comandline options of `solcjs` are not compatible with `solc` and tools (such as `geth`) expecting the behaviour of `solc` will not work with `solcjs`. @@ -62,9 +64,9 @@ We provide up to date docker builds for the compiler. The ``stable`` repository contains released versions while the ``nightly`` repository contains potentially unstable changes in the develop branch. -.. code:: bash +.. code-block:: bash - docker run ethereum/solc:stable solc --version + docker run ethereum/solc:stable --version Currently, the docker image only contains the compiler executable, so you have to do some additional work to link in the source and @@ -76,65 +78,66 @@ Binary Packages Binary packages of Solidity are available at `solidity/releases <https://github.com/ethereum/solidity/releases>`_. -We also have PPAs for Ubuntu. For the latest stable version. +We also have PPAs for Ubuntu, you can get the latest stable +version using the following commands: -.. code:: bash +.. code-block:: bash sudo add-apt-repository ppa:ethereum/ethereum sudo apt-get update sudo apt-get install solc -If you want to use the cutting edge developer version: +The nightly version can be installed using these commands: -.. code:: bash +.. code-block:: bash sudo add-apt-repository ppa:ethereum/ethereum sudo add-apt-repository ppa:ethereum/ethereum-dev sudo apt-get update sudo apt-get install solc - + We are also releasing a `snap package <https://snapcraft.io/>`_, which is installable in all the `supported Linux distros <https://snapcraft.io/docs/core/install>`_. To install the latest stable version of solc: -.. code:: bash +.. code-block:: bash sudo snap install solc -Or if you want to help testing the unstable solc with the most recent changes from the development branch: +If you want to help testing the latest development version of Solidity +with the most recent changes, please use the following: -.. code:: bash +.. code-block:: bash sudo snap install solc --edge Arch Linux also has packages, albeit limited to the latest development version: -.. code:: bash +.. code-block:: bash pacman -S solidity -Homebrew is missing pre-built bottles at the time of writing, -following a Jenkins to TravisCI migration, but Homebrew -should still work just fine as a means to build-from-source. -We will re-add the pre-built bottles soon. +We distribute the Solidity compiler through Homebrow +as a build-from-source version. Pre-built bottles are +currently not supported. -.. code:: bash +.. code-block:: bash brew update brew upgrade brew tap ethereum/ethereum brew install solidity -If you need a specific version of Solidity you can install a +If you need a specific version of Solidity you can install a Homebrew formula directly from Github. -View +View `solidity.rb commits on Github <https://github.com/ethereum/homebrew-ethereum/commits/master/solidity.rb>`_. -Follow the history links until you have a raw file link of a +Follow the history links until you have a raw file link of a specific commit of ``solidity.rb``. Install it using ``brew``: -.. code:: bash +.. code-block:: bash brew unlink solidity # Install 0.4.8 @@ -142,7 +145,7 @@ Install it using ``brew``: Gentoo Linux also provides a solidity package that can be installed using ``emerge``: -.. code:: bash +.. code-block:: bash emerge dev-lang/solidity @@ -151,29 +154,18 @@ Gentoo Linux also provides a solidity package that can be installed using ``emer Building from Source ==================== -Clone the Repository --------------------- - -To clone the source code, execute the following command: - -.. code:: bash - - git clone --recursive https://github.com/ethereum/solidity.git - cd solidity - -If you want to help developing Solidity, -you should fork Solidity and add your personal fork as a second remote: - -.. code:: bash - - cd solidity - git remote add personal git@github.com:[username]/solidity.git +Prerequisites - Linux +--------------------- -Solidity has git submodules. Ensure they are properly loaded: +You need to install the following dependencies for Linux builds of Solidity: -.. code:: bash ++-----------------------------------+-------------------------------------------------------+ +| Software | Notes | ++===================================+=======================================================+ +| `Git for Linux`_ | Command-line tool for retrieving source from Github. | ++-----------------------------------+-------------------------------------------------------+ - git submodule update --init --recursive +.. _Git for Linux: https://git-scm.com/download/linux Prerequisites - macOS --------------------- @@ -187,7 +179,7 @@ If you are installing Xcode for the first time, or have just installed a new version then you will need to agree to the license before you can do command-line builds: -.. code:: bash +.. code-block:: bash sudo xcodebuild -license accept @@ -201,7 +193,7 @@ if you ever want to start again from scratch. Prerequisites - Windows ----------------------- -You will need to install the following dependencies for Windows builds of Solidity: +You need to install the following dependencies for Windows builds of Solidity: +-----------------------------------+-------------------------------------------------------+ | Software | Notes | @@ -236,21 +228,36 @@ in Visual Studio 2017 Build Tools or Visual Studio 2017: .. _Visual Studio 2017: https://www.visualstudio.com/vs/ .. _Visual Studio 2017 Build Tools: https://www.visualstudio.com/downloads/#build-tools-for-visual-studio-2017 +Clone the Repository +-------------------- + +To clone the source code, execute the following command: + +.. code-block:: bash + + git clone --recursive https://github.com/ethereum/solidity.git + cd solidity + +If you want to help developing Solidity, +you should fork Solidity and add your personal fork as a second remote: + +.. code-block:: bash + + git remote add personal git@github.com:[username]/solidity.git External Dependencies --------------------- -We now have a "one button" script which installs all required external dependencies -on macOS, Windows and on numerous Linux distros. This used to be a multi-step -manual process, but is now a one-liner: +We have a helper script which installs all required external dependencies +on macOS, Windows and on numerous Linux distros. -.. code:: bash +.. code-block:: bash ./scripts/install_deps.sh Or, on Windows: -.. code:: bat +.. code-block:: bat scripts\install_deps.bat @@ -261,9 +268,11 @@ Command-Line Build **Be sure to install External Dependencies (see above) before build.** Solidity project uses CMake to configure the build. +You might want to install ccache to speed up repeated builds. +CMake will pick it up automatically. Building Solidity is quite similar on Linux, macOS and other Unices: -.. code:: bash +.. code-block:: bash mkdir build cd build @@ -271,14 +280,14 @@ Building Solidity is quite similar on Linux, macOS and other Unices: or even easier: -.. code:: bash - +.. code-block:: bash + #note: this will install binaries solc and soltest at usr/local/bin ./scripts/build.sh -And even for Windows: +And for Windows: -.. code:: bash +.. code-block:: bash mkdir build cd build @@ -291,7 +300,7 @@ should result in Visual Studio firing up. We suggest building Alternatively, you can build for Windows on the command-line, like so: -.. code:: bash +.. code-block:: bash cmake --build . --config RelWithDebInfo @@ -300,6 +309,29 @@ CMake options If you are interested what CMake options are available run ``cmake .. -LH``. +.. _smt_solvers_build: + +SMT Solvers +----------- +Solidity can be built against SMT solvers and will do so by default if +they are found in the system. Each solver can be disabled by a `cmake` option. + +*Note: In some cases, this can also be a potential workaround for build failures.* + + +Inside the build folder you can disable them, since they are enabled by default: + +.. code-block:: bash + + # disables only Z3 SMT Solver. + cmake .. -DUSE_Z3=OFF + + # disables only CVC4 SMT Solver. + cmake .. -DUSE_CVC4=OFF + + # disables both Z3 and CVC4 + cmake .. -DUSE_CVC4=OFF -DUSE_Z3=OFF + The version string in detail ============================ @@ -308,7 +340,7 @@ The Solidity version string contains four parts: - the version number - pre-release tag, usually set to ``develop.YYYY.MM.DD`` or ``nightly.YYYY.MM.DD`` - commit in the format of ``commit.GITHASH`` -- platform has arbitrary number of items, containing details about the platform and compiler +- platform, which has an arbitrary number of items, containing details about the platform and compiler If there are local modifications, the commit will be postfixed with ``.mod``. diff --git a/docs/introduction-to-smart-contracts.rst b/docs/introduction-to-smart-contracts.rst index d1789c44..9245300b 100644 --- a/docs/introduction-to-smart-contracts.rst +++ b/docs/introduction-to-smart-contracts.rst @@ -8,15 +8,16 @@ Introduction to Smart Contracts A Simple Smart Contract *********************** -Let us begin with the most basic example. It is fine if you do not understand everything -right now, we will go into more detail later. +Let us begin with a basic example that sets the value of a variable and exposes +it for other contracts to access. It is fine if you do not understand +everything right now, we will go into more detail later. Storage ======= :: - pragma solidity ^0.4.0; + pragma solidity >=0.4.0 <0.6.0; contract SimpleStorage { uint storedData; @@ -32,15 +33,15 @@ Storage The first line simply tells that the source code is written for Solidity version 0.4.0 or anything newer that does not break functionality -(up to, but not including, version 0.5.0). This is to ensure that the -contract does not suddenly behave differently with a new compiler version. The keyword ``pragma`` is called that way because, in general, -pragmas are instructions for the compiler about how to treat the +(up to, but not including, version 0.6.0). This is to ensure that the +contract is not compilable with a new (breaking) compiler version, where it could behave differently. +So-called pragmas are common instructions for compilers about how to treat the source code (e.g. `pragma once <https://en.wikipedia.org/wiki/Pragma_once>`_). A contract in the sense of Solidity is a collection of code (its *functions*) and data (its *state*) that resides at a specific address on the Ethereum blockchain. The line ``uint storedData;`` declares a state variable called ``storedData`` of -type ``uint`` (unsigned integer of 256 bits). You can think of it as a single slot +type ``uint`` (*u*nsigned *int*eger of *256* bits). You can think of it as a single slot in a database that can be queried and altered by calling functions of the code that manages the database. In the case of Ethereum, this is always the owning contract. And in this case, the functions ``set`` and ``get`` can be used to modify @@ -49,8 +50,8 @@ or retrieve the value of the variable. To access a state variable, you do not need the prefix ``this.`` as is common in other languages. -This contract does not do much yet (due to the infrastructure -built by Ethereum) apart from allowing anyone to store a single number that is accessible by +This contract does not do much yet apart from (due to the infrastructure +built by Ethereum) allowing anyone to store a single number that is accessible by anyone in the world without a (feasible) way to prevent you from publishing this number. Of course, anyone could just call ``set`` again with a different value and overwrite your number, but the number will still be stored in the history @@ -62,7 +63,7 @@ so that only you can alter the number. the ASCII character set. It is possible to store UTF-8 encoded data in string variables. .. warning:: - Be careful with using Unicode text as similarly looking (or even identical) characters can + Be careful with using Unicode text, as similar looking (or even identical) characters can have different code points and as such will be encoded as a different byte array. .. index:: ! subcurrency @@ -72,39 +73,40 @@ Subcurrency Example The following contract will implement the simplest form of a cryptocurrency. It is possible to generate coins out of thin air, but -only the person that created the contract will be able to do that (it is trivial +only the person that created the contract will be able to do that (it is easy to implement a different issuance scheme). -Furthermore, anyone can send coins to each other without any need for -registering with username and password - all you need is an Ethereum keypair. +Furthermore, anyone can send coins to each other without a need for +registering with username and password — all you need is an Ethereum keypair. :: - pragma solidity ^0.4.21; + pragma solidity >0.4.99 <0.6.0; contract Coin { // The keyword "public" makes those variables - // readable from outside. + // easily readable from outside. address public minter; mapping (address => uint) public balances; - // Events allow light clients to react on + // Events allow light clients to react to // changes efficiently. event Sent(address from, address to, uint amount); // This is the constructor whose code is // run only when the contract is created. - function Coin() public { + constructor() public { minter = msg.sender; } function mint(address receiver, uint amount) public { - if (msg.sender != minter) return; + require(msg.sender == minter); + require(amount < 1e60); balances[receiver] += amount; } function send(address receiver, uint amount) public { - if (balances[msg.sender] < amount) return; + require(amount <= balances[msg.sender], "Insufficient balance."); balances[msg.sender] -= amount; balances[receiver] += amount; emit Sent(msg.sender, receiver, amount); @@ -116,15 +118,15 @@ This contract introduces some new concepts, let us go through them one by one. The line ``address public minter;`` declares a state variable of type address that is publicly accessible. The ``address`` type is a 160-bit value that does not allow any arithmetic operations. It is suitable for -storing addresses of contracts or keypairs belonging to external +storing addresses of contracts or of keypairs belonging to external persons. The keyword ``public`` automatically generates a function that allows you to access the current value of the state variable from outside of the contract. Without this keyword, other contracts have no way to access the variable. The code of the function generated by the compiler is roughly equivalent -to the following:: +to the following (ignore ``external`` and ``view`` for now):: - function minter() returns (address) { return minter; } + function minter() external view returns (address) { return minter; } Of course, adding a function exactly like that will not work because we would have a @@ -137,17 +139,17 @@ The next line, ``mapping (address => uint) public balances;`` also creates a public state variable, but it is a more complex datatype. The type maps addresses to unsigned integers. Mappings can be seen as `hash tables <https://en.wikipedia.org/wiki/Hash_table>`_ which are -virtually initialized such that every possible key exists and is mapped to a +virtually initialized such that every possible key exists from the start and is mapped to a value whose byte-representation is all zeros. This analogy does not go too far, though, as it is neither possible to obtain a list of all keys of a mapping, nor a list of all values. So either keep in mind (or better, keep a list or use a more advanced data type) what you -added to the mapping or use it in a context where this is not needed, -like this one. The :ref:`getter function<getter-functions>` created by the ``public`` keyword +added to the mapping or use it in a context where this is not needed. +The :ref:`getter function<getter-functions>` created by the ``public`` keyword is a bit more complex in this case. It roughly looks like the following:: - function balances(address _account) public view returns (uint) { + function balances(address _account) external view returns (uint) { return balances[_account]; } @@ -162,7 +164,9 @@ a so-called "event" which is emitted in the last line of the function listen for those events being emitted on the blockchain without much cost. As soon as it is emitted, the listener will also receive the arguments ``from``, ``to`` and ``amount``, which makes it easy to track -transactions. In order to listen for this event, you would use :: +transactions. In order to listen for this event, you would use the following +JavaScript code (which assumes that ``Coin`` is a contract object created via +web3.js or a similar module):: Coin.Sent().watch({}, '', function(error, result) { if (!error) { @@ -180,23 +184,34 @@ the user interface. .. index:: coin -The special function ``Coin`` is the -constructor which is run during creation of the contract and +The constructor is a special function which is run during creation of the contract and cannot be called afterwards. It permanently stores the address of the person creating the -contract: ``msg`` (together with ``tx`` and ``block``) is a magic global variable that +contract: ``msg`` (together with ``tx`` and ``block``) is a special global variable that contains some properties which allow access to the blockchain. ``msg.sender`` is always the address where the current (external) function call came from. Finally, the functions that will actually end up with the contract and can be called by users and contracts alike are ``mint`` and ``send``. If ``mint`` is called by anyone except the account that created the contract, -nothing will happen. On the other hand, ``send`` can be used by anyone (who already -has some of these coins) to send coins to anyone else. Note that if you use -this contract to send coins to an address, you will not see anything when you -look at that address on a blockchain explorer, because the fact that you sent -coins and the changed balances are only stored in the data storage of this -particular coin contract. By the use of events it is relatively easy to create -a "blockchain explorer" that tracks transactions and balances of your new coin. +nothing will happen. This is ensured by the special function ``require`` which +causes all changes to be reverted if its argument evaluates to false. +The second call to ``require`` ensures that there will not be too many coins, +which could cause overflow errors later. + +On the other hand, ``send`` can be used by anyone (who already +has some of these coins) to send coins to anyone else. If you do not have +enough coins to send, the ``require`` call will fail and also provide the +user with an appropriate error message string. + +.. note:: + If you use + this contract to send coins to an address, you will not see anything when you + look at that address on a blockchain explorer, because the fact that you sent + coins and the changed balances are only stored in the data storage of this + particular coin contract. By the use of events it is relatively easy to create + a "blockchain explorer" that tracks transactions and balances of your new coin, + but you have to inspect the coin contract address and not the addresses of the + coin owners. .. _blockchain-basics: @@ -206,7 +221,7 @@ Blockchain Basics Blockchains as a concept are not too hard to understand for programmers. The reason is that most of the complications (mining, `hashing <https://en.wikipedia.org/wiki/Cryptographic_hash_function>`_, `elliptic-curve cryptography <https://en.wikipedia.org/wiki/Elliptic_curve_cryptography>`_, `peer-to-peer networks <https://en.wikipedia.org/wiki/Peer-to-peer>`_, etc.) -are just there to provide a certain set of features and promises. Once you accept these +are just there to provide a certain set of features and promises for the platform. Once you accept these features as given, you do not have to worry about the underlying technology - or do you have to know how Amazon's AWS works internally in order to use it? @@ -221,7 +236,7 @@ If you want to change something in the database, you have to create a so-called which has to be accepted by all others. The word transaction implies that the change you want to make (assume you want to change two values at the same time) is either not done at all or completely applied. Furthermore, -while your transaction is applied to the database, no other transaction can alter it. +while your transaction is being applied to the database, no other transaction can alter it. As an example, imagine a table that lists the balances of all accounts in an electronic currency. If a transfer from one account to another is requested, @@ -240,12 +255,13 @@ only the person holding the keys to the account can transfer money from it. Blocks ====== -One major obstacle to overcome is what, in Bitcoin terms, is called a "double-spend attack": -What happens if two transactions exist in the network that both want to empty an account, -a so-called conflict? +One major obstacle to overcome is what (in Bitcoin terms) is called a "double-spend attack": +What happens if two transactions exist in the network that both want to empty an account? +Only one of the transactions can be valid, typically the one that is accepted first. +The problem is that "first" is not an objective term in a peer-to-peer network. -The abstract answer to this is that you do not have to care. An order of the transactions -will be selected for you, the transactions will be bundled into what is called a "block" +The abstract answer to this is that you do not have to care. A globally accepted order of the transactions +will be selected for you, solving the conflict. The transactions will be bundled into what is called a "block" and then they will be executed and distributed among all participating nodes. If two transactions contradict each other, the one that ends up being second will be rejected and not become part of the block. @@ -256,10 +272,16 @@ Ethereum this is roughly every 17 seconds. As part of the "order selection mechanism" (which is called "mining") it may happen that blocks are reverted from time to time, but only at the "tip" of the chain. The more -blocks that are added on top, the less likely it is. So it might be that your transactions +blocks are added on top of a particular block, the less likely this block will be reverted. So it might be that your transactions are reverted and even removed from the blockchain, but the longer you wait, the less likely it will be. +.. note:: + Transactions are not guaranteed to be included in the next block or any specific future block, + since it is not up to the submitter of a transaction, but up to the miners to determine in which block the transaction is included. + + If you want to schedule future calls of your contract, you can use + the `alarm clock <http://www.ethereum-alarm-clock.com/>`_ or a similar oracle service. .. _the-ethereum-virtual-machine: @@ -301,7 +323,7 @@ Every account has a persistent key-value store mapping 256-bit words to 256-bit words called **storage**. Furthermore, every account has a **balance** in -Ether (in "Wei" to be exact) which can be modified by sending transactions that +Ether (in "Wei" to be exact, `1 ether` is `10**18 wei`) which can be modified by sending transactions that include Ether. .. index:: ! transaction @@ -310,19 +332,20 @@ Transactions ============ A transaction is a message that is sent from one account to another -account (which might be the same or the special zero-account, see below). -It can include binary data (its payload) and Ether. +account (which might be the same or empty, see below). +It can include binary data (which is called "payload") and Ether. If the target account contains code, that code is executed and the payload is provided as input data. -If the target account is the zero-account (the account with the -address ``0``), the transaction creates a **new contract**. +If the target account is not set (the transaction does not have +a recipient or the recipient is set to ``null``), the transaction +creates a **new contract**. As already mentioned, the address of that contract is not the zero address but an address derived from the sender and its number of transactions sent (the "nonce"). The payload of such a contract creation transaction is taken to be -EVM bytecode and executed. The output of this execution is +EVM bytecode and executed. The output data of this execution is permanently stored as the code of the contract. This means that in order to create a contract, you do not send the actual code of the contract, but in fact code that @@ -341,14 +364,14 @@ Gas Upon creation, each transaction is charged with a certain amount of **gas**, whose purpose is to limit the amount of work that is needed to execute -the transaction and to pay for this execution. While the EVM executes the +the transaction and to pay for this execution at the same time. While the EVM executes the transaction, the gas is gradually depleted according to specific rules. The **gas price** is a value set by the creator of the transaction, who has to pay ``gas_price * gas`` up front from the sending account. -If some gas is left after the execution, it is refunded in the same way. +If some gas is left after the execution, it is refunded to the creator in the same way. -If the gas is used up at any point (i.e. it is negative), +If the gas is used up at any point (i.e. it would be negative), an out-of-gas exception is triggered, which reverts all modifications made to the state in the current call frame. @@ -357,23 +380,27 @@ made to the state in the current call frame. Storage, Memory and the Stack ============================= -Each account has a persistent memory area which is called **storage**. +The Ethereum Virtual Machine has three areas where it can store data- +storage, memory and the stack, which are explained in the following +paragraphs. + +Each account has a data area called **storage**, which is persistent between function calls +and transactions. Storage is a key-value store that maps 256-bit words to 256-bit words. -It is not possible to enumerate storage from within a contract -and it is comparatively costly to read and even more so, to modify -storage. A contract can neither read nor write to any storage apart -from its own. +It is not possible to enumerate storage from within a contract and it is +comparatively costly to read, and even more to modify storage. +A contract can neither read nor write to any storage apart from its own. -The second memory area is called **memory**, of which a contract obtains +The second data area is called **memory**, of which a contract obtains a freshly cleared instance for each message call. Memory is linear and can be addressed at byte level, but reads are limited to a width of 256 bits, while writes can be either 8 bits or 256 bits wide. Memory is expanded by a word (256-bit), when -accessing (either reading or writing) a previously untouched memory word (ie. any offset +accessing (either reading or writing) a previously untouched memory word (i.e. any offset within a word). At the time of expansion, the cost in gas must be paid. Memory is more costly the larger it grows (it scales quadratically). The EVM is not a register machine but a stack machine, so all -computations are performed on an area called the **stack**. It has a maximum size of +computations are performed on an data area called the **stack**. It has a maximum size of 1024 elements and contains words of 256 bits. Access to the stack is limited to the top end in the following way: It is possible to copy one of @@ -381,7 +408,8 @@ the topmost 16 elements to the top of the stack or swap the topmost element with one of the 16 elements below it. All other operations take the topmost two (or one, or more, depending on the operation) elements from the stack and push the result onto the stack. -Of course it is possible to move stack elements to storage or memory, +Of course it is possible to move stack elements to storage or memory +in order to get deeper access to the stack, but it is not possible to just access arbitrary elements deeper in the stack without first removing the top of the stack. @@ -391,13 +419,17 @@ Instruction Set =============== The instruction set of the EVM is kept minimal in order to avoid -incorrect implementations which could cause consensus problems. -All instructions operate on the basic data type, 256-bit words. +incorrect or inconsistent implementations which could cause consensus problems. +All instructions operate on the basic data type, 256-bit words or on slices of memory +(or other byte arrays). The usual arithmetic, bit, logical and comparison operations are present. Conditional and unconditional jumps are possible. Furthermore, contracts can access relevant properties of the current block like its number and timestamp. +For a complete list, please see the :ref:`list of opcodes <opcodes>` as part of the inline +assembly documentation. + .. index:: ! message call, function;call Message Calls @@ -412,7 +444,7 @@ a top-level message call which in turn can create further message calls. A contract can decide how much of its remaining **gas** should be sent with the inner message call and how much it wants to retain. If an out-of-gas exception happens in the inner call (or any -other exception), this will be signalled by an error value put onto the stack. +other exception), this will be signaled by an error value put onto the stack. In this case, only the gas sent together with the call is used up. In Solidity, the calling contract causes a manual exception by default in such situations, so that exceptions "bubble up" the call stack. @@ -422,9 +454,12 @@ will receive a freshly cleared instance of memory and has access to the call payload - which will be provided in a separate area called the **calldata**. After it has finished execution, it can return data which will be stored at a location in the caller's memory preallocated by the caller. +All such calls are fully synchronous. Calls are **limited** to a depth of 1024, which means that for more complex -operations, loops should be preferred over recursive calls. +operations, loops should be preferred over recursive calls. Furthermore, +only 63/64th of the gas can be forwarded in a message call, which causes a +depth limit of a little less than 1000 in practice. .. index:: delegatecall, callcode, library @@ -442,7 +477,7 @@ refer to the calling contract, only the code is taken from the called address. This makes it possible to implement the "library" feature in Solidity: Reusable library code that can be applied to a contract's storage, e.g. in -order to implement a complex data structure. +order to implement a complex data structure. .. index:: log @@ -451,13 +486,13 @@ Logs It is possible to store data in a specially indexed data structure that maps all the way up to the block level. This feature called **logs** -is used by Solidity in order to implement **events**. +is used by Solidity in order to implement :ref:`events <events>`. Contracts cannot access log data after it has been created, but they can be efficiently accessed from outside the blockchain. Since some part of the log data is stored in `bloom filters <https://en.wikipedia.org/wiki/Bloom_filter>`_, it is possible to search for this data in an efficient and cryptographically secure way, so network peers that do not download the whole blockchain -("light clients") can still find these logs. +(so-called "light clients") can still find these logs. .. index:: contract creation @@ -465,26 +500,22 @@ Create ====== Contracts can even create other contracts using a special opcode (i.e. -they do not simply call the zero address). The only difference between +they do not simply call the zero address as a transaction would). The only difference between these **create calls** and normal message calls is that the payload data is executed and the result stored as code and the caller / creator receives the address of the new contract on the stack. -.. index:: selfdestruct +.. index:: selfdestruct, self-destruct, deactivate -Self-destruct -============= +Deactivate and Self-destruct +============================ -The only possibility that code is removed from the blockchain is -when a contract at that address performs the ``selfdestruct`` operation. -The remaining Ether stored at that address is sent to a designated -target and then the storage and code is removed from the state. +The only way to remove code from the blockchain is when a contract at that address performs the ``selfdestruct`` operation. The remaining Ether stored at that address is sent to a designated target and then the storage and code is removed from the state. Removing the contract in theory sounds like a good idea, but it is potentially dangerous, as if someone sends Ether to removed contracts, the Ether is forever lost. -.. warning:: Even if a contract's code does not contain a call to ``selfdestruct``, - it can still perform that operation using ``delegatecall`` or ``callcode``. +.. note:: + Even if a contract's code does not contain a call to ``selfdestruct``, it can still perform that operation using ``delegatecall`` or ``callcode``. -.. note:: The pruning of old contracts may or may not be implemented by Ethereum - clients. Additionally, archive nodes could choose to keep the contract storage - and code indefinitely. +If you want to deactivate your contracts, you should instead **disable** them by changing some internal state which causes all functions to revert. This makes it impossible to use the contract, as it returns Ether immediately. -.. note:: Currently **external accounts** cannot be removed from the state. +.. warning:: + Even if a contract is removed by "selfdestruct", it is still part of the history of the blockchain and probably retained by most Ethereum nodes. So using "selfdestruct" is not the same as deleting data from a hard disk. diff --git a/docs/julia.rst b/docs/julia.rst deleted file mode 100644 index c9b73db2..00000000 --- a/docs/julia.rst +++ /dev/null @@ -1,592 +0,0 @@ -################################################# -Joyfully Universal Language for (Inline) Assembly -################################################# - -.. _julia: - -.. index:: ! assembly, ! asm, ! evmasm, ! julia - -JULIA is an intermediate language that can compile to various different backends -(EVM 1.0, EVM 1.5 and eWASM are planned). -Because of that, it is designed to be a usable common denominator of all three -platforms. -It can already be used for "inline assembly" inside Solidity and -future versions of the Solidity compiler will even use JULIA as intermediate -language. It should also be easy to build high-level optimizer stages for JULIA. - -.. note:: - - Note that the flavour used for "inline assembly" does not have types - (everything is ``u256``) and the built-in functions are identical - to the EVM opcodes. Please resort to the inline assembly documentation - for details. - -The core components of JULIA are functions, blocks, variables, literals, -for-loops, if-statements, switch-statements, expressions and assignments to variables. - -JULIA is typed, both variables and literals must specify the type with postfix -notation. The supported types are ``bool``, ``u8``, ``s8``, ``u32``, ``s32``, -``u64``, ``s64``, ``u128``, ``s128``, ``u256`` and ``s256``. - -JULIA in itself does not even provide operators. If the EVM is targeted, -opcodes will be available as built-in functions, but they can be reimplemented -if the backend changes. For a list of mandatory built-in functions, see the section below. - -The following example program assumes that the EVM opcodes ``mul``, ``div`` -and ``mod`` are available either natively or as functions and computes exponentiation. - -.. code:: - - { - function power(base:u256, exponent:u256) -> result:u256 - { - switch exponent - case 0:u256 { result := 1:u256 } - case 1:u256 { result := base } - default: - { - result := power(mul(base, base), div(exponent, 2:u256)) - switch mod(exponent, 2:u256) - case 1:u256 { result := mul(base, result) } - } - } - } - -It is also possible to implement the same function using a for-loop -instead of with recursion. Here, we need the EVM opcodes ``lt`` (less-than) -and ``add`` to be available. - -.. code:: - - { - function power(base:u256, exponent:u256) -> result:u256 - { - result := 1:u256 - for { let i := 0:u256 } lt(i, exponent) { i := add(i, 1:u256) } - { - result := mul(result, base) - } - } - } - -Specification of JULIA -====================== - -JULIA code is described in this chapter. JULIA code is usually placed into a JULIA object, which is described in the following chapter. - -Grammar:: - - Block = '{' Statement* '}' - Statement = - Block | - FunctionDefinition | - VariableDeclaration | - Assignment | - Expression | - Switch | - ForLoop | - BreakContinue - FunctionDefinition = - 'function' Identifier '(' TypedIdentifierList? ')' - ( '->' TypedIdentifierList )? Block - VariableDeclaration = - 'let' TypedIdentifierList ( ':=' Expression )? - Assignment = - IdentifierList ':=' Expression - Expression = - FunctionCall | Identifier | Literal - If = - 'if' Expression Block - Switch = - 'switch' Expression Case* ( 'default' Block )? - Case = - 'case' Literal Block - ForLoop = - 'for' Block Expression Block Block - BreakContinue = - 'break' | 'continue' - FunctionCall = - Identifier '(' ( Expression ( ',' Expression )* )? ')' - Identifier = [a-zA-Z_$] [a-zA-Z_0-9]* - IdentifierList = Identifier ( ',' Identifier)* - TypeName = Identifier | BuiltinTypeName - BuiltinTypeName = 'bool' | [us] ( '8' | '32' | '64' | '128' | '256' ) - TypedIdentifierList = Identifier ':' TypeName ( ',' Identifier ':' TypeName )* - Literal = - (NumberLiteral | StringLiteral | HexLiteral | TrueLiteral | FalseLiteral) ':' TypeName - NumberLiteral = HexNumber | DecimalNumber - HexLiteral = 'hex' ('"' ([0-9a-fA-F]{2})* '"' | '\'' ([0-9a-fA-F]{2})* '\'') - StringLiteral = '"' ([^"\r\n\\] | '\\' .)* '"' - TrueLiteral = 'true' - FalseLiteral = 'false' - HexNumber = '0x' [0-9a-fA-F]+ - DecimalNumber = [0-9]+ - -Restrictions on the Grammar ---------------------------- - -Switches must have at least one case (including the default case). -If all possible values of the expression is covered, the default case should -not be allowed (i.e. a switch with a ``bool`` expression and having both a -true and false case should not allow a default case). - -Every expression evaluates to zero or more values. Identifiers and Literals -evaluate to exactly -one value and function calls evaluate to a number of values equal to the -number of return values of the function called. - -In variable declarations and assignments, the right-hand-side expression -(if present) has to evaluate to a number of values equal to the number of -variables on the left-hand-side. -This is the only situation where an expression evaluating -to more than one value is allowed. - -Expressions that are also statements (i.e. at the block level) have to -evaluate to zero values. - -In all other situations, expressions have to evaluate to exactly one value. - -The ``continue`` and ``break`` statements can only be used inside loop bodies -and have to be in the same function as the loop (or both have to be at the -top level). -The condition part of the for-loop has to evaluate to exactly one value. - -Literals cannot be larger than the their type. The largest type defined is 256-bit wide. - -Scoping Rules -------------- - -Scopes in JULIA are tied to Blocks (exceptions are functions and the for loop -as explained below) and all declarations -(``FunctionDefinition``, ``VariableDeclaration``) -introduce new identifiers into these scopes. - -Identifiers are visible in -the block they are defined in (including all sub-nodes and sub-blocks). -As an exception, identifiers defined in the "init" part of the for-loop -(the first block) are visible in all other parts of the for-loop -(but not outside of the loop). -Identifiers declared in the other parts of the for loop respect the regular -syntatical scoping rules. -The parameters and return parameters of functions are visible in the -function body and their names cannot overlap. - -Variables can only be referenced after their declaration. In particular, -variables cannot be referenced in the right hand side of their own variable -declaration. -Functions can be referenced already before their declaration (if they are visible). - -Shadowing is disallowed, i.e. you cannot declare an identifier at a point -where another identifier with the same name is also visible, even if it is -not accessible. - -Inside functions, it is not possible to access a variable that was declared -outside of that function. - -Formal Specification --------------------- - -We formally specify JULIA by providing an evaluation function E overloaded -on the various nodes of the AST. Any functions can have side effects, so -E takes two state objects and the AST node and returns two new -state objects and a variable number of other values. -The two state objects are the global state object -(which in the context of the EVM is the memory, storage and state of the -blockchain) and the local state object (the state of local variables, i.e. a -segment of the stack in the EVM). -If the AST node is a statement, E returns the two state objects and a "mode", -which is used for the ``break`` and ``continue`` statements. -If the AST node is an expression, E returns the two state objects and -as many values as the expression evaluates to. - - -The exact nature of the global state is unspecified for this high level -description. The local state ``L`` is a mapping of identifiers ``i`` to values ``v``, -denoted as ``L[i] = v``. - -For an identifier ``v``, let ``$v`` be the name of the identifier. - -We will use a destructuring notation for the AST nodes. - -.. code:: - - E(G, L, <{St1, ..., Stn}>: Block) = - let G1, L1, mode = E(G, L, St1, ..., Stn) - let L2 be a restriction of L1 to the identifiers of L - G1, L2, mode - E(G, L, St1, ..., Stn: Statement) = - if n is zero: - G, L, regular - else: - let G1, L1, mode = E(G, L, St1) - if mode is regular then - E(G1, L1, St2, ..., Stn) - otherwise - G1, L1, mode - E(G, L, FunctionDefinition) = - G, L, regular - E(G, L, <let var1, ..., varn := rhs>: VariableDeclaration) = - E(G, L, <var1, ..., varn := rhs>: Assignment) - E(G, L, <let var1, ..., varn>: VariableDeclaration) = - let L1 be a copy of L where L1[$vari] = 0 for i = 1, ..., n - G, L1, regular - E(G, L, <var1, ..., varn := rhs>: Assignment) = - let G1, L1, v1, ..., vn = E(G, L, rhs) - let L2 be a copy of L1 where L2[$vari] = vi for i = 1, ..., n - G, L2, regular - E(G, L, <for { i1, ..., in } condition post body>: ForLoop) = - if n >= 1: - let G1, L1, mode = E(G, L, i1, ..., in) - // mode has to be regular due to the syntactic restrictions - let G2, L2, mode = E(G1, L1, for {} condition post body) - // mode has to be regular due to the syntactic restrictions - let L3 be the restriction of L2 to only variables of L - G2, L3, regular - else: - let G1, L1, v = E(G, L, condition) - if v is false: - G1, L1, regular - else: - let G2, L2, mode = E(G1, L, body) - if mode is break: - G2, L2, regular - else: - G3, L3, mode = E(G2, L2, post) - E(G3, L3, for {} condition post body) - E(G, L, break: BreakContinue) = - G, L, break - E(G, L, continue: BreakContinue) = - G, L, continue - E(G, L, <if condition body>: If) = - let G0, L0, v = E(G, L, condition) - if v is true: - E(G0, L0, body) - else: - G0, L0, regular - E(G, L, <switch condition case l1:t1 st1 ... case ln:tn stn>: Switch) = - E(G, L, switch condition case l1:t1 st1 ... case ln:tn stn default {}) - E(G, L, <switch condition case l1:t1 st1 ... case ln:tn stn default st'>: Switch) = - let G0, L0, v = E(G, L, condition) - // i = 1 .. n - // Evaluate literals, context doesn't matter - let _, _, v1 = E(G0, L0, l1) - ... - let _, _, vn = E(G0, L0, ln) - if there exists smallest i such that vi = v: - E(G0, L0, sti) - else: - E(G0, L0, st') - - E(G, L, <name>: Identifier) = - G, L, L[$name] - E(G, L, <fname(arg1, ..., argn)>: FunctionCall) = - G1, L1, vn = E(G, L, argn) - ... - G(n-1), L(n-1), v2 = E(G(n-2), L(n-2), arg2) - Gn, Ln, v1 = E(G(n-1), L(n-1), arg1) - Let <function fname (param1, ..., paramn) -> ret1, ..., retm block> - be the function of name $fname visible at the point of the call. - Let L' be a new local state such that - L'[$parami] = vi and L'[$reti] = 0 for all i. - Let G'', L'', mode = E(Gn, L', block) - G'', Ln, L''[$ret1], ..., L''[$retm] - E(G, L, l: HexLiteral) = G, L, hexString(l), - where hexString decodes l from hex and left-aligns it into 32 bytes - E(G, L, l: StringLiteral) = G, L, utf8EncodeLeftAligned(l), - where utf8EncodeLeftAligned performs a utf8 encoding of l - and aligns it left into 32 bytes - E(G, L, n: HexNumber) = G, L, hex(n) - where hex is the hexadecimal decoding function - E(G, L, n: DecimalNumber) = G, L, dec(n), - where dec is the decimal decoding function - -Type Conversion Functions -------------------------- - -JULIA has no support for implicit type conversion and therefore functions exists to provide explicit conversion. -When converting a larger type to a shorter type a runtime exception can occur in case of an overflow. - -Truncating conversions are supported between the following types: - - ``bool`` - - ``u32`` - - ``u64`` - - ``u256`` - - ``s256`` - -For each of these a type conversion function exists having the prototype in the form of ``<input_type>to<output_type>(x:<input_type>) -> y:<output_type>``, -such as ``u32tobool(x:u32) -> y:bool``, ``u256tou32(x:u256) -> y:u32`` or ``s256tou256(x:s256) -> y:u256``. - -.. note:: - - ``u32tobool(x:u32) -> y:bool`` can be implemented as ``y := not(iszerou256(x))`` and - ``booltou32(x:bool) -> y:u32`` can be implemented as ``switch x case true:bool { y := 1:u32 } case false:bool { y := 0:u32 }`` - -Low-level Functions -------------------- - -The following functions must be available: - -+---------------------------------------------------------------------------------------------------------------+ -| *Logic* | -+---------------------------------------------+-----------------------------------------------------------------+ -| not(x:bool) -> z:bool | logical not | -+---------------------------------------------+-----------------------------------------------------------------+ -| and(x:bool, y:bool) -> z:bool | logical and | -+---------------------------------------------+-----------------------------------------------------------------+ -| or(x:bool, y:bool) -> z:bool | logical or | -+---------------------------------------------+-----------------------------------------------------------------+ -| xor(x:bool, y:bool) -> z:bool | xor | -+---------------------------------------------+-----------------------------------------------------------------+ -| *Arithmetics* | -+---------------------------------------------+-----------------------------------------------------------------+ -| addu256(x:u256, y:u256) -> z:u256 | x + y | -+---------------------------------------------+-----------------------------------------------------------------+ -| subu256(x:u256, y:u256) -> z:u256 | x - y | -+---------------------------------------------+-----------------------------------------------------------------+ -| mulu256(x:u256, y:u256) -> z:u256 | x * y | -+---------------------------------------------+-----------------------------------------------------------------+ -| divu256(x:u256, y:u256) -> z:u256 | x / y | -+---------------------------------------------+-----------------------------------------------------------------+ -| divs256(x:s256, y:s256) -> z:s256 | x / y, for signed numbers in two's complement | -+---------------------------------------------+-----------------------------------------------------------------+ -| modu256(x:u256, y:u256) -> z:u256 | x % y | -+---------------------------------------------+-----------------------------------------------------------------+ -| mods256(x:s256, y:s256) -> z:s256 | x % y, for signed numbers in two's complement | -+---------------------------------------------+-----------------------------------------------------------------+ -| signextendu256(i:u256, x:u256) -> z:u256 | sign extend from (i*8+7)th bit counting from least significant | -+---------------------------------------------+-----------------------------------------------------------------+ -| expu256(x:u256, y:u256) -> z:u256 | x to the power of y | -+---------------------------------------------+-----------------------------------------------------------------+ -| addmodu256(x:u256, y:u256, m:u256) -> z:u256| (x + y) % m with arbitrary precision arithmetics | -+---------------------------------------------+-----------------------------------------------------------------+ -| mulmodu256(x:u256, y:u256, m:u256) -> z:u256| (x * y) % m with arbitrary precision arithmetics | -+---------------------------------------------+-----------------------------------------------------------------+ -| ltu256(x:u256, y:u256) -> z:bool | true if x < y, false otherwise | -+---------------------------------------------+-----------------------------------------------------------------+ -| gtu256(x:u256, y:u256) -> z:bool | true if x > y, false otherwise | -+---------------------------------------------+-----------------------------------------------------------------+ -| sltu256(x:s256, y:s256) -> z:bool | true if x < y, false otherwise | -| | (for signed numbers in two's complement) | -+---------------------------------------------+-----------------------------------------------------------------+ -| sgtu256(x:s256, y:s256) -> z:bool | true if x > y, false otherwise | -| | (for signed numbers in two's complement) | -+---------------------------------------------+-----------------------------------------------------------------+ -| equ256(x:u256, y:u256) -> z:bool | true if x == y, false otherwise | -+---------------------------------------------+-----------------------------------------------------------------+ -| iszerou256(x:u256) -> z:bool | true if x == 0, false otherwise | -+---------------------------------------------+-----------------------------------------------------------------+ -| notu256(x:u256) -> z:u256 | ~x, every bit of x is negated | -+---------------------------------------------+-----------------------------------------------------------------+ -| andu256(x:u256, y:u256) -> z:u256 | bitwise and of x and y | -+---------------------------------------------+-----------------------------------------------------------------+ -| oru256(x:u256, y:u256) -> z:u256 | bitwise or of x and y | -+---------------------------------------------+-----------------------------------------------------------------+ -| xoru256(x:u256, y:u256) -> z:u256 | bitwise xor of x and y | -+---------------------------------------------+-----------------------------------------------------------------+ -| shlu256(x:u256, y:u256) -> z:u256 | logical left shift of x by y | -+---------------------------------------------+-----------------------------------------------------------------+ -| shru256(x:u256, y:u256) -> z:u256 | logical right shift of x by y | -+---------------------------------------------+-----------------------------------------------------------------+ -| saru256(x:u256, y:u256) -> z:u256 | arithmetic right shift of x by y | -+---------------------------------------------+-----------------------------------------------------------------+ -| byte(n:u256, x:u256) -> v:u256 | nth byte of x, where the most significant byte is the 0th byte | -| | Cannot this be just replaced by and256(shr256(n, x), 0xff) and | -| | let it be optimised out by the EVM backend? | -+---------------------------------------------+-----------------------------------------------------------------+ -| *Memory and storage* | -+---------------------------------------------+-----------------------------------------------------------------+ -| mload(p:u256) -> v:u256 | mem[p..(p+32)) | -+---------------------------------------------+-----------------------------------------------------------------+ -| mstore(p:u256, v:u256) | mem[p..(p+32)) := v | -+---------------------------------------------+-----------------------------------------------------------------+ -| mstore8(p:u256, v:u256) | mem[p] := v & 0xff - only modifies a single byte | -+---------------------------------------------+-----------------------------------------------------------------+ -| sload(p:u256) -> v:u256 | storage[p] | -+---------------------------------------------+-----------------------------------------------------------------+ -| sstore(p:u256, v:u256) | storage[p] := v | -+---------------------------------------------+-----------------------------------------------------------------+ -| msize() -> size:u256 | size of memory, i.e. largest accessed memory index, albeit due | -| | due to the memory extension function, which extends by words, | -| | this will always be a multiple of 32 bytes | -+---------------------------------------------+-----------------------------------------------------------------+ -| *Execution control* | -+---------------------------------------------+-----------------------------------------------------------------+ -| create(v:u256, p:u256, s:u256) | create new contract with code mem[p..(p+s)) and send v wei | -| | and return the new address | -+---------------------------------------------+-----------------------------------------------------------------+ -| call(g:u256, a:u256, v:u256, in:u256, | call contract at address a with input mem[in..(in+insize)) | -| insize:u256, out:u256, | providing g gas and v wei and output area | -| outsize:u256) | mem[out..(out+outsize)) returning 0 on error (eg. out of gas) | -| -> r:u256 | and 1 on success | -+---------------------------------------------+-----------------------------------------------------------------+ -| callcode(g:u256, a:u256, v:u256, in:u256, | identical to ``call`` but only use the code from a | -| insize:u256, out:u256, | and stay in the context of the | -| outsize:u256) -> r:u256 | current contract otherwise | -+---------------------------------------------+-----------------------------------------------------------------+ -| delegatecall(g:u256, a:u256, in:u256, | identical to ``callcode``, | -| insize:u256, out:u256, | but also keep ``caller`` | -| outsize:u256) -> r:u256 | and ``callvalue`` | -+---------------------------------------------+-----------------------------------------------------------------+ -| abort() | abort (equals to invalid instruction on EVM) | -+---------------------------------------------+-----------------------------------------------------------------+ -| return(p:u256, s:u256) | end execution, return data mem[p..(p+s)) | -+---------------------------------------------+-----------------------------------------------------------------+ -| revert(p:u256, s:u256) | end execution, revert state changes, return data mem[p..(p+s)) | -+---------------------------------------------+-----------------------------------------------------------------+ -| selfdestruct(a:u256) | end execution, destroy current contract and send funds to a | -+---------------------------------------------+-----------------------------------------------------------------+ -| log0(p:u256, s:u256) | log without topics and data mem[p..(p+s)) | -+---------------------------------------------+-----------------------------------------------------------------+ -| log1(p:u256, s:u256, t1:u256) | log with topic t1 and data mem[p..(p+s)) | -+---------------------------------------------+-----------------------------------------------------------------+ -| log2(p:u256, s:u256, t1:u256, t2:u256) | log with topics t1, t2 and data mem[p..(p+s)) | -+---------------------------------------------+-----------------------------------------------------------------+ -| log3(p:u256, s:u256, t1:u256, t2:u256, | log with topics t, t2, t3 and data mem[p..(p+s)) | -| t3:u256) | | -+---------------------------------------------+-----------------------------------------------------------------+ -| log4(p:u256, s:u256, t1:u256, t2:u256, | log with topics t1, t2, t3, t4 and data mem[p..(p+s)) | -| t3:u256, t4:u256) | | -+---------------------------------------------+-----------------------------------------------------------------+ -| *State queries* | -+---------------------------------------------+-----------------------------------------------------------------+ -| blockcoinbase() -> address:u256 | current mining beneficiary | -+---------------------------------------------+-----------------------------------------------------------------+ -| blockdifficulty() -> difficulty:u256 | difficulty of the current block | -+---------------------------------------------+-----------------------------------------------------------------+ -| blockgaslimit() -> limit:u256 | block gas limit of the current block | -+---------------------------------------------+-----------------------------------------------------------------+ -| blockhash(b:u256) -> hash:u256 | hash of block nr b - only for last 256 blocks excluding current | -+---------------------------------------------+-----------------------------------------------------------------+ -| blocknumber() -> block:u256 | current block number | -+---------------------------------------------+-----------------------------------------------------------------+ -| blocktimestamp() -> timestamp:u256 | timestamp of the current block in seconds since the epoch | -+---------------------------------------------+-----------------------------------------------------------------+ -| txorigin() -> address:u256 | transaction sender | -+---------------------------------------------+-----------------------------------------------------------------+ -| txgasprice() -> price:u256 | gas price of the transaction | -+---------------------------------------------+-----------------------------------------------------------------+ -| gasleft() -> gas:u256 | gas still available to execution | -+---------------------------------------------+-----------------------------------------------------------------+ -| balance(a:u256) -> v:u256 | wei balance at address a | -+---------------------------------------------+-----------------------------------------------------------------+ -| this() -> address:u256 | address of the current contract / execution context | -+---------------------------------------------+-----------------------------------------------------------------+ -| caller() -> address:u256 | call sender (excluding delegatecall) | -+---------------------------------------------+-----------------------------------------------------------------+ -| callvalue() -> v:u256 | wei sent together with the current call | -+---------------------------------------------+-----------------------------------------------------------------+ -| calldataload(p:u256) -> v:u256 | call data starting from position p (32 bytes) | -+---------------------------------------------+-----------------------------------------------------------------+ -| calldatasize() -> v:u256 | size of call data in bytes | -+---------------------------------------------+-----------------------------------------------------------------+ -| calldatacopy(t:u256, f:u256, s:u256) | copy s bytes from calldata at position f to mem at position t | -+---------------------------------------------+-----------------------------------------------------------------+ -| codesize() -> size:u256 | size of the code of the current contract / execution context | -+---------------------------------------------+-----------------------------------------------------------------+ -| codecopy(t:u256, f:u256, s:u256) | copy s bytes from code at position f to mem at position t | -+---------------------------------------------+-----------------------------------------------------------------+ -| extcodesize(a:u256) -> size:u256 | size of the code at address a | -+---------------------------------------------+-----------------------------------------------------------------+ -| extcodecopy(a:u256, t:u256, f:u256, s:u256) | like codecopy(t, f, s) but take code at address a | -+---------------------------------------------+-----------------------------------------------------------------+ -| *Others* | -+---------------------------------------------+-----------------------------------------------------------------+ -| discard(unused:bool) | discard value | -+---------------------------------------------+-----------------------------------------------------------------+ -| discardu256(unused:u256) | discard value | -+---------------------------------------------+-----------------------------------------------------------------+ -| splitu256tou64(x:u256) -> (x1:u64, x2:u64, | split u256 to four u64's | -| x3:u64, x4:u64) | | -+---------------------------------------------+-----------------------------------------------------------------+ -| combineu64tou256(x1:u64, x2:u64, x3:u64, | combine four u64's into a single u256 | -| x4:u64) -> (x:u256) | | -+---------------------------------------------+-----------------------------------------------------------------+ -| keccak256(p:u256, s:u256) -> v:u256 | keccak(mem[p...(p+s))) | -+---------------------------------------------+-----------------------------------------------------------------+ - -Backends --------- - -Backends or targets are the translators from JULIA to a specific bytecode. Each of the backends can expose functions -prefixed with the name of the backend. We reserve ``evm_`` and ``ewasm_`` prefixes for the two proposed backends. - -Backend: EVM ------------- - -The EVM target will have all the underlying EVM opcodes exposed with the `evm_` prefix. - -Backend: "EVM 1.5" ------------------- - -TBD - -Backend: eWASM --------------- - -TBD - -Specification of JULIA Object -============================= - -Grammar:: - - TopLevelObject = 'object' '{' Code? ( Object | Data )* '}' - Object = 'object' StringLiteral '{' Code? ( Object | Data )* '}' - Code = 'code' Block - Data = 'data' StringLiteral HexLiteral - HexLiteral = 'hex' ('"' ([0-9a-fA-F]{2})* '"' | '\'' ([0-9a-fA-F]{2})* '\'') - StringLiteral = '"' ([^"\r\n\\] | '\\' .)* '"' - -Above, ``Block`` refers to ``Block`` in the JULIA code grammar explained in the previous chapter. - -An example JULIA Object is shown below: - -..code:: - - // Code consists of a single object. A single "code" node is the code of the object. - // Every (other) named object or data section is serialized and - // made accessible to the special built-in functions datacopy / dataoffset / datasize - object { - code { - let size = datasize("runtime") - let offset = allocate(size) - // This will turn into a memory->memory copy for eWASM and - // a codecopy for EVM - datacopy(dataoffset("runtime"), offset, size) - // this is a constructor and the runtime code is returned - return(offset, size) - } - - data "Table2" hex"4123" - - object "runtime" { - code { - // runtime code - - let size = datasize("Contract2") - let offset = allocate(size) - // This will turn into a memory->memory copy for eWASM and - // a codecopy for EVM - datacopy(dataoffset("Contract2"), offset, size) - // constructor parameter is a single number 0x1234 - mstore(add(offset, size), 0x1234) - create(offset, add(size, 32)) - } - - // Embedded object. Use case is that the outside is a factory contract, - // and Contract2 is the code to be created by the factory - object "Contract2" { - code { - // code here ... - } - - object "runtime" { - code { - // code here ... - } - } - - data "Table1" hex"4123" - } - } - } diff --git a/docs/layout-of-source-files.rst b/docs/layout-of-source-files.rst index f9d197b7..9a8bdc3e 100644 --- a/docs/layout-of-source-files.rst +++ b/docs/layout-of-source-files.rst @@ -2,15 +2,29 @@ Layout of a Solidity Source File ******************************** -Source files can contain an arbitrary number of contract definitions, include directives -and pragma directives. +Source files can contain an arbitrary number of +:ref:`contract definitions<contract_structure>`, import_ directives +and :ref:`pragma directives<pragma>`. + +.. index:: ! pragma + +.. _pragma: + +Pragmas +======= + +The ``pragma`` keyword can be used to enable certain compiler features +or checks. A pragma directive is always local to a source file, so +you have to add the pragma to all your files if you want enable it +in all of your project. If you :ref:`import<import>` another file, the pragma +from that file will not automatically apply to the importing file. .. index:: ! pragma, version .. _version_pragma: Version Pragma -============== +-------------- Source files can (and should) be annotated with a so-called version pragma to reject being compiled with future compiler versions that might introduce incompatible @@ -35,6 +49,53 @@ the exact version of the compiler, so that bugfix releases are still possible. It is possible to specify much more complex rules for the compiler version, the expression follows those used by `npm <https://docs.npmjs.com/misc/semver>`_. +.. note:: + Using the version pragma will *not* change the version of the compiler. + It will also *not* enable or disable features of the compiler. It will just + instruct the compiler to check whether its version matches the one + required by the pragma. If it does not match, the compiler will issue + an error. + +.. index:: ! pragma, experimental + +.. _experimental_pragma: + +Experimental Pragma +------------------- + +The second pragma is the experimental pragma. It can be used to enable +features of the compiler or language that are not yet enabled by default. +The following experimental pragmas are currently supported: + + +ABIEncoderV2 +~~~~~~~~~~~~ + +The new ABI encoder is able to encode and decode arbitrarily nested +arrays and structs. It produces less optimal code (the optimizer +for this part of the code is still under development) and has not +received as much testing as the old encoder. You can activate it +using ``pragma experimental ABIEncoderV2;``. + +.. _smt_checker: + +SMTChecker +~~~~~~~~~~ + +This component has to be enabled when the Solidity compiler is built +and therefore it is not available in all Solidity binaries. +The :ref:`build instructions<smt_solvers_build>` explain how to activate this option. +It is activated for the Ubuntu PPA releases in most versions, +but not for solc-js, the Docker images, Windows binaries or the +statically-built Linux binaries. + +If you use +``pragma experimental SMTChecker;``, then you get additional +safety warnings which are obtained by querying an SMT solver. +The component does not yet support all features of the Solidity language +and likely outputs many warnings. In case it reports unsupported +features, the analysis may not be fully sound. + .. index:: source file, ! import .. _import: @@ -56,18 +117,27 @@ At a global level, you can use import statements of the following form: This statement imports all global symbols from "filename" (and symbols imported there) into the current global scope (different than in ES6 but backwards-compatible for Solidity). +This simple form is not recommended for use, because it pollutes the namespace in an +unpredictable way: If you add new top-level items inside "filename", they will automatically +appear in all files that import like this from "filename". It is better to import specific +symbols explicitly. + +The following example creates a new global symbol ``symbolName`` whose members are all +the global symbols from ``"filename"``. :: import * as symbolName from "filename"; -...creates a new global symbol ``symbolName`` whose members are all the global symbols from ``"filename"``. +If there is a naming collision, you can also rename symbols while importing. +This code +creates new global symbols ``alias`` and ``symbol2`` which reference ``symbol1`` and ``symbol2`` from inside ``"filename"``, respectively. :: import {symbol1 as alias, symbol2} from "filename"; -...creates new global symbols ``alias`` and ``symbol2`` which reference ``symbol1`` and ``symbol2`` from ``"filename"``, respectively. + Another syntax is not part of ES6, but probably convenient: @@ -77,6 +147,10 @@ Another syntax is not part of ES6, but probably convenient: which is equivalent to ``import * as symbolName from "filename";``. +.. note:: + If you use `import "filename.sol" as moduleName;`, you access a contract called `C` + from inside `"filename.sol"` as `moduleName.C` and not by using `C` directly. + Paths ----- @@ -93,47 +167,52 @@ It depends on the compiler (see below) how to actually resolve the paths. In general, the directory hierarchy does not need to strictly map onto your local filesystem, it can also map to resources discovered via e.g. ipfs, http or git. +.. note:: + Always use relative imports like ``import "./filename.sol";`` and avoid + using ``..`` in path specifiers. In the latter case, it is probably better to use + global paths and set up remappings as explained below. + Use in Actual Compilers ----------------------- -When the compiler is invoked, it is not only possible to specify how to -discover the first element of a path, but it is possible to specify path prefix -remappings so that e.g. ``github.com/ethereum/dapp-bin/library`` is remapped to -``/usr/local/dapp-bin/library`` and the compiler will read the files from there. -If multiple remappings can be applied, the one with the longest key is tried first. This -allows for a "fallback-remapping" with e.g. ``""`` maps to -``"/usr/local/include/solidity"``. Furthermore, these remappings can -depend on the context, which allows you to configure packages to -import e.g. different versions of a library of the same name. +When invoking the compiler, you can specify how to discover the first element +of a path, and also path prefix remappings. For +example you can setup a remapping so that everything imported from the virtual +directory ``github.com/ethereum/dapp-bin/library`` would actually be read from +your local directory ``/usr/local/dapp-bin/library``. +If multiple remappings apply, the one with the longest key is tried first. +An empty prefix is not allowed. The remappings can depend on a context, +which allows you to configure packages to import e.g., different versions of a +library of the same name. **solc**: -For solc (the commandline compiler), these remappings are provided as +For solc (the commandline compiler), you provide these path remappings as ``context:prefix=target`` arguments, where both the ``context:`` and the -``=target`` parts are optional (where target defaults to prefix in that +``=target`` parts are optional (``target`` defaults to ``prefix`` in this case). All remapping values that are regular files are compiled (including -their dependencies). This mechanism is completely backwards-compatible (as long -as no filename contains = or :) and thus not a breaking change. All imports -in files in or below the directory ``context`` that import a file that -starts with ``prefix`` are redirected by replacing ``prefix`` by ``target``. +their dependencies). + +This mechanism is backwards-compatible (as long +as no filename contains ``=`` or ``:``) and thus not a breaking change. All +files in or below the ``context`` directory that import a file that starts with +``prefix`` are redirected by replacing ``prefix`` by ``target``. -So as an example, if you clone -``github.com/ethereum/dapp-bin/`` locally to ``/usr/local/dapp-bin``, you can use -the following in your source file: +For example, if you clone ``github.com/ethereum/dapp-bin/`` locally to +``/usr/local/dapp-bin``, you can use the following in your source file: :: import "github.com/ethereum/dapp-bin/library/iterable_mapping.sol" as it_mapping; -and then run the compiler as +Then run the compiler: .. code-block:: bash solc github.com/ethereum/dapp-bin/=/usr/local/dapp-bin/ source.sol -As a more complex example, suppose you rely on some module that uses a -very old version of dapp-bin. That old version of dapp-bin is checked -out at ``/usr/local/dapp-bin_old``, then you can use +As a more complex example, suppose you rely on a module that uses an old +version of dapp-bin that you checked out to ``/usr/local/dapp-bin_old``, then you can run: .. code-block:: bash @@ -141,28 +220,29 @@ out at ``/usr/local/dapp-bin_old``, then you can use module2:github.com/ethereum/dapp-bin/=/usr/local/dapp-bin_old/ \ source.sol -so that all imports in ``module2`` point to the old version but imports -in ``module1`` get the new version. +This means that all imports in ``module2`` point to the old version but imports +in ``module1`` point to the new version. -Note that solc only allows you to include files from certain directories: -They have to be in the directory (or subdirectory) of one of the explicitly -specified source files or in the directory (or subdirectory) of a remapping -target. If you want to allow direct absolute includes, just add the -remapping ``=/``. +.. note:: + + ``solc`` only allows you to include files from certain directories. They have + to be in the directory (or subdirectory) of one of the explicitly specified + source files or in the directory (or subdirectory) of a remapping target. If + you want to allow direct absolute includes, add the remapping ``/=/``. If there are multiple remappings that lead to a valid file, the remapping with the longest common prefix is chosen. **Remix**: -`Remix <https://remix.ethereum.org/>`_ -provides an automatic remapping for github and will also automatically retrieve -the file over the network: -You can import the iterable mapping by e.g. -``import "github.com/ethereum/dapp-bin/library/iterable_mapping.sol" as it_mapping;``. +`Remix <https://remix.ethereum.org/>`_ provides an automatic remapping for +GitHub and automatically retrieves the file over the network. You can import +the iterable mapping as above, e.g. -Other source code providers may be added in the future. +:: + import "github.com/ethereum/dapp-bin/library/iterable_mapping.sol" as it_mapping; +Remix may add other source code providers in the future. .. index:: ! comment, natspec @@ -180,6 +260,11 @@ Single-line comments (``//``) and multi-line comments (``/*...*/``) are possible multi-line comment. */ +.. note:: + A single-line comment is terminated by any unicode line terminator + (LF, VF, FF, CR, NEL, LS or PS) in utf8 encoding. The terminator is still part of + the source code after the comment, so if it is not an ascii symbol + (these are NEL, LS and PS), it will lead to a parser error. Additionally, there is another type of comment called a natspec comment, for which the documentation is not yet written. They are written with a @@ -195,17 +280,17 @@ for the two input parameters and two returned values. :: - pragma solidity ^0.4.0; + pragma solidity >=0.4.0 <0.6.0; /** @title Shape calculator. */ - contract shapeCalculator { + contract ShapeCalculator { /** @dev Calculates a rectangle's surface and perimeter. * @param w Width of the rectangle. * @param h Height of the rectangle. * @return s The calculated surface. * @return p The calculated perimeter. */ - function rectangle(uint w, uint h) returns (uint s, uint p) { + function rectangle(uint w, uint h) public pure returns (uint s, uint p) { s = w * h; p = 2 * (w + h); } diff --git a/docs/lll.rst b/docs/lll.rst new file mode 100644 index 00000000..d9409bf8 --- /dev/null +++ b/docs/lll.rst @@ -0,0 +1,14 @@ +### +LLL +### + +.. _lll: + +LLL is a low-level language for the EVM with an s-expressions syntax. + +The Solidity repository contains an LLL compiler, which shares the assembler subsystem with Solidity. +However, apart from maintaining that it still compiles, no other improvements are made to it. + +.. warning:: + + The LLL codebase is deprecated and will be removed from the Solidity repository in the future. diff --git a/docs/make.bat b/docs/make.bat index de2bccf7..bc06e706 100644 --- a/docs/make.bat +++ b/docs/make.bat @@ -1,242 +1,242 @@ -@ECHO OFF
-
-REM Command file for Sphinx documentation
-
-if "%SPHINXBUILD%" == "" (
- set SPHINXBUILD=sphinx-build
-)
-set BUILDDIR=_build
-set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% .
-set I18NSPHINXOPTS=%SPHINXOPTS% .
-if NOT "%PAPER%" == "" (
- set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS%
- set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS%
-)
-
-if "%1" == "" goto help
-
-if "%1" == "help" (
- :help
- echo.Please use `make ^<target^>` where ^<target^> is one of
- echo. html to make standalone HTML files
- echo. dirhtml to make HTML files named index.html in directories
- echo. singlehtml to make a single large HTML file
- echo. pickle to make pickle files
- echo. json to make JSON files
- echo. htmlhelp to make HTML files and a HTML help project
- echo. qthelp to make HTML files and a qthelp project
- echo. devhelp to make HTML files and a Devhelp project
- echo. epub to make an epub
- echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter
- echo. text to make text files
- echo. man to make manual pages
- echo. texinfo to make Texinfo files
- echo. gettext to make PO message catalogs
- echo. changes to make an overview over all changed/added/deprecated items
- echo. xml to make Docutils-native XML files
- echo. pseudoxml to make pseudoxml-XML files for display purposes
- echo. linkcheck to check all external links for integrity
- echo. doctest to run all doctests embedded in the documentation if enabled
- goto end
-)
-
-if "%1" == "clean" (
- for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i
- del /q /s %BUILDDIR%\*
- goto end
-)
-
-
-%SPHINXBUILD% 2> nul
-if errorlevel 9009 (
- echo.
- echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
- echo.installed, then set the SPHINXBUILD environment variable to point
- echo.to the full path of the 'sphinx-build' executable. Alternatively you
- echo.may add the Sphinx directory to PATH.
- echo.
- echo.If you don't have Sphinx installed, grab it from
- echo.http://sphinx-doc.org/
- exit /b 1
-)
-
-if "%1" == "html" (
- %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html
- if errorlevel 1 exit /b 1
- echo.
- echo.Build finished. The HTML pages are in %BUILDDIR%/html.
- goto end
-)
-
-if "%1" == "dirhtml" (
- %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml
- if errorlevel 1 exit /b 1
- echo.
- echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml.
- goto end
-)
-
-if "%1" == "singlehtml" (
- %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml
- if errorlevel 1 exit /b 1
- echo.
- echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml.
- goto end
-)
-
-if "%1" == "pickle" (
- %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle
- if errorlevel 1 exit /b 1
- echo.
- echo.Build finished; now you can process the pickle files.
- goto end
-)
-
-if "%1" == "json" (
- %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json
- if errorlevel 1 exit /b 1
- echo.
- echo.Build finished; now you can process the JSON files.
- goto end
-)
-
-if "%1" == "htmlhelp" (
- %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp
- if errorlevel 1 exit /b 1
- echo.
- echo.Build finished; now you can run HTML Help Workshop with the ^
-.hhp project file in %BUILDDIR%/htmlhelp.
- goto end
-)
-
-if "%1" == "qthelp" (
- %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp
- if errorlevel 1 exit /b 1
- echo.
- echo.Build finished; now you can run "qcollectiongenerator" with the ^
-.qhcp project file in %BUILDDIR%/qthelp, like this:
- echo.^> qcollectiongenerator %BUILDDIR%\qthelp\Solidity.qhcp
- echo.To view the help file:
- echo.^> assistant -collectionFile %BUILDDIR%\qthelp\Solidity.ghc
- goto end
-)
-
-if "%1" == "devhelp" (
- %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp
- if errorlevel 1 exit /b 1
- echo.
- echo.Build finished.
- goto end
-)
-
-if "%1" == "epub" (
- %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub
- if errorlevel 1 exit /b 1
- echo.
- echo.Build finished. The epub file is in %BUILDDIR%/epub.
- goto end
-)
-
-if "%1" == "latex" (
- %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
- if errorlevel 1 exit /b 1
- echo.
- echo.Build finished; the LaTeX files are in %BUILDDIR%/latex.
- goto end
-)
-
-if "%1" == "latexpdf" (
- %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
- cd %BUILDDIR%/latex
- make all-pdf
- cd %BUILDDIR%/..
- echo.
- echo.Build finished; the PDF files are in %BUILDDIR%/latex.
- goto end
-)
-
-if "%1" == "latexpdfja" (
- %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
- cd %BUILDDIR%/latex
- make all-pdf-ja
- cd %BUILDDIR%/..
- echo.
- echo.Build finished; the PDF files are in %BUILDDIR%/latex.
- goto end
-)
-
-if "%1" == "text" (
- %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text
- if errorlevel 1 exit /b 1
- echo.
- echo.Build finished. The text files are in %BUILDDIR%/text.
- goto end
-)
-
-if "%1" == "man" (
- %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man
- if errorlevel 1 exit /b 1
- echo.
- echo.Build finished. The manual pages are in %BUILDDIR%/man.
- goto end
-)
-
-if "%1" == "texinfo" (
- %SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo
- if errorlevel 1 exit /b 1
- echo.
- echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo.
- goto end
-)
-
-if "%1" == "gettext" (
- %SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale
- if errorlevel 1 exit /b 1
- echo.
- echo.Build finished. The message catalogs are in %BUILDDIR%/locale.
- goto end
-)
-
-if "%1" == "changes" (
- %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes
- if errorlevel 1 exit /b 1
- echo.
- echo.The overview file is in %BUILDDIR%/changes.
- goto end
-)
-
-if "%1" == "linkcheck" (
- %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck
- if errorlevel 1 exit /b 1
- echo.
- echo.Link check complete; look for any errors in the above output ^
-or in %BUILDDIR%/linkcheck/output.txt.
- goto end
-)
-
-if "%1" == "doctest" (
- %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest
- if errorlevel 1 exit /b 1
- echo.
- echo.Testing of doctests in the sources finished, look at the ^
-results in %BUILDDIR%/doctest/output.txt.
- goto end
-)
-
-if "%1" == "xml" (
- %SPHINXBUILD% -b xml %ALLSPHINXOPTS% %BUILDDIR%/xml
- if errorlevel 1 exit /b 1
- echo.
- echo.Build finished. The XML files are in %BUILDDIR%/xml.
- goto end
-)
-
-if "%1" == "pseudoxml" (
- %SPHINXBUILD% -b pseudoxml %ALLSPHINXOPTS% %BUILDDIR%/pseudoxml
- if errorlevel 1 exit /b 1
- echo.
- echo.Build finished. The pseudo-XML files are in %BUILDDIR%/pseudoxml.
- goto end
-)
-
-:end
+@ECHO OFF + +REM Command file for Sphinx documentation + +if "%SPHINXBUILD%" == "" ( + set SPHINXBUILD=sphinx-build +) +set BUILDDIR=_build +set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% . +set I18NSPHINXOPTS=%SPHINXOPTS% . +if NOT "%PAPER%" == "" ( + set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS% + set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS% +) + +if "%1" == "" goto help + +if "%1" == "help" ( + :help + echo.Please use `make ^<target^>` where ^<target^> is one of + echo. html to make standalone HTML files + echo. dirhtml to make HTML files named index.html in directories + echo. singlehtml to make a single large HTML file + echo. pickle to make pickle files + echo. json to make JSON files + echo. htmlhelp to make HTML files and a HTML help project + echo. qthelp to make HTML files and a qthelp project + echo. devhelp to make HTML files and a Devhelp project + echo. epub to make an epub + echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter + echo. text to make text files + echo. man to make manual pages + echo. texinfo to make Texinfo files + echo. gettext to make PO message catalogs + echo. changes to make an overview over all changed/added/deprecated items + echo. xml to make Docutils-native XML files + echo. pseudoxml to make pseudoxml-XML files for display purposes + echo. linkcheck to check all external links for integrity + echo. doctest to run all doctests embedded in the documentation if enabled + goto end +) + +if "%1" == "clean" ( + for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i + del /q /s %BUILDDIR%\* + goto end +) + + +%SPHINXBUILD% 2> nul +if errorlevel 9009 ( + echo. + echo.The 'sphinx-build' command was not found. Make sure you have Sphinx + echo.installed, then set the SPHINXBUILD environment variable to point + echo.to the full path of the 'sphinx-build' executable. Alternatively you + echo.may add the Sphinx directory to PATH. + echo. + echo.If you don't have Sphinx installed, grab it from + echo.http://sphinx-doc.org/ + exit /b 1 +) + +if "%1" == "html" ( + %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The HTML pages are in %BUILDDIR%/html. + goto end +) + +if "%1" == "dirhtml" ( + %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml. + goto end +) + +if "%1" == "singlehtml" ( + %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml. + goto end +) + +if "%1" == "pickle" ( + %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; now you can process the pickle files. + goto end +) + +if "%1" == "json" ( + %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; now you can process the JSON files. + goto end +) + +if "%1" == "htmlhelp" ( + %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; now you can run HTML Help Workshop with the ^ +.hhp project file in %BUILDDIR%/htmlhelp. + goto end +) + +if "%1" == "qthelp" ( + %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; now you can run "qcollectiongenerator" with the ^ +.qhcp project file in %BUILDDIR%/qthelp, like this: + echo.^> qcollectiongenerator %BUILDDIR%\qthelp\Solidity.qhcp + echo.To view the help file: + echo.^> assistant -collectionFile %BUILDDIR%\qthelp\Solidity.ghc + goto end +) + +if "%1" == "devhelp" ( + %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. + goto end +) + +if "%1" == "epub" ( + %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The epub file is in %BUILDDIR%/epub. + goto end +) + +if "%1" == "latex" ( + %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; the LaTeX files are in %BUILDDIR%/latex. + goto end +) + +if "%1" == "latexpdf" ( + %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex + cd %BUILDDIR%/latex + make all-pdf + cd %BUILDDIR%/.. + echo. + echo.Build finished; the PDF files are in %BUILDDIR%/latex. + goto end +) + +if "%1" == "latexpdfja" ( + %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex + cd %BUILDDIR%/latex + make all-pdf-ja + cd %BUILDDIR%/.. + echo. + echo.Build finished; the PDF files are in %BUILDDIR%/latex. + goto end +) + +if "%1" == "text" ( + %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The text files are in %BUILDDIR%/text. + goto end +) + +if "%1" == "man" ( + %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The manual pages are in %BUILDDIR%/man. + goto end +) + +if "%1" == "texinfo" ( + %SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo. + goto end +) + +if "%1" == "gettext" ( + %SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The message catalogs are in %BUILDDIR%/locale. + goto end +) + +if "%1" == "changes" ( + %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes + if errorlevel 1 exit /b 1 + echo. + echo.The overview file is in %BUILDDIR%/changes. + goto end +) + +if "%1" == "linkcheck" ( + %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck + if errorlevel 1 exit /b 1 + echo. + echo.Link check complete; look for any errors in the above output ^ +or in %BUILDDIR%/linkcheck/output.txt. + goto end +) + +if "%1" == "doctest" ( + %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest + if errorlevel 1 exit /b 1 + echo. + echo.Testing of doctests in the sources finished, look at the ^ +results in %BUILDDIR%/doctest/output.txt. + goto end +) + +if "%1" == "xml" ( + %SPHINXBUILD% -b xml %ALLSPHINXOPTS% %BUILDDIR%/xml + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The XML files are in %BUILDDIR%/xml. + goto end +) + +if "%1" == "pseudoxml" ( + %SPHINXBUILD% -b pseudoxml %ALLSPHINXOPTS% %BUILDDIR%/pseudoxml + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The pseudo-XML files are in %BUILDDIR%/pseudoxml. + goto end +) + +:end diff --git a/docs/metadata.rst b/docs/metadata.rst index 5e37219e..c0613809 100644 --- a/docs/metadata.rst +++ b/docs/metadata.rst @@ -4,28 +4,28 @@ Contract Metadata .. index:: metadata, contract verification -The Solidity compiler automatically generates a JSON file, the -contract metadata, that contains information about the current contract. -It can be used to query the compiler version, the sources used, the ABI -and NatSpec documentation in order to more safely interact with the contract -and to verify its source code. +The Solidity compiler automatically generates a JSON file, the contract +metadata, that contains information about the current contract. You can use +this file to query the compiler version, the sources used, the ABI and NatSpec +documentation to more safely interact with the contract and verify its source +code. The compiler appends a Swarm hash of the metadata file to the end of the bytecode (for details, see below) of each contract, so that you can retrieve the file in an authenticated way without having to resort to a centralized data provider. -Of course, you have to publish the metadata file to Swarm (or some other service) -so that others can access it. The file can be output by using ``solc --metadata`` -and the file will be called ``ContractName_meta.json``. -It will contain Swarm references to the source code, so you have to upload -all source files and the metadata file. +You have to publish the metadata file to Swarm (or another service) so that +others can access it. You create the file by using the ``solc --metadata`` +command that generates a file called ``ContractName_meta.json``. It contains +Swarm references to the source code, so you have to upload all source files and +the metadata file. The metadata file has the following format. The example below is presented in a human-readable way. Properly formatted metadata should use quotes correctly, reduce whitespace to a minimum and sort the keys of all objects to arrive at a -unique formatting. -Comments are of course also not permitted and used here only for explanatory purposes. +unique formatting. Comments are not permitted and used here only for +explanatory purposes. .. code-block:: none @@ -93,14 +93,15 @@ Comments are of course also not permitted and used here only for explanatory pur } } -.. note:: - Note the ABI definition above has no fixed order. It can change with compiler versions. +.. warning:: + Since the bytecode of the resulting contract contains the metadata hash, any + change to the metadata results in a change of the bytecode. This includes + changes to a filename or path, and since the metadata includes a hash of all the + sources used, a single whitespace change results in different metadata, and + different bytecode. .. note:: - Since the bytecode of the resulting contract contains the metadata hash, any change to - the metadata will result in a change of the bytecode. Furthermore, since the metadata - includes a hash of all the sources used, a single whitespace change in any of the source - codes will result in a different metadata, and subsequently a different bytecode. + Note the ABI definition above has no fixed order. It can change with compiler versions. Encoding of the Metadata Hash in the Bytecode ============================================= @@ -117,21 +118,30 @@ to the end of the deployed bytecode:: So in order to retrieve the data, the end of the deployed bytecode can be checked to match that pattern and use the Swarm hash to retrieve the file. +.. note:: + The compiler currently uses the "swarm version 0" hash of the metadata, + but this might change in the future, so do not rely on this sequence + to start with ``0xa1 0x65 'b' 'z' 'z' 'r' '0'``. We might also + add additional data to this CBOR structure, so the + best option is to use a proper CBOR parser. + + Usage for Automatic Interface Generation and NatSpec ==================================================== The metadata is used in the following way: A component that wants to interact -with a contract (e.g. Mist) retrieves the code of the contract, from that +with a contract (e.g. Mist or any wallet) retrieves the code of the contract, from that the Swarm hash of a file which is then retrieved. That file is JSON-decoded into a structure like above. The component can then use the ABI to automatically generate a rudimentary user interface for the contract. -Furthermore, Mist can use the userdoc to display a confirmation message to the user -whenever they interact with the contract. +Furthermore, the wallet can use the NatSpec user documentation to display a confirmation message to the user +whenever they interact with the contract, together with requesting +authorization for the transaction signature. -Additional information about Ethereum Natural Specification (NatSpec) can be found `here <https://github.com/ethereum/wiki/wiki/Ethereum-Natural-Specification-Format>`_. +Additional information about Ethereum Natural Specification (NatSpec) can be found `here <https://github.com/ethereum/wiki/wiki/Ethereum-Natural-Specification-Format>`_. Usage for Source Code Verification ================================== diff --git a/docs/miscellaneous.rst b/docs/miscellaneous.rst index c7c32528..21978ded 100644 --- a/docs/miscellaneous.rst +++ b/docs/miscellaneous.rst @@ -32,23 +32,33 @@ Statically-sized variables (everything except mapping and dynamically-sized arra ``uint128, uint256, uint128``, as the former will only take up two slots of storage whereas the latter will take up three. + .. note:: + The layout of state variables in storage is considered to be part of the external interface + of Solidity due to the fact that storage pointers can be passed to libraries. This means that + any change to the rules outlined in this section is considered a breaking change + of the language and due to its critical nature should be considered very carefully before + being executed. + + The elements of structs and arrays are stored after each other, just as if they were given explicitly. +Mappings and Dynamic Arrays +=========================== + Due to their unpredictable size, mapping and dynamically-sized array types use a Keccak-256 hash computation to find the starting position of the value or the array data. These starting positions are always full stack slots. -The mapping or the dynamic array itself -occupies an (unfilled) slot in storage at some position ``p`` according to the above rule (or by -recursively applying this rule for mappings to mappings or arrays of arrays). For a dynamic array, this slot stores the number of elements in the array (byte arrays and strings are an exception here, see below). For a mapping, the slot is unused (but it is needed so that two equal mappings after each other will use a different hash distribution). -Array data is located at ``keccak256(p)`` and the value corresponding to a mapping key +The mapping or the dynamic array itself occupies a slot in storage at some position ``p`` +according to the above rule (or by recursively applying this rule for mappings of mappings or arrays of arrays). For dynamic arrays, +this slot stores the number of elements in the array (byte arrays and strings are an exception, see :ref:`below <bytes-and-string>`). +For mappings, the slot is unused (but it is needed so that two equal mappings after each other will use a different +hash distribution). Array data is located at ``keccak256(p)`` and the value corresponding to a mapping key ``k`` is located at ``keccak256(k . p)`` where ``.`` is concatenation. If the value is again a non-elementary type, the positions are found by adding an offset of ``keccak256(k . p)``. -``bytes`` and ``string`` store their data in the same slot where also the length is stored if they are short. In particular: If the data is at most ``31`` bytes long, it is stored in the higher-order bytes (left aligned) and the lowest-order byte stores ``length * 2``. If it is longer, the main slot stores ``length * 2 + 1`` and the data is stored as usual in ``keccak256(slot)``. - So for the following contract snippet:: - pragma solidity ^0.4.0; + pragma solidity >=0.4.0 <0.6.0; contract C { struct s { uint a; uint b; } @@ -58,27 +68,43 @@ So for the following contract snippet:: The position of ``data[4][9].b`` is at ``keccak256(uint256(9) . keccak256(uint256(4) . uint256(1))) + 1``. +.. _bytes-and-string: + +``bytes`` and ``string`` +------------------------ + +``bytes`` and ``string`` are encoded identically. For short byte arrays, they store their data in the same +slot where the length is also stored. In particular: if the data is at most ``31`` bytes long, it is stored +in the higher-order bytes (left aligned) and the lowest-order byte stores ``length * 2``. +For byte arrays that store data which is ``32`` or more bytes long, the main slot stores ``length * 2 + 1`` and the data is +stored as usual in ``keccak256(slot)``. This means that you can distinguish a short array from a long array +by checking if the lowest bit is set: short (not set) and long (set). + +.. note:: + Handling invalidly encoded slots is currently not supported but may be added in the future. + .. index: memory layout **************** Layout in Memory **************** -Solidity reserves four 32 byte slots: +Solidity reserves four 32-byte slots, with specific byte ranges (inclusive of endpoints) being used as follows: -- ``0x00`` - ``0x3f``: scratch space for hashing methods -- ``0x40`` - ``0x5f``: currently allocated memory size (aka. free memory pointer) -- ``0x60`` - ``0x7f``: zero slot +- ``0x00`` - ``0x3f`` (64 bytes): scratch space for hashing methods +- ``0x40`` - ``0x5f`` (32 bytes): currently allocated memory size (aka. free memory pointer) +- ``0x60`` - ``0x7f`` (32 bytes): zero slot -Scratch space can be used between statements (ie. within inline assembly). The zero slot +Scratch space can be used between statements (i.e. within inline assembly). The zero slot is used as initial value for dynamic memory arrays and should never be written to (the free memory pointer points to ``0x80`` initially). Solidity always places new objects at the free memory pointer and memory is never freed (this might change in the future). .. warning:: - There are some operations in Solidity that need a temporary memory area larger than 64 bytes and therefore will not fit into the scratch space. They will be placed where the free memory points to, but given their short lifecycle, the pointer is not updated. The memory may or may not be zeroed out. Because of this, one shouldn't expect the free memory to be zeroed out. + There are some operations in Solidity that need a temporary memory area larger than 64 bytes and therefore will not fit into the scratch space. They will be placed where the free memory points to, but given their short lifetime, the pointer is not updated. The memory may or may not be zeroed out. Because of this, one shouldn't expect the free memory to point to zeroed out memory. + While it may seem like a good idea to use ``msize`` to arrive at a definitely zeroed out memory area, using such a pointer non-temporarily without updating the free memory pointer can have adverse results. .. index: calldata layout @@ -86,10 +112,14 @@ Solidity always places new objects at the free memory pointer and memory is neve Layout of Call Data ******************* -When a Solidity contract is deployed and when it is called from an -account, the input data is assumed to be in the format in :ref:`the ABI -specification <ABI>`. The ABI specification requires arguments to be padded to multiples of 32 -bytes. The internal function calls use a different convention. +The input data for a function call is assumed to be in the format defined by the :ref:`ABI +specification <ABI>`. Among others, the ABI specification requires arguments to be padded to multiples of 32 +bytes. The internal function calls use a different convention. + +Arguments for the constructor of a contract are directly appended at the end of the +contract's code, also in ABI encoding. The constructor will access them through a hard-coded offset, and +not by using the ``codesize`` opcode, since this of course changes when appending +data to the code. .. index: variable cleanup @@ -143,35 +173,34 @@ Different types have different rules for cleaning up invalid values: .. index:: optimizer, common subexpression elimination, constant propagation ************************* -Internals - The Optimizer +Internals - The Optimiser ************************* -The Solidity optimizer operates on assembly, so it can be and also is used by other languages. It splits the sequence of instructions into basic blocks at ``JUMPs`` and ``JUMPDESTs``. Inside these blocks, the instructions are analysed and every modification to the stack, to memory or storage is recorded as an expression which consists of an instruction and a list of arguments which are essentially pointers to other expressions. The main idea is now to find expressions that are always equal (on every input) and combine them into an expression class. The optimizer first tries to find each new expression in a list of already known expressions. If this does not work, the expression is simplified according to rules like ``constant + constant = sum_of_constants`` or ``X * 1 = X``. Since this is done recursively, we can also apply the latter rule if the second factor is a more complex expression where we know that it will always evaluate to one. Modifications to storage and memory locations have to erase knowledge about storage and memory locations which are not known to be different: If we first write to location x and then to location y and both are input variables, the second could overwrite the first, so we actually do not know what is stored at x after we wrote to y. On the other hand, if a simplification of the expression x - y evaluates to a non-zero constant, we know that we can keep our knowledge about what is stored at x. +The Solidity optimiser operates on assembly so that other languages can use it. It splits the sequence of instructions into basic blocks at ``JUMPs`` and ``JUMPDESTs``. Inside these blocks, the optimiser analyses the instructions and records every modification to the stack, memory, or storage as an expression which consists of an instruction and a list of arguments which are pointers to other expressions. The optimiser uses a component called "CommonSubexpressionEliminator" that amongst other tasks, finds expressions that are always equal (on every input) and combines them into an expression class. The optimiser first tries to find each new expression in a list of already known expressions. If this does not work, it simplifies the expression according to rules like ``constant + constant = sum_of_constants`` or ``X * 1 = X``. Since this is a recursive process, we can also apply the latter rule if the second factor is a more complex expression where we know that it always evaluates to one. Modifications to storage and memory locations have to erase knowledge about storage and memory locations which are not known to be different. If we first write to location x and then to location y and both are input variables, the second could overwrite the first, so we do not know what is stored at x after we wrote to y. If simplification of the expression x - y evaluates to a non-zero constant, we know that we can keep our knowledge about what is stored at x. -At the end of this process, we know which expressions have to be on the stack in the end and have a list of modifications to memory and storage. This information is stored together with the basic blocks and is used to link them. Furthermore, knowledge about the stack, storage and memory configuration is forwarded to the next block(s). If we know the targets of all ``JUMP`` and ``JUMPI`` instructions, we can build a complete control flow graph of the program. If there is only one target we do not know (this can happen as in principle, jump targets can be computed from inputs), we have to erase all knowledge about the input state of a block as it can be the target of the unknown ``JUMP``. If a ``JUMPI`` is found whose condition evaluates to a constant, it is transformed to an unconditional jump. +After this process, we know which expressions have to be on the stack at the end, and have a list of modifications to memory and storage. This information is stored together with the basic blocks and is used to link them. Furthermore, knowledge about the stack, storage and memory configuration is forwarded to the next block(s). If we know the targets of all ``JUMP`` and ``JUMPI`` instructions, we can build a complete control flow graph of the program. If there is only one target we do not know (this can happen as in principle, jump targets can be computed from inputs), we have to erase all knowledge about the input state of a block as it can be the target of the unknown ``JUMP``. If the optimiser finds a ``JUMPI`` whose condition evaluates to a constant, it transforms it to an unconditional jump. -As the last step, the code in each block is completely re-generated. A dependency graph is created from the expressions on the stack at the end of the block and every operation that is not part of this graph is essentially dropped. Now code is generated that applies the modifications to memory and storage in the order they were made in the original code (dropping modifications which were found not to be needed) and finally, generates all values that are required to be on the stack in the correct place. +As the last step, the code in each block is re-generated. The optimiser creates a dependency graph from the expressions on the stack at the end of the block, and it drops every operation that is not part of this graph. It generates code that applies the modifications to memory and storage in the order they were made in the original code (dropping modifications which were found not to be needed). Finally, it generates all values that are required to be on the stack in the correct place. -These steps are applied to each basic block and the newly generated code is used as replacement if it is smaller. If a basic block is split at a ``JUMPI`` and during the analysis, the condition evaluates to a constant, the ``JUMPI`` is replaced depending on the value of the constant, and thus code like +These steps are applied to each basic block and the newly generated code is used as replacement if it is smaller. If a basic block is split at a ``JUMPI`` and during the analysis, the condition evaluates to a constant, the ``JUMPI`` is replaced depending on the value of the constant. Thus code like :: - var x = 7; + uint x = 7; data[7] = 9; if (data[x] != x + 2) return 2; else return 1; -is simplified to code which can also be compiled from +still simplifies to code which you can compile even though the instructions contained +a jump in the beginning of the process: :: data[7] = 9; return 1; -even though the instructions contained a jump in the beginning. - .. index:: source mappings *************** @@ -190,7 +219,7 @@ important for static analysis tools that operate on bytecode level and for displaying the current position in the source code inside a debugger or for breakpoint handling. -Both kinds of source mappings use integer indentifiers to refer to source files. +Both kinds of source mappings use integer identifiers to refer to source files. These are regular array indices into a list of source files usually called ``"sourceList"``, which is part of the combined-json and the output of the json / npm compiler. @@ -237,7 +266,6 @@ Tips and Tricks * Use shorter types for struct elements and sort them such that short types are grouped together. This can lower the gas costs as multiple ``SSTORE`` operations might be combined into a single (``SSTORE`` costs 5000 or 20000 gas, so this is what you want to optimise). Use the gas price estimator (with optimiser enabled) to check! * Make your state variables public - the compiler will create :ref:`getters <visibility-and-getters>` for you automatically. * If you end up checking conditions on input or state a lot at the beginning of your functions, try using :ref:`modifiers`. -* If your contract has a function called ``send`` but you want to use the built-in send-function, use ``address(contractVariable).send(amount)``. * Initialize storage structs with a single assignment: ``x = MyStruct({a: 1, b: 2});`` .. note:: @@ -273,7 +301,7 @@ The following is the order of precedence for operators, listed in order of evalu +------------+-------------------------------------+--------------------------------------------+ | *2* | Prefix increment and decrement | ``++``, ``--`` | + +-------------------------------------+--------------------------------------------+ -| | Unary plus and minus | ``+``, ``-`` | +| | Unary minus | ``-`` | + +-------------------------------------+--------------------------------------------+ | | Unary operations | ``delete`` | + +-------------------------------------+--------------------------------------------+ @@ -317,45 +345,42 @@ The following is the order of precedence for operators, listed in order of evalu Global Variables ================ -- ``abi.encode(...) returns (bytes)``: :ref:`ABI <ABI>`-encodes the given arguments -- ``abi.encodePacked(...) returns (bytes)``: Performes :ref:`packed encoding <abi_packed_mode>` of the given arguments -- ``abi.encodeWithSelector(bytes4 selector, ...) returns (bytes)``: :ref:`ABI <ABI>`-encodes the given arguments +- ``abi.decode(bytes memory encodedData, (...)) returns (...)``: :ref:`ABI <ABI>`-decodes the provided data. The types are given in parentheses as second argument. Example: ``(uint a, uint[2] memory b, bytes memory c) = abi.decode(data, (uint, uint[2], bytes))`` +- ``abi.encode(...) returns (bytes memory)``: :ref:`ABI <ABI>`-encodes the given arguments +- ``abi.encodePacked(...) returns (bytes memory)``: Performs :ref:`packed encoding <abi_packed_mode>` of the given arguments +- ``abi.encodeWithSelector(bytes4 selector, ...) returns (bytes memory)``: :ref:`ABI <ABI>`-encodes the given arguments starting from the second and prepends the given four-byte selector -- ``abi.encodeWithSignature(string signature, ...) returns (bytes)``: Equivalent to ``abi.encodeWithSelector(bytes4(keccak256(signature), ...)``` -- ``block.blockhash(uint blockNumber) returns (bytes32)``: hash of the given block - only works for 256 most recent, excluding current, blocks - deprecated in version 0.4.22 and replaced by ``blockhash(uint blockNumber)``. -- ``block.coinbase`` (``address``): current block miner's address +- ``abi.encodeWithSignature(string memory signature, ...) returns (bytes memory)``: Equivalent to ``abi.encodeWithSelector(bytes4(keccak256(bytes(signature)), ...)``` +- ``block.coinbase`` (``address payable``): current block miner's address - ``block.difficulty`` (``uint``): current block difficulty - ``block.gaslimit`` (``uint``): current block gaslimit - ``block.number`` (``uint``): current block number - ``block.timestamp`` (``uint``): current block timestamp - ``gasleft() returns (uint256)``: remaining gas - ``msg.data`` (``bytes``): complete calldata -- ``msg.gas`` (``uint``): remaining gas - deprecated in version 0.4.21 and to be replaced by ``gasleft()`` -- ``msg.sender`` (``address``): sender of the message (current call) +- ``msg.sender`` (``address payable``): sender of the message (current call) - ``msg.value`` (``uint``): number of wei sent with the message - ``now`` (``uint``): current block timestamp (alias for ``block.timestamp``) - ``tx.gasprice`` (``uint``): gas price of the transaction -- ``tx.origin`` (``address``): sender of the transaction (full call chain) +- ``tx.origin`` (``address payable``): sender of the transaction (full call chain) - ``assert(bool condition)``: abort execution and revert state changes if condition is ``false`` (use for internal error) - ``require(bool condition)``: abort execution and revert state changes if condition is ``false`` (use for malformed input or error in external component) -- ``require(bool condition, string message)``: abort execution and revert state changes if condition is ``false`` (use for malformed input or error in external component). Also provide error message. +- ``require(bool condition, string memory message)``: abort execution and revert state changes if condition is ``false`` (use for malformed input or error in external component). Also provide error message. - ``revert()``: abort execution and revert state changes -- ``revert(string message)``: abort execution and revert state changes providing an explanatory string +- ``revert(string memory message)``: abort execution and revert state changes providing an explanatory string - ``blockhash(uint blockNumber) returns (bytes32)``: hash of the given block - only works for 256 most recent blocks -- ``keccak256(...) returns (bytes32)``: compute the Ethereum-SHA-3 (Keccak-256) hash of the :ref:`(tightly packed) arguments <abi_packed_mode>` -- ``sha3(...) returns (bytes32)``: an alias to ``keccak256`` -- ``sha256(...) returns (bytes32)``: compute the SHA-256 hash of the :ref:`(tightly packed) arguments <abi_packed_mode>` -- ``ripemd160(...) returns (bytes20)``: compute the RIPEMD-160 hash of the :ref:`(tightly packed) arguments <abi_packed_mode>` +- ``keccak256(bytes memory) returns (bytes32)``: compute the Keccak-256 hash of the input +- ``sha256(bytes memory) returns (bytes32)``: compute the SHA-256 hash of the input +- ``ripemd160(bytes memory) returns (bytes20)``: compute the RIPEMD-160 hash of the input - ``ecrecover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) returns (address)``: recover address associated with the public key from elliptic curve signature, return zero on error - ``addmod(uint x, uint y, uint k) returns (uint)``: compute ``(x + y) % k`` where the addition is performed with arbitrary precision and does not wrap around at ``2**256``. Assert that ``k != 0`` starting from version 0.5.0. - ``mulmod(uint x, uint y, uint k) returns (uint)``: compute ``(x * y) % k`` where the multiplication is performed with arbitrary precision and does not wrap around at ``2**256``. Assert that ``k != 0`` starting from version 0.5.0. -- ``this`` (current contract's type): the current contract, explicitly convertible to ``address`` +- ``this`` (current contract's type): the current contract, explicitly convertible to ``address`` or ``address payable`` - ``super``: the contract one level higher in the inheritance hierarchy -- ``selfdestruct(address recipient)``: destroy the current contract, sending its funds to the given address -- ``suicide(address recipient)``: a deprecated alias to ``selfdestruct`` +- ``selfdestruct(address payable recipient)``: destroy the current contract, sending its funds to the given address - ``<address>.balance`` (``uint256``): balance of the :ref:`address` in Wei -- ``<address>.send(uint256 amount) returns (bool)``: send given amount of Wei to :ref:`address`, returns ``false`` on failure -- ``<address>.transfer(uint256 amount)``: send given amount of Wei to :ref:`address`, throws on failure +- ``<address payable>.send(uint256 amount) returns (bool)``: send given amount of Wei to :ref:`address`, returns ``false`` on failure +- ``<address payable>.transfer(uint256 amount)``: send given amount of Wei to :ref:`address`, throws on failure .. note:: Do not rely on ``block.timestamp``, ``now`` and ``blockhash`` as a source of randomness, @@ -374,6 +399,11 @@ Global Variables You can only access the hashes of the most recent 256 blocks, all other values will be zero. +.. note:: + In version 0.5.0, the following aliases were removed: ``suicide`` as alias for ``selfdestruct``, + ``msg.gas`` as alias for ``gasleft``, ``block.blockhash`` as alias for ``blockhash`` and + ``sha3`` as alias for ``keccak256``. + .. index:: visibility, public, private, external, internal Function Visibility Specifiers @@ -396,11 +426,10 @@ Function Visibility Specifiers Modifiers ========= -- ``pure`` for functions: Disallows modification or access of state - this is not enforced yet. -- ``view`` for functions: Disallows modification of state - this is not enforced yet. +- ``pure`` for functions: Disallows modification or access of state. +- ``view`` for functions: Disallows modification of state. - ``payable`` for functions: Allows them to receive Ether together with a call. - ``constant`` for state variables: Disallows assignment (except initialisation), does not occupy storage slot. -- ``constant`` for functions: Same as ``view``. - ``anonymous`` for events: Does not store event signature as topic. - ``indexed`` for event parameters: Stores the parameter as topic. @@ -409,8 +438,11 @@ Reserved Keywords These keywords are reserved in Solidity. They might become part of the syntax in the future: -``abstract``, ``after``, ``case``, ``catch``, ``default``, ``final``, ``in``, ``inline``, ``let``, ``match``, ``null``, -``of``, ``relocatable``, ``static``, ``switch``, ``try``, ``type``, ``typeof``. +``abstract``, ``after``, ``alias``, ``apply``, ``auto``, ``case``, ``catch``, ``copyof``, ``default``, +``define``, ``final``, ``immutable``, ``implements``, ``in``, ``inline``, ``let``, ``macro``, ``match``, +``mutable``, ``null``, ``of``, ``override``, ``partial``, ``promise``, ``reference``, ``relocatable``, +``sealed``, ``sizeof``, ``static``, ``supports``, ``switch``, ``try``, ``type``, ``typedef``, ``typeof``, +``unchecked``. Language Grammar ================ diff --git a/docs/requirements.txt b/docs/requirements.txt index 0607b1ef..5fba631c 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -1 +1,2 @@ sphinx_rtd_theme>=0.3.1 +pygments-lexer-solidity>=0.3.1 diff --git a/docs/resources.rst b/docs/resources.rst new file mode 100644 index 00000000..e9e65692 --- /dev/null +++ b/docs/resources.rst @@ -0,0 +1,132 @@ +Resources +--------- + +General +~~~~~~~ + +* `Ethereum <https://ethereum.org>`_ + +* `Changelog <https://github.com/ethereum/solidity/blob/develop/Changelog.md>`_ + +* `Source Code <https://github.com/ethereum/solidity/>`_ + +* `Ethereum Stackexchange <https://ethereum.stackexchange.com/>`_ + +* `Language Users Chat <https://gitter.im/ethereum/solidity/>`_ + +* `Compiler Developers Chat <https://gitter.im/ethereum/solidity-dev/>`_ + +Solidity Integrations +~~~~~~~~~~~~~~~~~~~~~ + +* Generic: + + + * `EthFiddle <https://ethfiddle.com/>`_ + Solidity IDE in the Browser. Write and share your solidity code. Uses server-side components. + + * `Remix <https://remix.ethereum.org/>`_ + Browser-based IDE with integrated compiler and Solidity runtime environment without server-side components. + + * `Solium <https://github.com/duaraghav8/Solium/>`_ + Linter to identify and fix style and security issues in Solidity. + + * `Solhint <https://github.com/protofire/solhint>`_ + Solidity linter that provides security, style guide and best practice rules for smart contract validation. + +* Atom: + + * `Etheratom <https://github.com/0mkara/etheratom>`_ + Plugin for the Atom editor that features syntax highlighting, compilation and a runtime environment (Backend node & VM compatible). + + * `Atom Solidity Linter <https://atom.io/packages/linter-solidity>`_ + Plugin for the Atom editor that provides Solidity linting. + + * `Atom Solium Linter <https://atom.io/packages/linter-solium>`_ + Configurable Solidty linter for Atom using Solium as a base. + +* Eclipse: + + * `YAKINDU Solidity Tools <https://yakindu.github.io/solidity-ide/>`_ + Eclipse based IDE. Features context sensitive code completion and help, code navigation, syntax coloring, built in compiler, quick fixes and templates. + +* Emacs: + + * `Emacs Solidity <https://github.com/ethereum/emacs-solidity/>`_ + Plugin for the Emacs editor providing syntax highlighting and compilation error reporting. + +* IntelliJ: + + * `IntelliJ IDEA plugin <https://plugins.jetbrains.com/plugin/9475-intellij-solidity>`_ + Solidity plugin for IntelliJ IDEA (and all other JetBrains IDEs) + +* Sublime: + + * `Package for SublimeText - Solidity language syntax <https://packagecontrol.io/packages/Ethereum/>`_ + Solidity syntax highlighting for SublimeText editor. + +* Vim: + + * `Vim Solidity <https://github.com/tomlion/vim-solidity/>`_ + Plugin for the Vim editor providing syntax highlighting. + + * `Vim Syntastic <https://github.com/scrooloose/syntastic>`_ + Plugin for the Vim editor providing compile checking. + +* Visual Studio Code: + + * `Visual Studio Code extension <http://juan.blanco.ws/solidity-contracts-in-visual-studio-code/>`_ + Solidity plugin for Microsoft Visual Studio Code that includes syntax highlighting and the Solidity compiler. + +Discontinued: + +* `Mix IDE <https://github.com/ethereum/mix/>`_ + Qt based IDE for designing, debugging and testing solidity smart contracts. + +* `Ethereum Studio <https://live.ether.camp/>`_ + Specialized web IDE that also provides shell access to a complete Ethereum environment. + +* `Visual Studio Extension <https://visualstudiogallery.msdn.microsoft.com/96221853-33c4-4531-bdd5-d2ea5acc4799/>`_ + Solidity plugin for Microsoft Visual Studio that includes the Solidity compiler. + +Solidity Tools +~~~~~~~~~~~~~~ + +* `Dapp <https://dapp.tools/dapp/>`_ + Build tool, package manager, and deployment assistant for Solidity. + +* `Solidity REPL <https://github.com/raineorshine/solidity-repl>`_ + Try Solidity instantly with a command-line Solidity console. + +* `solgraph <https://github.com/raineorshine/solgraph>`_ + Visualize Solidity control flow and highlight potential security vulnerabilities. + +* `Doxity <https://github.com/DigixGlobal/doxity>`_ + Documentation Generator for Solidity. + +* `evmdis <https://github.com/Arachnid/evmdis>`_ + EVM Disassembler that performs static analysis on the bytecode to provide a higher level of abstraction than raw EVM operations. + +* `ABI to solidity interface converter <https://gist.github.com/chriseth/8f533d133fa0c15b0d6eaf3ec502c82b>`_ + A script for generating contract interfaces from the ABI of a smart contract. + +* `Securify <https://securify.ch/>`_ + Fully automated online static analyzer for smart contracts, providing a security report based on vulnerability patterns. + +* `Sūrya <https://github.com/ConsenSys/surya/>`_ + Utility tool for smart contract systems, offering a number of visual outputs and information about the contracts' structure. Also supports querying the function call graph. + +* `EVM Lab <https://github.com/ethereum/evmlab/>`_ + Rich tool package to interact with the EVM. Includes a VM, Etherchain API, and a trace-viewer with gas cost display. + +.. note:: + Information like variable names, comments, and source code formatting is lost in the compilation process and it is not possible to completely recover the original source code. Decompiling smart contracts to view the original source code might not be possible, or the end result that useful. + +Third-Party Solidity Parsers and Grammars +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +* `solidity-parser <https://github.com/ConsenSys/solidity-parser>`_ + Solidity parser for JavaScript + +* `Solidity Grammar for ANTLR 4 <https://github.com/federicobond/solidity-antlr4>`_ + Solidity grammar for the ANTLR 4 parser generator diff --git a/docs/security-considerations.rst b/docs/security-considerations.rst index 4133edb1..bd06276b 100644 --- a/docs/security-considerations.rst +++ b/docs/security-considerations.rst @@ -55,7 +55,7 @@ complete contract): :: - pragma solidity ^0.4.0; + pragma solidity >=0.4.0 <0.6.0; // THIS CONTRACT CONTAINS A BUG - DO NOT USE contract Fund { @@ -78,7 +78,7 @@ as it uses ``call`` which forwards all remaining gas by default: :: - pragma solidity ^0.4.0; + pragma solidity >=0.4.0 <0.6.0; // THIS CONTRACT CONTAINS A BUG - DO NOT USE contract Fund { @@ -86,7 +86,8 @@ as it uses ``call`` which forwards all remaining gas by default: mapping(address => uint) shares; /// Withdraw your share. function withdraw() public { - if (msg.sender.call.value(shares[msg.sender])()) + (bool success,) = msg.sender.call.value(shares[msg.sender])(""); + if (success) shares[msg.sender] = 0; } } @@ -96,14 +97,14 @@ outlined further below: :: - pragma solidity ^0.4.11; + pragma solidity >=0.4.11 <0.6.0; contract Fund { /// Mapping of ether shares of the contract. mapping(address => uint) shares; /// Withdraw your share. function withdraw() public { - var share = shares[msg.sender]; + uint share = shares[msg.sender]; shares[msg.sender] = 0; msg.sender.transfer(share); } @@ -135,15 +136,16 @@ Sending and Receiving Ether - If a contract receives Ether (without a function being called), the fallback function is executed. If it does not have a fallback function, the Ether will be rejected (by throwing an exception). During the execution of the fallback function, the contract can only rely - on the "gas stipend" (2300 gas) being available to it at that time. This stipend is not enough to access storage in any way. + on the "gas stipend" it is passed (2300 gas) being available to it at that time. This stipend is not enough to modify storage + (do not take this for granted though, the stipend might change with future hard forks). To be sure that your contract can receive Ether in that way, check the gas requirements of the fallback function (for example in the "details" section in Remix). - There is a way to forward more gas to the receiving contract using - ``addr.call.value(x)()``. This is essentially the same as ``addr.transfer(x)``, + ``addr.call.value(x)("")``. This is essentially the same as ``addr.transfer(x)``, only that it forwards all remaining gas and opens up the ability for the - recipient to perform more expensive actions (and it only returns a failure code - and does not automatically propagate the error). This might include calling back + recipient to perform more expensive actions (and it returns a failure code + instead of automatically propagating the error). This might include calling back into the sending contract or other state changes you might not have thought of. So it allows for great flexibility for honest users but also for malicious actors. @@ -171,7 +173,8 @@ before they interact with your contract. Note that ``.send()`` does **not** throw an exception if the call stack is depleted but rather returns ``false`` in that case. The low-level functions -``.call()``, ``.callcode()`` and ``.delegatecall()`` behave in the same way. +``.call()``, ``.callcode()``, ``.delegatecall()`` and ``.staticcall()`` behave +in the same way. tx.origin ========= @@ -180,17 +183,17 @@ Never use tx.origin for authorization. Let's say you have a wallet contract like :: - pragma solidity ^0.4.11; + pragma solidity >0.4.99 <0.6.0; // THIS CONTRACT CONTAINS A BUG - DO NOT USE contract TxUserWallet { address owner; - function TxUserWallet() public { + constructor() public { owner = msg.sender; } - function transferTo(address dest, uint amount) public { + function transferTo(address payable dest, uint amount) public { require(tx.origin == owner); dest.transfer(amount); } @@ -200,20 +203,20 @@ Now someone tricks you into sending ether to the address of this attack wallet: :: - pragma solidity ^0.4.11; + pragma solidity >0.4.99 <0.6.0; interface TxUserWallet { - function transferTo(address dest, uint amount) public; + function transferTo(address payable dest, uint amount) external; } contract TxAttackWallet { - address owner; + address payable owner; - function TxAttackWallet() public { + constructor() public { owner = msg.sender; } - function() public { + function() external { TxUserWallet(msg.sender).transferTo(owner, msg.sender.balance); } } @@ -221,10 +224,29 @@ Now someone tricks you into sending ether to the address of this attack wallet: If your wallet had checked ``msg.sender`` for authorization, it would get the address of the attack wallet, instead of the owner address. But by checking ``tx.origin``, it gets the original address that kicked off the transaction, which is still the owner address. The attack wallet instantly drains all your funds. + +Two's Complement / Underflows / Overflows +========================================= + +As in many programming languages, Solidity's integer types are not actually integers. +They resemble integers when the values are small, but behave differently if the numbers are larger. +For example, the following is true: ``uint8(255) + uint8(1) == 0``. This situation is called +an *overflow*. It occurs when an operation is performed that requires a fixed size variable +to store a number (or piece of data) that is outside the range of the variable's data type. +An *underflow* is the converse situation: ``uint8(0) - uint8(1) == 255``. + +In general, read about the limits of two's complement representation, which even has some +more special edge cases for signed numbers. + +Try to use ``require`` to limit the size of inputs to a reasonable range and use the +:ref:`SMT checker<smt_checker>` to find potential overflows, or +use a library like +`SafeMath<https://github.com/OpenZeppelin/openzeppelin-solidity/blob/master/contracts/math/SafeMath.sol>` +if you want all overflows to cause a revert. + Minor Details ============= -- In ``for (var i = 0; i < arrayName.length; i++) { ... }``, the type of ``i`` will be ``uint8``, because this is the smallest type that is required to hold the value ``0``. If the array has more than 255 elements, the loop will not terminate. - Types that do not occupy the full 32 bytes might contain "dirty higher order bits". This is especially important if you access ``msg.data`` - it poses a malleability risk: You can craft transactions that call a function ``f(uint8 x)`` with a raw byte argument @@ -245,12 +267,8 @@ implications, there might be another issue buried beneath it. Any compiler warning we issue can be silenced by slight changes to the code. -Also try to enable the "0.5.0" safety features as early as possible -by adding ``pragma experimental "v0.5.0";``. Note that in this case, -the word ``experimental`` does not mean that the safety features are in any -way risky, it is just a way to enable some features that are -not yet part of the latest version of Solidity due to backwards -compatibility. +Always use the latest version of the compiler to be notified about all recently +introduced warnings. Restrict the Amount of Ether ============================ @@ -304,6 +322,12 @@ of "failsafe" mode, which, for example, disables most of the features, hands ove control to a fixed and trusted third party or just converts the contract into a simple "give me back my money" contract. +Ask for Peer Review +=================== + +The more people examine a piece of code, the more issues are found. +Asking people to review your code also helps as a cross-check to find out whether your code +is easy to understand - a very important criterion for good smart contracts. ******************* Formal Verification diff --git a/docs/solidity-by-example.rst b/docs/solidity-by-example.rst index 2b3d4b48..752e8641 100644 --- a/docs/solidity-by-example.rst +++ b/docs/solidity-by-example.rst @@ -36,7 +36,7 @@ of votes. :: - pragma solidity ^0.4.22; + pragma solidity >=0.4.22 <0.6.0; /// @title Voting with delegation. contract Ballot { @@ -66,7 +66,7 @@ of votes. Proposal[] public proposals; /// Create a new ballot to choose one of `proposalNames`. - constructor(bytes32[] proposalNames) public { + constructor(bytes32[] memory proposalNames) public { chairperson = msg.sender; voters[chairperson].weight = 1; @@ -90,7 +90,7 @@ of votes. // If the first argument of `require` evaluates // to `false`, execution terminates and all // changes to the state and to Ether balances - // are reverted. + // are reverted. // This used to consume all gas in old EVM versions, but // not anymore. // It is often a good idea to use `require` to check if @@ -152,6 +152,7 @@ of votes. /// to proposal `proposals[proposal].name`. function vote(uint proposal) public { Voter storage sender = voters[msg.sender]; + require(sender.weight != 0, "Has no right to vote"); require(!sender.voted, "Already voted."); sender.voted = true; sender.vote = proposal; @@ -220,19 +221,19 @@ bid. If the highest bid is raised, the previously highest bidder gets her money back. After the end of the bidding period, the contract has to be called manually for the -beneficiary to receive his money - contracts cannot +beneficiary to receive their money - contracts cannot activate themselves. :: - pragma solidity ^0.4.22; + pragma solidity >=0.4.22 <0.6.0; contract SimpleAuction { // Parameters of the auction. Times are either // absolute unix timestamps (seconds since 1970-01-01) // or time periods in seconds. - address public beneficiary; - uint public auctionEnd; + address payable public beneficiary; + uint public auctionEndTime; // Current state of the auction. address public highestBidder; @@ -241,10 +242,11 @@ activate themselves. // Allowed withdrawals of previous bids mapping(address => uint) pendingReturns; - // Set to true at the end, disallows any change + // Set to true at the end, disallows any change. + // By default initialized to `false`. bool ended; - // Events that will be fired on changes. + // Events that will be emitted on changes. event HighestBidIncreased(address bidder, uint amount); event AuctionEnded(address winner, uint amount); @@ -258,10 +260,10 @@ activate themselves. /// beneficiary address `_beneficiary`. constructor( uint _biddingTime, - address _beneficiary + address payable _beneficiary ) public { beneficiary = _beneficiary; - auctionEnd = now + _biddingTime; + auctionEndTime = now + _biddingTime; } /// Bid on the auction with the value sent @@ -278,7 +280,7 @@ activate themselves. // Revert the call if the bidding // period is over. require( - now <= auctionEnd, + now <= auctionEndTime, "Auction already ended." ); @@ -337,7 +339,7 @@ activate themselves. // external contracts. // 1. Conditions - require(now >= auctionEnd, "Auction not yet ended."); + require(now >= auctionEndTime, "Auction not yet ended."); require(!ended, "auctionEnd has already been called."); // 2. Effects @@ -372,7 +374,7 @@ is the same as the one provided during the bidding period. Another challenge is how to make the auction **binding and blind** at the same time: The only way to prevent the bidder from just not sending the money -after he won the auction is to make her send it +after they won the auction is to make her send it together with the bid. Since value transfers cannot be blinded in Ethereum, anyone can see the value. @@ -388,7 +390,7 @@ high or low invalid bids. :: - pragma solidity >0.4.23 <0.5.0; + pragma solidity >0.4.23 <0.6.0; contract BlindAuction { struct Bid { @@ -396,7 +398,7 @@ high or low invalid bids. uint deposit; } - address public beneficiary; + address payable public beneficiary; uint public biddingEnd; uint public revealEnd; bool public ended; @@ -421,15 +423,15 @@ high or low invalid bids. constructor( uint _biddingTime, uint _revealTime, - address _beneficiary + address payable _beneficiary ) public { beneficiary = _beneficiary; biddingEnd = now + _biddingTime; revealEnd = biddingEnd + _revealTime; } - /// Place a blinded bid with `_blindedBid` = keccak256(value, - /// fake, secret). + /// Place a blinded bid with `_blindedBid` = + /// keccak256(abi.encodePacked(value, fake, secret)). /// The sent ether is only refunded if the bid is correctly /// revealed in the revealing phase. The bid is valid if the /// ether sent together with the bid is at least "value" and @@ -452,9 +454,9 @@ high or low invalid bids. /// correctly blinded invalid bids and for all bids except for /// the totally highest. function reveal( - uint[] _values, - bool[] _fake, - bytes32[] _secret + uint[] memory _values, + bool[] memory _fake, + bytes32[] memory _secret ) public onlyAfter(biddingEnd) @@ -467,22 +469,22 @@ high or low invalid bids. uint refund; for (uint i = 0; i < length; i++) { - Bid storage bid = bids[msg.sender][i]; + Bid storage bidToCheck = bids[msg.sender][i]; (uint value, bool fake, bytes32 secret) = (_values[i], _fake[i], _secret[i]); - if (bid.blindedBid != keccak256(value, fake, secret)) { + if (bidToCheck.blindedBid != keccak256(abi.encodePacked(value, fake, secret))) { // Bid was not actually revealed. // Do not refund deposit. continue; } - refund += bid.deposit; - if (!fake && bid.deposit >= value) { + refund += bidToCheck.deposit; + if (!fake && bidToCheck.deposit >= value) { if (placeBid(msg.sender, value)) refund -= value; } // Make it impossible for the sender to re-claim // the same deposit. - bid.blindedBid = bytes32(0); + bidToCheck.blindedBid = bytes32(0); } msg.sender.transfer(refund); } @@ -496,7 +498,7 @@ high or low invalid bids. if (value <= highestBid) { return false; } - if (highestBidder != 0) { + if (highestBidder != address(0)) { // Refund the previously highest bidder. pendingReturns[highestBidder] += highestBid; } @@ -541,12 +543,12 @@ Safe Remote Purchase :: - pragma solidity ^0.4.22; + pragma solidity >=0.4.22 <0.6.0; contract Purchase { uint public value; - address public seller; - address public buyer; + address payable public seller; + address payable public buyer; enum State { Created, Locked, Inactive } State public state; @@ -645,4 +647,489 @@ Safe Remote Purchase Micropayment Channel ******************** -To be written. +In this section we will learn how to build a simple implementation +of a payment channel. It use cryptographics signatures to make +repeated transfers of Ether between the same parties secure, instantaneous, and +without transaction fees. To do it we need to understand how to +sign and verify signatures, and setup the payment channel. + +Creating and verifying signatures +================================= + +Imagine Alice wants to send a quantity of Ether to Bob, i.e. +Alice is the sender and the Bob is the recipient. +Alice only needs to send cryptographically signed messages off-chain +(e.g. via email) to Bob and it will be very similar to writing checks. + +Signatures are used to authorize transactions, +and they are a general tool that is available to +smart contracts. Alice will build a simple +smart contract that lets her transmit Ether, but +in a unusual way, instead of calling a function herself +to initiate a payment, she will let Bob +do that, and therefore pay the transaction fee. +The contract will work as follows: + + 1. Alice deploys the ``ReceiverPays`` contract, attaching enough Ether to cover the payments that will be made. + 2. Alice authorizes a payment by signing a message with their private key. + 3. Alice sends the cryptographically signed message to Bob. The message does not need to be kept secret + (you will understand it later), and the mechanism for sending it does not matter. + 4. Bob claims their payment by presenting the signed message to the smart contract, it verifies the + authenticity of the message and then releases the funds. + +Creating the signature +---------------------- + +Alice does not need to interact with Ethereum network to +sign the transaction, the process is completely offline. +In this tutorial, we will sign messages in the browser +using ``web3.js`` and ``MetaMask``. +In particular, we will use the standard way described in `EIP-762 <https://github.com/ethereum/EIPs/pull/712>`_, +as it provides a number of other security benefits. + +:: + + /// Hashing first makes a few things easier + var hash = web3.sha3("message to sign"); + web3.personal.sign(hash, web3.eth.defaultAccount, function () {...}); + + +Note that the ``web3.personal.sign`` prepends the length of the message to the signed data. +Since we hash first, the message will always be exactly 32 bytes long, +and thus this length prefix is always the same, making everything easier. + +What to Sign +------------ + +For a contract that fulfills payments, the signed message must include: + + 1. The recipient's address + 2. The amount to be transferred + 3. Protection against replay attacks + +A replay attack is when a signed message is reused to claim authorization for +a second action. +To avoid replay attacks we will use the same as in Ethereum transactions +themselves, a so-called nonce, which is the number of transactions sent by an +account. +The smart contract will check if a nonce is used multiple times. + +There is another type of replay attacks, it occurs when the +owner deploys a ``ReceiverPays`` smart contract, performs some payments, +and then destroy the contract. Later, she decides to deploy the +``RecipientPays`` smart contract again, but the new contract does not +know the nonces used in the previous deployment, so the attacker +can use the old messages again. + +Alice can protect against it including +the contract's address in the message, and only +messages containing contract's address itself will be accepted. +This functionality can be found in the first two lines of the ``claimPayment()`` function in the full contract +at the end of this chapter. + +Packing arguments +----------------- + +Now that we have identified what information to include in the +signed message, we are ready to put the message together, hash it, +and sign it. For simplicity, we just concatenate the data. +The +`ethereumjs-abi <https://github.com/ethereumjs/ethereumjs-abi>`_ library provides +a function called ``soliditySHA3`` that mimics the behavior +of Solidity's ``keccak256`` function applied to arguments encoded +using ``abi.encodePacked``. +Putting it all together, here is a JavaScript function that +creates the proper signature for the ``ReceiverPays`` example: + +:: + + // recipient is the address that should be paid. + // amount, in wei, specifies how much ether should be sent. + // nonce can be any unique number to prevent replay attacks + // contractAddress is used to prevent cross-contract replay attacks + function signPayment(recipient, amount, nonce, contractAddress, callback) { + var hash = "0x" + ethereumjs.ABI.soliditySHA3( + ["address", "uint256", "uint256", "address"], + [recipient, amount, nonce, contractAddress] + ).toString("hex"); + + web3.personal.sign(hash, web3.eth.defaultAccount, callback); + } + +Recovering the Message Signer in Solidity +----------------------------------------- + +In general, ECDSA signatures consist of two parameters, ``r`` and ``s``. +Signatures in Ethereum include a third parameter called ``v``, that can be used +to recover which account's private key was used to sign in the message, +the transaction's sender. Solidity provides a built-in function +`ecrecover <mathematical-and-cryptographic-functions>`_ +that accepts a message along with the ``r``, ``s`` and ``v`` parameters and +returns the address that was used to sign the message. + +Extracting the Signature Parameters +----------------------------------- + +Signatures produced by web3.js are the concatenation of ``r``, ``s`` and ``v``, +so the first step is splitting those parameters back out. It can be done on the client, +but doing it inside the smart contract means only one signature parameter +needs to be sent rather than three. +Splitting apart a byte array into component parts is a little messy. +We will use `inline assembly <assembly>`_ to do the job +in the ``splitSignature`` function (the third function in the full contract +at the end of this chapter). + +Computing the Message Hash +-------------------------- + +The smart contract needs to know exactly what parameters were signed, +and so it must recreate the message from the parameters and use that +for signature verification. The functions ``prefixed`` and +``recoverSigner`` do this and their use can be found in the +``claimPayment`` function. + + +The full contract +----------------- + +:: + + pragma solidity >=0.4.24 <0.6.0; + + contract ReceiverPays { + address owner = msg.sender; + + mapping(uint256 => bool) usedNonces; + + constructor() public payable {} + + function claimPayment(uint256 amount, uint256 nonce, bytes memory signature) public { + require(!usedNonces[nonce]); + usedNonces[nonce] = true; + + // this recreates the message that was signed on the client + bytes32 message = prefixed(keccak256(abi.encodePacked(msg.sender, amount, nonce, this))); + + require(recoverSigner(message, signature) == owner); + + msg.sender.transfer(amount); + } + + /// destroy the contract and reclaim the leftover funds. + function kill() public { + require(msg.sender == owner); + selfdestruct(msg.sender); + } + + /// signature methods. + function splitSignature(bytes memory sig) + internal + pure + returns (uint8 v, bytes32 r, bytes32 s) + { + require(sig.length == 65); + + assembly { + // first 32 bytes, after the length prefix. + r := mload(add(sig, 32)) + // second 32 bytes. + s := mload(add(sig, 64)) + // final byte (first byte of the next 32 bytes). + v := byte(0, mload(add(sig, 96))) + } + + return (v, r, s); + } + + function recoverSigner(bytes32 message, bytes memory sig) + internal + pure + returns (address) + { + (uint8 v, bytes32 r, bytes32 s) = splitSignature(sig); + + return ecrecover(message, v, r, s); + } + + /// builds a prefixed hash to mimic the behavior of eth_sign. + function prefixed(bytes32 hash) internal pure returns (bytes32) { + return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", hash)); + } + } + + +Writing a Simple Payment Channel +================================ + +Alice will now build a simple but complete implementation of a payment channel. +Payment channels use cryptographic signatures to make repeated transfers +of Ether securely, instantaneously, and without transaction fees. + +What is a Payment Channel? +-------------------------- + +Payment channels allow participants to make repeated transfers of Ether without +using transactions. This means that the delays and fees associated with transactions +can be avoided. We are going to explore a simple unidirectional payment channel between +two parties (Alice and Bob). Using it involves three steps: + + 1. Alice funds a smart contract with Ether. This "opens" the payment channel. + 2. Alice signs messages that specify how much of that Ether is owed to the recipient. This step is repeated for each payment. + 3. Bob "closes" the payment channel, withdrawing their portion of the Ether and sending the remainder back to the sender. + +Note that only steps 1 and 3 require Ethereum transactions, step 2 means that +the sender transmits a cryptographically signed message to the recipient via off chain ways (e.g. email). +This means only two transactions are required to support any number of transfers. + +Bob is guaranteed to receive their funds because the smart contract escrows +the Ether and honors a valid signed message. The smart contract also enforces a timeout, +so Alice is guaranteed to eventually recover their funds even if the recipient refuses +to close the channel. +It is up to the participants in a payment channel to decide how long to keep it open. +For a short-lived transaction, such as paying an internet cafe for each minute of network access, +or for a longer relationship, such as paying an employee an hourly wage, a payment could last for months or years. + +Opening the Payment Channel +--------------------------- + +To open the payment channel, Alice deploys the smart contract, +attaching the Ether to be escrowed and specifying the intendend recipient +and a maximum duration for the channel to exist. It is the function +``SimplePaymentChannel`` in the contract, that is at the end of this chapter. + +Making Payments +--------------- + +Alice makes payments by sending signed messages to Bob. +This step is performed entirely outside of the Ethereum network. +Messages are cryptographically signed by the sender and then transmitted directly to the recipient. + +Each message includes the following information: + + * The smart contract's address, used to prevent cross-contract replay attacks. + * The total amount of Ether that is owed the recipient so far. + +A payment channel is closed just once, at the end of a series of transfers. +Because of this, only one of the messages sent will be redeemed. This is why +each message specifies a cumulative total amount of Ether owed, rather than the +amount of the individual micropayment. The recipient will naturally choose to +redeem the most recent message because that is the one with the highest total. +The nonce per-message is not needed anymore, because the smart contract will +only honor a single message. The address of the smart contract is still used +to prevent a message intended for one payment channel from being used for a different channel. + +Here is the modified javascript code to cryptographically sign a message from the previous chapter: + +:: + + function constructPaymentMessage(contractAddress, amount) { + return ethereumjs.ABI.soliditySHA3( + ["address", "uint256"], + [contractAddress, amount] + ); + } + + function signMessage(message, callback) { + web3.personal.sign( + "0x" + message.toString("hex"), + web3.eth.defaultAccount, + callback + ); + } + + // contractAddress is used to prevent cross-contract replay attacks. + // amount, in wei, specifies how much Ether should be sent. + + function signPayment(contractAddress, amount, callback) { + var message = constructPaymentMessage(contractAddress, amount); + signMessage(message, callback); + } + + +Closing the Payment Channel +--------------------------- + +When Bob is ready to receive their funds, it is time to +close the payment channel by calling a ``close`` function on the smart contract. +Closing the channel pays the recipient the Ether they are owed and destroys the contract, +sending any remaining Ether back to Alice. +To close the channel, Bob needs to provide a message signed by Alice. + +The smart contract must verify that the message contains a valid signature from the sender. +The process for doing this verification is the same as the process the recipient uses. +The Solidity functions ``isValidSignature`` and ``recoverSigner`` work just like their +JavaScript counterparts in the previous section. The latter is borrowed from the +``ReceiverPays`` contract in the previous chapter. + +The ``close`` function can only be called by the payment channel recipient, +who will naturally pass the most recent payment message because that message +carries the highest total owed. If the sender were allowed to call this function, +they could provide a message with a lower amount and cheat the recipient out of what they are owed. + +The function verifies the signed message matches the given parameters. +If everything checks out, the recipient is sent their portion of the Ether, +and the sender is sent the rest via a ``selfdestruct``. +You can see the ``close`` function in the full contract. + +Channel Expiration +------------------- + +Bob can close the payment channel at any time, but if they fail to do so, +Alice needs a way to recover their escrowed funds. An *expiration* time was set +at the time of contract deployment. Once that time is reached, Alice can call +``claimTimeout`` to recover their funds. You can see the ``claimTimeout`` function in the +full contract. + +After this function is called, Bob can no longer receive any Ether, +so it is important that Bob closes the channel before the expiration is reached. + + +The full contract +----------------- + +:: + + pragma solidity >=0.4.24 <0.6.0; + + contract SimplePaymentChannel { + address payable public sender; // The account sending payments. + address payable public recipient; // The account receiving the payments. + uint256 public expiration; // Timeout in case the recipient never closes. + + constructor (address payable _recipient, uint256 duration) + public + payable + { + sender = msg.sender; + recipient = _recipient; + expiration = now + duration; + } + + function isValidSignature(uint256 amount, bytes memory signature) + internal + view + returns (bool) + { + bytes32 message = prefixed(keccak256(abi.encodePacked(this, amount))); + + // check that the signature is from the payment sender + return recoverSigner(message, signature) == sender; + } + + /// the recipient can close the channel at any time by presenting a + /// signed amount from the sender. the recipient will be sent that amount, + /// and the remainder will go back to the sender + function close(uint256 amount, bytes memory signature) public { + require(msg.sender == recipient); + require(isValidSignature(amount, signature)); + + recipient.transfer(amount); + selfdestruct(sender); + } + + /// the sender can extend the expiration at any time + function extend(uint256 newExpiration) public { + require(msg.sender == sender); + require(newExpiration > expiration); + + expiration = newExpiration; + } + + /// if the timeout is reached without the recipient closing the channel, + /// then the Ether is released back to the sender. + function claimTimeout() public { + require(now >= expiration); + selfdestruct(sender); + } + + /// All functions below this are just taken from the chapter + /// 'creating and verifying signatures' chapter. + + function splitSignature(bytes memory sig) + internal + pure + returns (uint8 v, bytes32 r, bytes32 s) + { + require(sig.length == 65); + + assembly { + // first 32 bytes, after the length prefix + r := mload(add(sig, 32)) + // second 32 bytes + s := mload(add(sig, 64)) + // final byte (first byte of the next 32 bytes) + v := byte(0, mload(add(sig, 96))) + } + + return (v, r, s); + } + + function recoverSigner(bytes32 message, bytes memory sig) + internal + pure + returns (address) + { + (uint8 v, bytes32 r, bytes32 s) = splitSignature(sig); + + return ecrecover(message, v, r, s); + } + + /// builds a prefixed hash to mimic the behavior of eth_sign. + function prefixed(bytes32 hash) internal pure returns (bytes32) { + return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", hash)); + } + } + + +Note: The function ``splitSignature`` is very simple and does not use all security checks. +A real implementation should use a more rigorously tested library, such as +openzepplin's `version <https://github.com/OpenZeppelin/openzeppelin-solidity/blob/master/contracts/ECRecovery.sol>`_ of this code. + + + +Verifying Payments +------------------ + +Unlike in our previous chapter, messages in a payment channel aren't +redeemed right away. The recipient keeps track of the latest message and +redeems it when it's time to close the payment channel. This means it's +critical that the recipient perform their own verification of each message. +Otherwise there is no guarantee that the recipient will be able to get paid +in the end. + +The recipient should verify each message using the following process: + + 1. Verify that the contact address in the message matches the payment channel. + 2. Verify that the new total is the expected amount. + 3. Verify that the new total does not exceed the amount of Ether escrowed. + 4. Verify that the signature is valid and comes from the payment channel sender. + +We'll use the `ethereumjs-util <https://github.com/ethereumjs/ethereumjs-util>`_ +library to write this verifications. The final step can be done a number of ways, +but if it's being done in **JavaScript**. +The following code borrows the `constructMessage` function from the signing **JavaScript code** +above: + +:: + + // this mimics the prefixing behavior of the eth_sign JSON-RPC method. + function prefixed(hash) { + return ethereumjs.ABI.soliditySHA3( + ["string", "bytes32"], + ["\x19Ethereum Signed Message:\n32", hash] + ); + } + + function recoverSigner(message, signature) { + var split = ethereumjs.Util.fromRpcSig(signature); + var publicKey = ethereumjs.Util.ecrecover(message, split.v, split.r, split.s); + var signer = ethereumjs.Util.pubToAddress(publicKey).toString("hex"); + return signer; + } + + function isValidSignature(contractAddress, amount, signature, expectedSigner) { + var message = prefixed(constructPaymentMessage(contractAddress, amount)); + var signer = recoverSigner(message, signature); + return signer.toLowerCase() == + ethereumjs.Util.stripHexPrefix(expectedSigner).toLowerCase(); + } diff --git a/docs/solidity-in-depth.rst b/docs/solidity-in-depth.rst index b6217b47..ddfddb77 100644 --- a/docs/solidity-in-depth.rst +++ b/docs/solidity-in-depth.rst @@ -4,7 +4,7 @@ Solidity in Depth This section should provide you with all you need to know about Solidity. If something is missing here, please contact us on -`Gitter <https://gitter.im/ethereum/solidity>`_ or make a pull request on +`Gitter <https://gitter.im/ethereum/solidity>`_ or create a pull request on `Github <https://github.com/ethereum/solidity/pulls>`_. .. toctree:: @@ -18,3 +18,4 @@ If something is missing here, please contact us on contracts.rst assembly.rst miscellaneous.rst + 050-breaking-changes.rst diff --git a/docs/structure-of-a-contract.rst b/docs/structure-of-a-contract.rst index 7a6317eb..582e5338 100644 --- a/docs/structure-of-a-contract.rst +++ b/docs/structure-of-a-contract.rst @@ -11,16 +11,22 @@ Each contract can contain declarations of :ref:`structure-state-variables`, :ref :ref:`structure-function-modifiers`, :ref:`structure-events`, :ref:`structure-struct-types` and :ref:`structure-enum-types`. Furthermore, contracts can inherit from other contracts. +There are also special kinds of contracts called :ref:`libraries<libraries>` and :ref:`interfaces<interfaces>`. + +The section about :ref:`contracts<contracts>` contains more details than this section, +which serves to provide a quick overview. + .. _structure-state-variables: State Variables =============== -State variables are values which are permanently stored in contract storage. +State variables are variables whose values are permanently stored in contract +storage. :: - pragma solidity ^0.4.0; + pragma solidity >=0.4.0 <0.6.0; contract SimpleStorage { uint storedData; // State variable @@ -40,7 +46,7 @@ Functions are the executable units of code within a contract. :: - pragma solidity ^0.4.0; + pragma solidity >=0.4.0 <0.6.0; contract SimpleAuction { function bid() public payable { // Function @@ -49,7 +55,7 @@ Functions are the executable units of code within a contract. } :ref:`function-calls` can happen internally or externally -and have different levels of visibility (:ref:`visibility-and-getters`) +and have different levels of :ref:`visibility<visibility-and-getters>` towards other contracts. .. _structure-function-modifiers: @@ -58,11 +64,11 @@ Function Modifiers ================== Function modifiers can be used to amend the semantics of functions in a declarative way -(see :ref:`modifiers` in contracts section). +(see :ref:`modifiers` in the contracts section). :: - pragma solidity ^0.4.22; + pragma solidity >=0.4.22 <0.6.0; contract Purchase { address public seller; @@ -75,7 +81,7 @@ Function modifiers can be used to amend the semantics of functions in a declarat _; } - function abort() public onlySeller { // Modifier usage + function abort() public view onlySeller { // Modifier usage // ... } } @@ -89,7 +95,7 @@ Events are convenience interfaces with the EVM logging facilities. :: - pragma solidity ^0.4.21; + pragma solidity >=0.4.21 <0.6.0; contract SimpleAuction { event HighestBidIncreased(address bidder, uint amount); // Event @@ -113,7 +119,7 @@ Structs are custom defined types that can group several variables (see :: - pragma solidity ^0.4.0; + pragma solidity >=0.4.0 <0.6.0; contract Ballot { struct Voter { // Struct @@ -134,7 +140,7 @@ Enums can be used to create custom types with a finite set of 'constant values' :: - pragma solidity ^0.4.0; + pragma solidity >=0.4.0 <0.6.0; contract Purchase { enum State { Created, Locked, Inactive } // Enum diff --git a/docs/style-guide.rst b/docs/style-guide.rst index 6b28f2ab..7b48ccad 100644 --- a/docs/style-guide.rst +++ b/docs/style-guide.rst @@ -52,31 +52,35 @@ Surround top level declarations in solidity source with two blank lines. Yes:: + pragma solidity >=0.4.0 <0.6.0; + contract A { - ... + // ... } contract B { - ... + // ... } contract C { - ... + // ... } No:: + pragma solidity >=0.4.0 <0.6.0; + contract A { - ... + // ... } contract B { - ... + // ... } contract C { - ... + // ... } Within a contract surround function declarations with a single blank line. @@ -85,44 +89,48 @@ Blank lines may be omitted between groups of related one-liners (such as stub fu Yes:: + pragma solidity >=0.4.0 <0.6.0; + contract A { - function spam() public; - function ham() public; + function spam() public pure; + function ham() public pure; } contract B is A { - function spam() public { - ... + function spam() public pure { + // ... } - function ham() public { - ... + function ham() public pure { + // ... } } No:: + pragma solidity >=0.4.0 <0.6.0; + contract A { - function spam() public { - ... + function spam() public pure { + // ... } - function ham() public { - ... + function ham() public pure { + // ... } } .. _maximum_line_length: -Maximum Line Length +Maximum Line Length =================== -Keeping lines under the `PEP 8 recommendation <https://www.python.org/dev/peps/pep-0008/#maximum-line-length>`_ to a maximum of 79 (or 99) +Keeping lines under the `PEP 8 recommendation <https://www.python.org/dev/peps/pep-0008/#maximum-line-length>`_ to a maximum of 79 (or 99) characters helps readers easily parse the code. Wrapped lines should conform to the following guidelines. -1. The first argument should not be attached to the opening parenthesis. +1. The first argument should not be attached to the opening parenthesis. 2. One, and only one, indent should be used. 3. Each argument should fall on its own line. 4. The terminating element, :code:`);`, should be placed on the final line by itself. @@ -132,38 +140,38 @@ Function Calls Yes:: thisFunctionCallIsReallyLong( - longArgument1, - longArgument2, + longArgument1, + longArgument2, longArgument3 ); No:: - thisFunctionCallIsReallyLong(longArgument1, - longArgument2, + thisFunctionCallIsReallyLong(longArgument1, + longArgument2, longArgument3 ); - - thisFunctionCallIsReallyLong(longArgument1, - longArgument2, + + thisFunctionCallIsReallyLong(longArgument1, + longArgument2, longArgument3 - ); - + ); + thisFunctionCallIsReallyLong( longArgument1, longArgument2, longArgument3 - ); + ); thisFunctionCallIsReallyLong( - longArgument1, - longArgument2, + longArgument1, + longArgument2, longArgument3 ); thisFunctionCallIsReallyLong( - longArgument1, - longArgument2, - longArgument3); + longArgument1, + longArgument2, + longArgument3); Assignment Statements @@ -188,8 +196,8 @@ Event Definitions and Event Emitters Yes:: event LongAndLotsOfArgs( - adress sender, - adress recipient, + address sender, + address recipient, uint256 publicKey, uint256 amount, bytes32[] options @@ -205,8 +213,8 @@ Yes:: No:: - event LongAndLotsOfArgs(adress sender, - adress recipient, + event LongAndLotsOfArgs(address sender, + address recipient, uint256 publicKey, uint256 amount, bytes32[] options); @@ -215,7 +223,7 @@ No:: recipient, publicKey, amount, - options); + options); Source File Encoding ==================== @@ -229,30 +237,32 @@ Import statements should always be placed at the top of the file. Yes:: - import "owned"; + pragma solidity >=0.4.0 <0.6.0; + import "./Owned.sol"; contract A { - ... + // ... } - - contract B is owned { - ... + contract B is Owned { + // ... } No:: + pragma solidity >=0.4.0 <0.6.0; + contract A { - ... + // ... } - import "owned"; + import "./Owned.sol"; - contract B is owned { - ... + contract B is Owned { + // ... } Order of Functions @@ -273,13 +283,15 @@ Within a grouping, place the ``view`` and ``pure`` functions last. Yes:: + pragma solidity >=0.4.0 <0.6.0; + contract A { - function A() public { - ... + constructor() public { + // ... } - function() public { - ... + function() external { + // ... } // External functions @@ -303,23 +315,25 @@ Yes:: No:: + pragma solidity >=0.4.0 <0.6.0; + contract A { // External functions // ... + function() external { + // ... + } + // Private functions // ... // Public functions // ... - function A() public { - ... - } - - function() public { - ... + constructor() public { + // ... } // Internal functions @@ -374,13 +388,13 @@ Don't include a whitespace in the fallback function: Yes:: - function() public { + function() external { ... } No:: - function () public { + function () external { ... } @@ -397,6 +411,8 @@ should: Yes:: + pragma solidity >=0.4.0 <0.6.0; + contract Coin { struct Bank { address owner; @@ -406,6 +422,8 @@ Yes:: No:: + pragma solidity >=0.4.0 <0.6.0; + contract Coin { struct Bank { @@ -529,7 +547,7 @@ No:: function increment(uint x) public pure returns (uint) { return x + 1;} -You should explicitly label the visibility of all functions, including constructors. +You should explicitly label the visibility of all functions, including constructors. Yes:: @@ -540,7 +558,7 @@ Yes:: No:: function implicitlyPublic(uint val) { - doSomething(); + doSomething(); } The visibility modifier for a function should come before any custom @@ -663,19 +681,19 @@ Yes:: address a, address b, address c - ) - public + ) + public returns ( - address someAddressName, - uint256 LongArgument, + address someAddressName, + uint256 LongArgument, uint256 Argument ) - { + { doSomething() - + return ( - veryLongReturnArg1, - veryLongReturnArg2, + veryLongReturnArg1, + veryLongReturnArg2, veryLongReturnArg3 ); } @@ -686,16 +704,16 @@ No:: address a, address b, address c - ) - public - returns (address someAddressName, - uint256 LongArgument, + ) + public + returns (address someAddressName, + uint256 LongArgument, uint256 Argument) - { + { doSomething() - - return (veryLongReturnArg1, - veryLongReturnArg1, + + return (veryLongReturnArg1, + veryLongReturnArg1, veryLongReturnArg1); } @@ -705,37 +723,76 @@ manner as modifiers if the function declaration is long or hard to read. Yes:: + pragma solidity >=0.4.0 <0.6.0; + + // Base contracts just to make this compile + contract B { + constructor(uint) public { + } + } + contract C { + constructor(uint, uint) public { + } + } + contract D { + constructor(uint) public { + } + } + contract A is B, C, D { - function A(uint param1, uint param2, uint param3, uint param4, uint param5) + uint x; + + constructor(uint param1, uint param2, uint param3, uint param4, uint param5) B(param1) C(param2, param3) D(param4) public { // do something with param5 + x = param5; } } No:: + pragma solidity >=0.4.0 <0.6.0; + + // Base contracts just to make this compile + contract B { + constructor(uint) public { + } + } + contract C { + constructor(uint, uint) public { + } + } + contract D { + constructor(uint) public { + } + } + contract A is B, C, D { - function A(uint param1, uint param2, uint param3, uint param4, uint param5) + uint x; + + constructor(uint param1, uint param2, uint param3, uint param4, uint param5) B(param1) C(param2, param3) D(param4) public { - // do something with param5 + x = param5; } } - contract A is B, C, D { - function A(uint param1, uint param2, uint param3, uint param4, uint param5) + contract X is B, C, D { + uint x; + + constructor(uint param1, uint param2, uint param3, uint param4, uint param5) B(param1) C(param2, param3) D(param4) public { - // do something with param5 + x = param5; } } @@ -817,6 +874,29 @@ No:: x = y+z; x +=1; +*************** +Order of Layout +*************** + +Layout contract elements in the following order: + +1. Pragma statements +2. Import statements +3. Interfaces +4. Libraries +5. Contracts + +Inside each contract, library or interface, use the following order: + +1. Type declarations +2. State variables +3. Events +4. Functions + +.. note:: + + It might be clearer to declare types close to their use in events or state + variables. ****************** Naming Conventions @@ -830,7 +910,7 @@ The naming recommendations given here are intended to improve the readability, and thus they are not rules, but rather guidelines to try and help convey the most information through the names of things. -Lastly, consistency within a codebase should always supercede any conventions +Lastly, consistency within a codebase should always supersede any conventions outlined in this document. @@ -867,7 +947,69 @@ indistinguishable from the numerals one and zero. Contract and Library Names ========================== -Contracts and libraries should be named using the CapWords style. Examples: ``SimpleToken``, ``SmartBank``, ``CertificateHashRepository``, ``Player``. +* Contracts and libraries should be named using the CapWords style. Examples: ``SimpleToken``, ``SmartBank``, ``CertificateHashRepository``, ``Player``, ``Congress``, ``Owned``. +* Contract and library names should also match their filenames. +* If a contract file includes multiple contracts and/or libraries, then the filename should match the *core contract*. This is not recommended however if it can be avoided. + +As shown in the example below, if the contract name is `Congress` and the library name is `Owned`, then their associated filenames should be `Congress.sol` and `Owned.sol`. + +Yes:: + + pragma solidity >=0.4.0 <0.6.0; + + // Owned.sol + contract Owned { + address public owner; + + constructor() public { + owner = msg.sender; + } + + modifier onlyOwner { + require(msg.sender == owner); + _; + } + + function transferOwnership(address newOwner) public onlyOwner { + owner = newOwner; + } + } + + // Congress.sol + import "./Owned.sol"; + + contract Congress is Owned, TokenRecipient { + //... + } + +No:: + + pragma solidity >=0.4.0 <0.6.0; + + // owned.sol + contract owned { + address public owner; + + constructor() public { + owner = msg.sender; + } + + modifier onlyOwner { + require(msg.sender == owner); + _; + } + + function transferOwnership(address newOwner) public onlyOwner { + owner = newOwner; + } + } + + // Congress.sol + import "./owned.sol"; + + contract Congress is owned, tokenRecipient { + //... + } Struct Names diff --git a/docs/types.rst b/docs/types.rst index 5c20dc67..34b94b88 100644 --- a/docs/types.rst +++ b/docs/types.rst @@ -7,10 +7,8 @@ Types ***** Solidity is a statically typed language, which means that the type of each -variable (state and local) needs to be specified (or at least known - -see :ref:`type-deduction` below) at -compile-time. Solidity provides several elementary types which can be combined -to form complex types. +variable (state and local) needs to be specified. +Solidity provides several elementary types which can be combined to form complex types. In addition, types can interact with each other in expressions containing operators. For a quick reference of the various operators, see :ref:`order`. @@ -52,23 +50,91 @@ Operators: * Comparisons: ``<=``, ``<``, ``==``, ``!=``, ``>=``, ``>`` (evaluate to ``bool``) * Bit operators: ``&``, ``|``, ``^`` (bitwise exclusive or), ``~`` (bitwise negation) -* Arithmetic operators: ``+``, ``-``, unary ``-``, unary ``+``, ``*``, ``/``, ``%`` (remainder), ``**`` (exponentiation), ``<<`` (left shift), ``>>`` (right shift) +* Shift operators: ``<<`` (left shift), ``>>`` (right shift) +* Arithmetic operators: ``+``, ``-``, unary ``-``, ``*``, ``/``, ``%`` (modulo), ``**`` (exponentiation) -Division always truncates (it is just compiled to the ``DIV`` opcode of the EVM), but it does not truncate if both -operators are :ref:`literals<rational_literals>` (or literal expressions). -Division by zero and modulus with zero throws a runtime exception. +Comparisons +^^^^^^^^^^^ -The result of a shift operation is the type of the left operand. The -expression ``x << y`` is equivalent to ``x * 2**y``, and ``x >> y`` is -equivalent to ``x / 2**y``. This means that shifting negative numbers -sign extends. Shifting by a negative amount throws a runtime exception. +The value of a comparison is the one obtained by comparing the integer value. + +Bit operations +^^^^^^^^^^^^^^ + +Bit operations are performed on the two's complement representation of the number. +This means that, for example ``~int256(0) == int256(-1)``. + +Shifts +^^^^^^ + +The result of a shift operation has the type of the left operand. The +expression ``x << y`` is equivalent to ``x * 2**y``, and, for positive integers, +``x >> y`` is equivalent to ``x / 2**y``. For negative ``x``, ``x >> y`` +is equivalent to dividing by a power of ``2`` while rounding down (towards negative infinity). +Shifting by a negative amount throws a runtime exception. .. warning:: - The results produced by shift right of negative values of signed integer types is different from those produced - by other programming languages. In Solidity, shift right maps to division so the shifted negative values - are going to be rounded towards zero (truncated). In other programming languages the shift right of negative values - works like division with rounding down (towards negative infinity). + Before version ``0.5.0`` a right shift ``x >> y`` for negative ``x`` was equivalent to ``x / 2**y``, + i.e. right shifts used rounding towards zero instead of rounding towards negative infinity. + +Addition, Subtraction and Multiplication +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Addition, subtraction and multiplication have the usual semantics. +They wrap in two's complement representation, meaning that +for example ``uint256(0) - uint256(1) == 2**256 - 1``. You have to take these overflows +into account when designing safe smart contracts. + +The expression ``-x`` is equivalent to ``(T(0) - x)`` where +``T`` is the type of ``x``. This means that ``-x`` will not be negative +if the type of ``x`` is an unsigned integer type. Also, ``-x`` can be +positive if ``x`` is negative. There is another caveat also resulting +from two's complement representation:: + + int x = -2**255; + assert(-x == x); + +This means that even if a number is negative, you cannot assume that +its negation will be positive. + + +Division +^^^^^^^^ + +Since the type of the result of an operation is always the type of one of +the operands, division on integers always results in an integer. +In Solidity, division rounds towards zero. This mean that ``int256(-5) / int256(2) == int256(-2)``. + +Note that in contrast, division on :ref:`literals<rational_literals>` results in fractional values +of arbitrary precision. + +.. note:: + Division by zero causes a failing assert. + +Modulo +^^^^^^ + +The modulo operation ``a % n`` yields the remainder ``r`` after the division of the operand ``a`` +by the operand ``n``, where ``q = int(a / n)`` and ``r = a - (n * q)``. This means that modulo +results in the same sign as its left operand (or zero) and ``a % n == -(abs(a) % n)`` holds for negative ``a``: + + * ``int256(5) % int256(2) == int256(1)`` + * ``int256(5) % int256(-2) == int256(1)`` + * ``int256(-5) % int256(2) == int256(-1)`` + * ``int256(-5) % int256(-2) == int256(-1)`` + +.. note:: + Modulo with zero causes a failing assert. + +Exponentiation +^^^^^^^^^^^^^^ + +Exponentiation is only available for unsigned types. Please take care that the types +you are using are large enough to hold the result and prepare for potential wrapping behaviour. + +.. note:: + Note that ``0**0`` is defined by the EVM as ``1``. .. index:: ! ufixed, ! fixed, ! fixed point number @@ -86,7 +152,7 @@ the type and ``N`` represents how many decimal points are available. ``M`` must Operators: * Comparisons: ``<=``, ``<``, ``==``, ``!=``, ``>=``, ``>`` (evaluate to ``bool``) -* Arithmetic operators: ``+``, ``-``, unary ``-``, unary ``+``, ``*``, ``/``, ``%`` (remainder) +* Arithmetic operators: ``+``, ``-``, unary ``-``, ``*``, ``/``, ``%`` (modulo) .. note:: The main difference between floating point (``float`` and ``double`` in many languages, more precisely IEEE 754 numbers) and fixed point numbers is @@ -94,42 +160,83 @@ Operators: defined in the latter. Generally, in floating point almost the entire space is used to represent the number, while only a small number of bits define where the decimal point is. -.. index:: address, balance, send, call, callcode, delegatecall, transfer +.. index:: address, balance, send, call, callcode, delegatecall, staticcall, transfer .. _address: Address ------- -``address``: Holds a 20 byte value (size of an Ethereum address). Address types also have members and serve as a base for all contracts. +The address type comes in two flavours, which are largely identical: + + - ``address``: Holds a 20 byte value (size of an Ethereum address). + - ``address payable``: Same as ``address``, but with the additional members ``transfer`` and ``send``. + +The idea behind this distinction is that ``address payable`` is an address you can send Ether to, +while a plain ``address`` cannot be sent Ether. + +Type conversions: + +Implicit conversions from ``address payable`` to ``address`` are allowed, whereas conversions from ``address`` to ``address payable`` are +not possible (the only way to perform such a conversion is by using an intermediate conversion to ``uint160``). + +:ref:`Address literals<address_literals>` can be implicitly converted to ``address payable``. + +Explicit conversions to and from ``address`` are allowed for integers, integer literals, ``bytes20`` and contract types with the following +caveat: +Conversions of the form ``address payable(x)`` are not allowed. Instead the result of a conversion of the form ``address(x)`` +has the type ``address payable``, if ``x`` is of integer or fixed bytes type, a literal or a contract with a payable fallback function. +If ``x`` is a contract without payable fallback function, then ``address(x)`` will be of type ``address``. +In external function signatures ``address`` is used for both the ``address`` and the ``address payable`` type. + +.. note:: + It might very well be that you do not need to care about the distinction between ``address`` + and ``address payable`` and just use ``address`` everywhere. For example, + if you are using the :ref:`withdrawal pattern<withdrawal_pattern>`, you can (and should) store the + address itself as ``address``, because you invoke the ``transfer`` function on + ``msg.sender``, which is an ``address payable``. Operators: * ``<=``, ``<``, ``==``, ``!=``, ``>=`` and ``>`` +.. warning:: + If you convert a type that uses a larger byte size to an ``address``, for example ``bytes32``, then the ``address`` is truncated. + To reduce conversion ambiguity version 0.4.24 and higher of the compiler force you make the truncation explicit in the conversion. + Take for example the address ``0x111122223333444455556666777788889999AAAABBBBCCCCDDDDEEEEFFFFCCCC``. + + You can use ``address(uint160(bytes20(b)))``, which results in ``0x111122223333444455556666777788889999aAaa``, + or you can use ``address(uint160(uint256(b)))``, which results in ``0x777788889999AaAAbBbbCcccddDdeeeEfFFfCcCc``. + .. note:: - Starting with version 0.5.0 contracts do not derive from the address type, but can still be explicitly converted to address. + The distinction between ``address`` and ``address payable`` was introduced with version 0.5.0. + Also starting from that version, contracts do not derive from the address type, but can still be explicitly converted to + ``address`` or to ``address payable``, if they have a payable fallback function. .. _members-of-addresses: Members of Addresses ^^^^^^^^^^^^^^^^^^^^ -* ``balance`` and ``transfer`` +For a quick reference of all members of address, see :ref:`address_related`. -For a quick reference, see :ref:`address_related`. +* ``balance`` and ``transfer`` It is possible to query the balance of an address using the property ``balance`` -and to send Ether (in units of wei) to an address using the ``transfer`` function: +and to send Ether (in units of wei) to a payable address using the ``transfer`` function: :: - address x = 0x123; - address myAddress = this; + address payable x = address(0x123); + address myAddress = address(this); if (x.balance < 10 && myAddress.balance >= 10) x.transfer(10); +The ``transfer`` function fails if the balance of the current contract is not large enough +or if the Ether transfer is rejected by the receiving account. The ``transfer`` function +reverts on failure. + .. note:: - If ``x`` is a contract address, its code (more specifically: its fallback function, if present) will be executed together with the ``transfer`` call (this is a feature of the EVM and cannot be prevented). If that execution runs out of gas or fails in any way, the Ether transfer will be reverted and the current contract will stop with an exception. + If ``x`` is a contract address, its code (more specifically: its :ref:`fallback-function`, if present) will be executed together with the ``transfer`` call (this is a feature of the EVM and cannot be prevented). If that execution runs out of gas or fails in any way, the Ether transfer will be reverted and the current contract will stop with an exception. * ``send`` @@ -141,40 +248,56 @@ Send is the low-level counterpart of ``transfer``. If the execution fails, the c to make safe Ether transfers, always check the return value of ``send``, use ``transfer`` or even better: use a pattern where the recipient withdraws the money. -* ``call``, ``callcode`` and ``delegatecall`` +* ``call``, ``delegatecall`` and ``staticcall`` -Furthermore, to interface with contracts that do not adhere to the ABI, -the function ``call`` is provided which takes an arbitrary number of arguments of any type. These arguments are padded to 32 bytes and concatenated. One exception is the case where the first argument is encoded to exactly four bytes. In this case, it is not padded to allow the use of function signatures here. +In order to interface with contracts that do not adhere to the ABI, +or to get more direct control over the encoding, +the functions ``call``, ``delegatecall`` and ``staticcall`` are provided. +They all take a single ``bytes memory`` argument as input and +return the success condition (as a ``bool``) and the returned data +(``bytes memory``). +The functions ``abi.encode``, ``abi.encodePacked``, ``abi.encodeWithSelector`` +and ``abi.encodeWithSignature`` can be used to encode structured data. -:: +Example:: - address nameReg = 0x72ba7d8e73fe8eb666ea66babc8116a41bfb10e2; - nameReg.call("register", "MyName"); - nameReg.call(bytes4(keccak256("fun(uint256)")), a); + bytes memory payload = abi.encodeWithSignature("register(string)", "MyName"); + (bool success, bytes memory returnData) = address(nameReg).call(payload); + require(success); -``call`` returns a boolean indicating whether the invoked function terminated (``true``) or caused an EVM exception (``false``). It is not possible to access the actual data returned (for this we would need to know the encoding and size in advance). +.. warning:: + All these functions are low-level functions and should be used with care. + Specifically, any unknown contract might be malicious and if you call it, you + hand over control to that contract which could in turn call back into + your contract, so be prepared for changes to your state variables + when the call returns. The regular way to interact with other contracts + is to call a function on a contract object (``x.f()``). + +:: note:: + Previous versions of Solidity allowed these functions to receive + arbitrary arguments and would also handle a first argument of type + ``bytes4`` differently. These edge cases were removed in version 0.5.0. It is possible to adjust the supplied gas with the ``.gas()`` modifier:: - namReg.call.gas(1000000)("register", "MyName"); + namReg.call.gas(1000000)(abi.encodeWithSignature("register(string)", "MyName")); Similarly, the supplied Ether value can be controlled too:: - nameReg.call.value(1 ether)("register", "MyName"); + nameReg.call.value(1 ether)(abi.encodeWithSignature("register(string)", "MyName")); Lastly, these modifiers can be combined. Their order does not matter:: - nameReg.call.gas(1000000).value(1 ether)("register", "MyName"); + nameReg.call.gas(1000000).value(1 ether)(abi.encodeWithSignature("register(string)", "MyName")); -.. note:: - It is not yet possible to use the gas or value modifiers on overloaded functions. +In a similar way, the function ``delegatecall`` can be used: the difference is that only the code of the given address is used, all other aspects (storage, balance, ...) are taken from the current contract. The purpose of ``delegatecall`` is to use library code which is stored in another contract. The user has to ensure that the layout of storage in both contracts is suitable for delegatecall to be used. - A workaround is to introduce a special case for gas and value and just re-check - whether they are present at the point of overload resolution. +.. note:: + Prior to homestead, only a limited variant called ``callcode`` was available that did not provide access to the original ``msg.sender`` and ``msg.value`` values. This function was removed in version 0.5.0. -In a similar way, the function ``delegatecall`` can be used: the difference is that only the code of the given address is used, all other aspects (storage, balance, ...) are taken from the current contract. The purpose of ``delegatecall`` is to use library code which is stored in another contract. The user has to ensure that the layout of storage in both contracts is suitable for delegatecall to be used. Prior to homestead, only a limited variant called ``callcode`` was available that did not provide access to the original ``msg.sender`` and ``msg.value`` values. +Since byzantium ``staticcall`` can be used as well. This is basically the same as ``call``, but will revert if the called function modifies the state in any way. -All three functions ``call``, ``delegatecall`` and ``callcode`` are very low-level functions and should only be used as a *last resort* as they break the type-safety of Solidity. +All three functions ``call``, ``delegatecall`` and ``staticcall`` are very low-level functions and should only be used as a *last resort* as they break the type-safety of Solidity. The ``.gas()`` option is available on all three methods, while the ``.value()`` option is not supported for ``delegatecall``. @@ -182,41 +305,72 @@ The ``.gas()`` option is available on all three methods, while the ``.value()`` All contracts can be converted to ``address`` type, so it is possible to query the balance of the current contract using ``address(this).balance``. +.. index:: ! contract type, ! type; contract + +.. _contract_types: + +Contract Types +-------------- + +Every :ref:`contract<contracts>` defines its own type. +You can implicitly convert contracts to contracts they inherit from. +Contracts can be explicitly converted to and from all other contract types +and the ``address`` type. + +Explicit conversion to and from the ``address payable`` type +is only possible if the contract type has a payable fallback function. +The conversion is still performed using ``address(x)`` and not +using ``address payable(x)``. You can find more information in the section about +the :ref:`address type<address>`. + .. note:: - The use of ``callcode`` is discouraged and will be removed in the future. + Before version 0.5.0, contracts directly derived from the address type + and there was no distinction between ``address`` and ``address payable``. -.. warning:: - All these functions are low-level functions and should be used with care. - Specifically, any unknown contract might be malicious and if you call it, you - hand over control to that contract which could in turn call back into - your contract, so be prepared for changes to your state variables - when the call returns. +If you declare a local variable of contract type (`MyContract c`), you can call +functions on that contract. Take care to assign it from somewhere that is the +same contract type. -.. index:: byte array, bytes32 +You can also instantiate contracts (which means they are newly created). You +can find more details in the :ref:`'Contracts via new'<creating-contracts>` +section. + +The data representation of a contract is identical to that of the ``address`` +type and this type is also used in the :ref:`ABI<ABI>`. +Contracts do not support any operators. + +The members of contract types are the external functions of the contract +including public state variables. + +.. index:: byte array, bytes32 Fixed-size byte arrays ---------------------- -``bytes1``, ``bytes2``, ``bytes3``, ..., ``bytes32``. ``byte`` is an alias for ``bytes1``. +The value types ``bytes1``, ``bytes2``, ``bytes3``, ..., ``bytes32`` +hold a sequence of bytes from one to up to 32. +``byte`` is an alias for ``bytes1``. Operators: * Comparisons: ``<=``, ``<``, ``==``, ``!=``, ``>=``, ``>`` (evaluate to ``bool``) -* Bit operators: ``&``, ``|``, ``^`` (bitwise exclusive or), ``~`` (bitwise negation), ``<<`` (left shift), ``>>`` (right shift) +* Bit operators: ``&``, ``|``, ``^`` (bitwise exclusive or), ``~`` (bitwise negation) +* Shift operators: ``<<`` (left shift), ``>>`` (right shift) * Index access: If ``x`` is of type ``bytesI``, then ``x[k]`` for ``0 <= k < I`` returns the ``k`` th byte (read-only). -The shifting operator works with any integer type as right operand (but will -return the type of the left operand), which denotes the number of bits to shift by. -Shifting by a negative amount will cause a runtime exception. +The shifting operator works with any integer type as right operand (but +returns the type of the left operand), which denotes the number of bits to shift by. +Shifting by a negative amount causes a runtime exception. Members: * ``.length`` yields the fixed length of the byte array (read-only). .. note:: - It is possible to use an array of bytes as ``byte[]``, but it is wasting a lot of space, 31 bytes every element, - to be exact, when passing in calls. It is better to use ``bytes``. + The type ``byte[]`` is an array of bytes, but due to padding rules, it wastes + 31 bytes of space for each element (except in storage). It is better to use the ``bytes`` + type instead. Dynamically-sized byte array ---------------------------- @@ -226,10 +380,6 @@ Dynamically-sized byte array ``string``: Dynamically-sized UTF-8-encoded string, see :ref:`arrays`. Not a value-type! -As a rule of thumb, use ``bytes`` for arbitrary-length raw byte data and ``string`` -for arbitrary-length string (UTF-8) data. If you can limit the length to a certain -number of bytes, always use one of ``bytes1`` to ``bytes32`` because they are much cheaper. - .. index:: address, literal;address .. _address_literals: @@ -238,7 +388,7 @@ Address Literals ---------------- Hexadecimal literals that pass the address checksum test, for example -``0xdCad3a6d3569DF655070DEd06cb7A1b2Ccd1D3AF`` are of ``address`` type. +``0xdCad3a6d3569DF655070DEd06cb7A1b2Ccd1D3AF`` are of ``address payable`` type. Hexadecimal literals that are between 39 and 41 digits long and do not pass the checksum test produce a warning and are treated as regular rational number literals. @@ -263,8 +413,14 @@ one side. Examples include ``1.``, ``.1`` and ``1.3``. Scientific notation is also supported, where the base can have fractions, while the exponent cannot. Examples include ``2e10``, ``-2e10``, ``2e-10``, ``2.5e1``. +Underscores can be used to separate the digits of a numeric literal to aid readability. +For example, decimal ``123_000``, hexadecimal ``0x2eff_abde``, scientific decimal notation ``1_2e345_678`` are all valid. +Underscores are only allowed between two digits and only one consecutive underscore is allowed. +There is no additional semantic meaning added to a number literal containing underscores, +the underscores are ignored. + Number literal expressions retain arbitrary precision until they are converted to a non-literal type (i.e. by -using them together with a non-literal expression). +using them together with a non-literal expression or by explicit conversion). This means that computations do not overflow and divisions do not truncate in number literal expressions. @@ -286,14 +442,15 @@ a non-rational number). belong to the same number literal type for the rational number three. .. warning:: - Division on integer literals used to truncate in earlier versions, but it will now convert into a rational number, i.e. ``5 / 2`` is not equal to ``2``, but to ``2.5``. + Division on integer literals used to truncate in Solidity prior to version 0.4.0, but it now converts into a rational number, i.e. ``5 / 2`` is not equal to ``2``, but to ``2.5``. .. note:: Number literal expressions are converted into a non-literal type as soon as they are used with non-literal - expressions. Even though we know that the value of the - expression assigned to ``b`` in the following example evaluates to - an integer, but the partial expression ``2.5 + a`` does not type check so the code - does not compile + expressions. Disregarding types, the value of the expression assigned to ``b`` + below evaluates to an integer. Because ``a`` is of type ``uint128``, the + expression ``2.5 + a`` has to have a proper type, though. Since there is no common type + for the type of ``2.5`` and ``uint128``, the Solidity compiler does not accept + this code. :: @@ -301,22 +458,51 @@ a non-rational number). uint128 b = 2.5 + a + 0.5; .. index:: literal, literal;string, string +.. _string_literals: String Literals --------------- -String literals are written with either double or single-quotes (``"foo"`` or ``'bar'``). They do not imply trailing zeroes as in C; ``"foo"`` represents three bytes not four. As with integer literals, their type can vary, but they are implicitly convertible to ``bytes1``, ..., ``bytes32``, if they fit, to ``bytes`` and to ``string``. +String literals are written with either double or single-quotes (``"foo"`` or ``'bar'``). They do not imply trailing zeroes as in C; ``"foo"`` represents three bytes, not four. As with integer literals, their type can vary, but they are implicitly convertible to ``bytes1``, ..., ``bytes32``, if they fit, to ``bytes`` and to ``string``. + +String literals support the following escape characters: + + - ``\<newline>`` (escapes an actual newline) + - ``\\`` (backslash) + - ``\'`` (single quote) + - ``\"`` (double quote) + - ``\b`` (backspace) + - ``\f`` (form feed) + - ``\n`` (newline) + - ``\r`` (carriage return) + - ``\t`` (tab) + - ``\v`` (vertical tab) + - ``\xNN`` (hex escape, see below) + - ``\uNNNN`` (unicode escape, see below) -String literals support escape characters, such as ``\n``, ``\xNN`` and ``\uNNNN``. ``\xNN`` takes a hex value and inserts the appropriate byte, while ``\uNNNN`` takes a Unicode codepoint and inserts an UTF-8 sequence. +``\xNN`` takes a hex value and inserts the appropriate byte, while ``\uNNNN`` takes a Unicode codepoint and inserts an UTF-8 sequence. + +The string in the following example has a length of ten bytes. +It starts with a newline byte, followed by a double quote, a single +quote a backslash character and then (without separator) the +character sequence ``abcdef``. + +:: + + "\n\"\'\\abc\ + def" + +Any unicode line terminator which is not a newline (i.e. LF, VF, FF, CR, NEL, LS, PS) is considered to +terminate the string literal. Newline only terminates the string literal if it is not preceded by a ``\``. .. index:: literal, bytes Hexadecimal Literals -------------------- -Hexademical Literals are prefixed with the keyword ``hex`` and are enclosed in double or single-quotes (``hex"001122FF"``). Their content must be a hexadecimal string and their value will be the binary representation of those values. +Hexadecimal literals are prefixed with the keyword ``hex`` and are enclosed in double or single-quotes (``hex"001122FF"``). Their content must be a hexadecimal string and their value will be the binary representation of those values. -Hexademical Literals behave like String Literals and have the same convertibility restrictions. +Hexadecimal literals behave like :ref:`string literals <string_literals>` and have the same convertibility restrictions. .. index:: enum @@ -326,12 +512,17 @@ Enums ----- Enums are one way to create a user-defined type in Solidity. They are explicitly convertible -to and from all integer types but implicit conversion is not allowed. The explicit conversions -check the value ranges at runtime and a failure causes an exception. Enums needs at least one member. +to and from all integer types but implicit conversion is not allowed. The explicit conversion +from integer checks at runtime that the value lies inside the range of the enum and causes a failing assert otherwise. +Enums needs at least one member. + +The data representation is the same as for enums in C: The options are represented by +subsequent unsigned integer values starting from ``0``. + :: - pragma solidity ^0.4.16; + pragma solidity >=0.4.16 <0.6.0; contract test { enum ActionChoices { GoLeft, GoRight, GoStraight, SitStill } @@ -345,7 +536,7 @@ check the value ranges at runtime and a failure causes an exception. Enums need // Since enum types are not part of the ABI, the signature of "getChoice" // will automatically be changed to "getChoice() returns (uint8)" // for all matters external to Solidity. The integer type used is just - // large enough to hold all enum values, i.e. if you have more values, + // large enough to hold all enum values, i.e. if you have more than 256 values, // `uint16` will be used and so on. function getChoice() public view returns (ActionChoices) { return choice; @@ -380,22 +571,41 @@ be passed via and returned from external function calls. Function types are notated as follows:: - function (<parameter types>) {internal|external} [pure|constant|view|payable] [returns (<return types>)] + function (<parameter types>) {internal|external} [pure|view|payable] [returns (<return types>)] In contrast to the parameter types, the return types cannot be empty - if the function type should not return anything, the whole ``returns (<return types>)`` part has to be omitted. By default, function types are internal, so the ``internal`` keyword can be -omitted. In contrast, contract functions themselves are public by default, -only when used as the name of a type, the default is internal. +omitted. Note that this only applies to function types. Visibility has +to be specified explicitly for functions defined in contracts, they +do not have a default. + +Conversions: + +A value of external function type can be explicitly converted to ``address`` +resulting in the address of the contract of the function. + +A function type ``A`` is implicitly convertible to a function type ``B`` if and only if +their parameter types are identical, their return types are identical, +their internal/external property is identical and the state mutability of ``A`` +is not more restrictive than the state mutability of ``B``. In particular: -There are two ways to access a function in the current contract: Either directly -by its name, ``f``, or using ``this.f``. The former will result in an internal -function, the latter in an external function. + - ``pure`` functions can be converted to ``view`` and ``non-payable`` functions + - ``view`` functions can be converted to ``non-payable`` functions + - ``payable`` functions can be converted to ``non-payable`` functions -If a function type variable is not initialized, calling it will result -in an exception. The same happens if you call a function after using ``delete`` +No other conversions between function types are possible. + +The rule about ``payable`` and ``non-payable`` might be a little +confusing, but in essence, if a function is ``payable``, this means that it +also accepts a payment of zero Ether, so it also is ``non-payable``. +On the other hand, a ``non-payable`` function will reject Ether sent to it, +so ``non-payable`` functions cannot be converted to ``payable`` functions. + +If a function type variable is not initialised, calling it results +in a failed assertion. The same happens if you call a function after using ``delete`` on it. If external function types are used outside of the context of Solidity, @@ -406,20 +616,22 @@ Note that public functions of the current contract can be used both as an internal and as an external function. To use ``f`` as an internal function, just use ``f``, if you want to use its external form, use ``this.f``. -Additionally, public (or external) functions also have a special member called ``selector``, +Members: + +Public (or external) functions also have a special member called ``selector``, which returns the :ref:`ABI function selector <abi_function_selector>`:: - pragma solidity ^0.4.16; + pragma solidity >=0.4.16 <0.6.0; contract Selector { - function f() public view returns (bytes4) { + function f() public pure returns (bytes4) { return this.f.selector; } } Example that shows how to use internal function types:: - pragma solidity ^0.4.16; + pragma solidity >=0.4.16 <0.6.0; library ArrayUtils { // internal functions can be used in internal library functions because @@ -470,20 +682,20 @@ Example that shows how to use internal function types:: Another example that uses external function types:: - pragma solidity ^0.4.22; + pragma solidity >=0.4.22 <0.6.0; contract Oracle { struct Request { bytes data; - function(bytes memory) external callback; + function(uint) external callback; } Request[] requests; event NewRequest(uint); - function query(bytes data, function(bytes memory) external callback) public { + function query(bytes memory data, function(uint) external callback) public { requests.push(Request(data, callback)); emit NewRequest(requests.length - 1); } - function reply(uint requestID, bytes response) public { + function reply(uint requestID, uint response) public { // Here goes the check that the reply comes from a trusted source requests[requestID].callback(response); } @@ -491,15 +703,16 @@ Another example that uses external function types:: contract OracleUser { Oracle constant oracle = Oracle(0x1234567); // known contract - function buySomething() { + uint exchangeRate; + function buySomething() public { oracle.query("USD", this.oracleResponse); } - function oracleResponse(bytes response) public { + function oracleResponse(uint response) public { require( msg.sender == address(oracle), "Only oracle can call this." ); - // Use the data + exchangeRate = response; } } @@ -509,48 +722,58 @@ Another example that uses external function types:: .. index:: ! type;reference, ! reference type, storage, memory, location, array, struct Reference Types -================== +=============== -Complex types, i.e. types which do not always fit into 256 bits have to be handled -more carefully than the value-types we have already seen. Since copying -them can be quite expensive, we have to think about whether we want them to be -stored in **memory** (which is not persisting) or **storage** (where the state -variables are held). +Values of reference type can be modified through multiple different names. +Contrast this with value types where you get an independent copy whenever +a variable of value type is used. Because of that, reference types have to be handled +more carefully than value types. Currently, reference types comprise structs, +arrays and mappings. If you use a reference type, you always have to explicitly +provide the data area where the type is stored: ``memory`` (whose lifetime is limited +to a function call), ``storage`` (the location where the state variables are stored) +or ``calldata`` (special data location that contains the function arguments, +only available for external function call parameters). + +An assignment or type conversion that changes the data location will always incur an automatic copy operation, +while assignments inside the same data location only copy in some cases for storage types. + +.. _data-location: Data location ------------- -Every complex type, i.e. *arrays* and *structs*, has an additional -annotation, the "data location", about whether it is stored in memory or in storage. Depending on the -context, there is always a default, but it can be overridden by appending -either ``storage`` or ``memory`` to the type. The default for function parameters (including return parameters) is ``memory``, the default for local variables is ``storage`` and the location is forced -to ``storage`` for state variables (obviously). - -There is also a third data location, ``calldata``, which is a non-modifiable, -non-persistent area where function arguments are stored. Function parameters -(not return parameters) of external functions are forced to ``calldata`` and -behave mostly like ``memory``. - -Data locations are important because they change how assignments behave: -assignments between storage and memory and also to a state variable (even from other state variables) -always create an independent copy. -Assignments to local storage variables only assign a reference though, and -this reference always points to the state variable even if the latter is changed -in the meantime. -On the other hand, assignments from a memory stored reference type to another -memory-stored reference type do not create a copy. +Every reference type, i.e. *arrays* and *structs*, has an additional +annotation, the "data location", about where it is stored. There are three data locations: +``memory``, ``storage`` and ``calldata``. Calldata is only valid for parameters of external contract +functions and is required for this type of parameter. Calldata is a non-modifiable, +non-persistent area where function arguments are stored, and behaves mostly like memory. + + +.. note:: + Prior to version 0.5.0 the data location could be omitted, and would default to different locations + depending on the kind of variable, function type, etc., but all complex types must now give an explicit + data location. + +Data locations are not only relevant for persistency of data, but also for the semantics of assignments: +assignments between storage and memory (or from calldata) always create an independent copy. +Assignments from memory to memory only create references. This means that changes to one memory variable +are also visible in all other memory variables that refer to the same data. +Assignments from storage to a local storage variables also only assign a reference. +In contrast, all other assignments to storage always copy. Examples for this case +are assignments to state variables or to members of local variables of storage struct type, even +if the local variable itself is just a reference. :: - pragma solidity ^0.4.0; + pragma solidity >=0.4.0 <0.6.0; contract C { uint[] x; // the data location of x is storage // the data location of memoryArray is memory - function f(uint[] memoryArray) public { + function f(uint[] memory memoryArray) public { x = memoryArray; // works, copies the whole array to storage - var y = x; // works, assigns a pointer, data location of y is storage + uint[] storage y = x; // works, assigns a pointer, data location of y is storage y[7]; // fine, returns the 8th element y.length = 2; // fine, modifies x through y delete x; // fine, clears the array, also modifies y @@ -564,21 +787,10 @@ memory-stored reference type do not create a copy. h(x); // calls h and creates an independent, temporary copy in memory } - function g(uint[] storage storageArray) internal {} - function h(uint[] memoryArray) public {} + function g(uint[] storage) internal pure {} + function h(uint[] memory) public pure {} } -Summary -^^^^^^^ - -Forced data location: - - parameters (not return) of external functions: calldata - - state variables: storage - -Default data location: - - parameters (also return) of functions: memory - - all other local variables: storage - .. index:: ! array .. _arrays: @@ -587,9 +799,10 @@ Arrays ------ Arrays can have a compile-time fixed size or they can be dynamic. -For storage arrays, the element type can be arbitrary (i.e. also other -arrays, mappings or structs). For memory arrays, it cannot be a mapping and -has to be an ABI type if it is an argument of a publicly-visible function. +The are few restrictions for the element, it can also be +another array, a mapping or a struct. The general restrictions for +types apply, though, in that mappings can only be used in storage +and publicly-visible functions need parameters that are :ref:`ABI types <ABI>`. An array of fixed size ``k`` and element type ``T`` is written as ``T[k]``, an array of dynamic size as ``T[]``. As an example, an array of 5 dynamic @@ -599,11 +812,17 @@ third dynamic array, you use ``x[2][1]`` (indices are zero-based and access works in the opposite way of the declaration, i.e. ``x[2]`` shaves off one level in the type from the right). -Variables of type ``bytes`` and ``string`` are special arrays. A ``bytes`` is similar to ``byte[]``, -but it is packed tightly in calldata. ``string`` is equal to ``bytes`` but does not allow -length or index access (for now). +Accessing an array past its end causes a revert. If you want to add +new elements, you have to use ``.push()`` or increase the ``.length`` +member (see below). +Variables of type ``bytes`` and ``string`` are special arrays. A ``bytes`` is similar to ``byte[]``, +but it is packed tightly in calldata and memory. ``string`` is equal to ``bytes`` but does not allow +length or index access. So ``bytes`` should always be preferred over ``byte[]`` because it is cheaper. +As a rule of thumb, use ``bytes`` for arbitrary-length raw byte data and ``string`` +for arbitrary-length string (UTF-8) data. If you can limit the length to a certain +number of bytes, always use one of ``bytes1`` to ``bytes32`` because they are much cheaper. .. note:: If you want to access the byte-representation of a string ``s``, use @@ -619,19 +838,21 @@ The numeric index will become a required parameter for the getter. Allocating Memory Arrays ^^^^^^^^^^^^^^^^^^^^^^^^ -Creating arrays with variable length in memory can be done using the ``new`` keyword. -As opposed to storage arrays, it is **not** possible to resize memory arrays by assigning to -the ``.length`` member. +You can use the ``new`` keyword to create arrays with a runtime-dependent length in memory. +As opposed to storage arrays, it is **not** possible to resize memory arrays (e.g. by assigning to +the ``.length`` member). You either have to calculate the required size in advance +or create a new memory array and copy every element. :: - pragma solidity ^0.4.16; + pragma solidity >=0.4.16 <0.6.0; contract C { function f(uint len) public pure { uint[] memory a = new uint[](7); bytes memory b = new bytes(len); - // Here we have a.length == 7 and b.length == len + assert(a.length == 7); + assert(b.length == len); a[6] = 8; } } @@ -646,13 +867,13 @@ assigned to a variable right away. :: - pragma solidity ^0.4.16; + pragma solidity >=0.4.16 <0.6.0; contract C { function f() public pure { g([uint(1), 2, 3]); } - function g(uint[3] _data) public pure { + function g(uint[3] memory) public pure { // ... } } @@ -660,68 +881,103 @@ assigned to a variable right away. The type of an array literal is a memory array of fixed size whose base type is the common type of the given elements. The type of ``[1, 2, 3]`` is ``uint8[3] memory``, because the type of each of these constants is ``uint8``. -Because of that, it was necessary to convert the first element in the example +Because of that, it is necessary to convert the first element in the example above to ``uint``. Note that currently, fixed size memory arrays cannot be assigned to dynamically-sized memory arrays, i.e. the following is not possible: :: - // This will not compile. - - pragma solidity ^0.4.0; + pragma solidity >=0.4.0 <0.6.0; + // This will not compile. contract C { function f() public { // The next line creates a type error because uint[3] memory // cannot be converted to uint[] memory. - uint[] x = [uint(1), 3, 4]; + uint[] memory x = [uint(1), 3, 4]; } } It is planned to remove this restriction in the future but currently creates some complications because of how arrays are passed in the ABI. -.. index:: ! array;length, length, push, !array;push +.. index:: ! array;length, length, push, pop, !array;push, !array;pop Members ^^^^^^^ **length**: - Arrays have a ``length`` member to hold their number of elements. - Dynamic arrays can be resized in storage (not in memory) by changing the - ``.length`` member. This does not happen automatically when attempting to access elements outside the current length. The size of memory arrays is fixed (but dynamic, i.e. it can depend on runtime parameters) once they are created. + Arrays have a ``length`` member that contains their number of elements. + The length of memory arrays is fixed (but dynamic, i.e. it can depend on runtime parameters) once they are created. + For dynamically-sized arrays (only available for storage), this member can be assigned to resize the array. + Accessing elements outside the current length does not automatically resize the array and instead causes a failing assertion. + Increasing the length adds new zero-initialised elements to the array. + Reducing the length performs an implicit :ref:``delete`` on each of the removed elements. **push**: - Dynamic storage arrays and ``bytes`` (not ``string``) have a member function called ``push`` that can be used to append an element at the end of the array. The function returns the new length. + Dynamic storage arrays and ``bytes`` (not ``string``) have a member function called ``push`` that you can use to append an element at the end of the array. The element will be zero-initialised. The function returns the new length. +**pop**: + Dynamic storage arrays and ``bytes`` (not ``string``) have a member function called ``pop`` that you can use to remove an element from the end of the array. This also implicitly calls :ref:``delete`` on the removed element. .. warning:: - It is not yet possible to use arrays of arrays in external functions. + If you use ``.length--`` on an empty array, it causes an underflow and + thus sets the length to ``2**256-1``. -.. warning:: - Due to limitations of the EVM, it is not possible to return - dynamic content from external function calls. The function ``f`` in - ``contract C { function f() returns (uint[]) { ... } }`` will return - something if called from web3.js, but not if called from Solidity. +.. note:: + Increasing the length of a storage array has constant gas costs because + storage is assumed to be zero-initialised, while decreasing + the length has at least linear cost (but in most cases worse than linear), + because it includes explicitly clearing the removed + elements similar to calling :ref:``delete`` on them. - The only workaround for now is to use large statically-sized arrays. +.. note:: + It is not yet possible to use arrays of arrays in external functions + (but they are supported in public functions). +.. note:: + In EVM versions before Byzantium, it was not possible to access + dynamic arrays return from function calls. If you call functions + that return dynamic arrays, make sure to use an EVM that is set to + Byzantium mode. :: - pragma solidity ^0.4.16; + pragma solidity >=0.4.16 <0.6.0; contract ArrayContract { uint[2**20] m_aLotOfIntegers; // Note that the following is not a pair of dynamic arrays but a // dynamic array of pairs (i.e. of fixed size arrays of length two). + // Because of that, T[] is always a dynamic array of T, even if T + // itself is an array. + // Data location for all state variables is storage. bool[2][] m_pairsOfFlags; - // newPairs is stored in memory - the default for function arguments - function setAllFlagPairs(bool[2][] newPairs) public { - // assignment to a storage array replaces the complete array + // newPairs is stored in memory - the only possibility + // for public contract function arguments + function setAllFlagPairs(bool[2][] memory newPairs) public { + // assignment to a storage array performs a copy of ``newPairs`` and + // replaces the complete array ``m_pairsOfFlags``. m_pairsOfFlags = newPairs; } + struct StructType { + uint[] contents; + uint moreInfo; + } + StructType s; + + function f(uint[] memory c) public { + // stores a reference to ``s`` in ``g`` + StructType storage g = s; + // also changes ``s.moreInfo``. + g.moreInfo = 2; + // assigns a copy because ``g.contents`` + // is not a local variable, but a member of + // a local variable. + g.contents = c; + } + function setFlagPair(uint index, bool flagA, bool flagB) public { // access to a non-existing index will throw an exception m_pairsOfFlags[index][0] = flagA; @@ -743,26 +999,31 @@ Members bytes m_byteData; - function byteArrays(bytes data) public { + function byteArrays(bytes memory data) public { // byte arrays ("bytes") are different as they are stored without padding, // but can be treated identical to "uint8[]" m_byteData = data; m_byteData.length += 7; - m_byteData[3] = byte(8); + m_byteData[3] = 0x08; delete m_byteData[2]; } - function addFlag(bool[2] flag) public returns (uint) { + function addFlag(bool[2] memory flag) public returns (uint) { return m_pairsOfFlags.push(flag); } - function createMemoryArray(uint size) public pure returns (bytes) { + function createMemoryArray(uint size) public pure returns (bytes memory) { // Dynamic memory arrays are created using `new`: uint[2][] memory arrayOfPairs = new uint[2][](size); + + // Inline arrays are always statically-sized and if you only + // use literals, you have to provide at least one type. + arrayOfPairs[0] = [uint(1), 2]; + // Create a dynamic byte array: bytes memory b = new bytes(200); for (uint i = 0; i < b.length; i++) - b[i] = byte(i); + b[i] = byte(uint8(i)); return b; } } @@ -780,7 +1041,7 @@ shown in the following example: :: - pragma solidity ^0.4.11; + pragma solidity >=0.4.11 <0.6.0; contract CrowdFunding { // Defines a new type with two fields. @@ -790,7 +1051,7 @@ shown in the following example: } struct Campaign { - address beneficiary; + address payable beneficiary; uint fundingGoal; uint numFunders; uint amount; @@ -800,9 +1061,12 @@ shown in the following example: uint numCampaigns; mapping (uint => Campaign) campaigns; - function newCampaign(address beneficiary, uint goal) public returns (uint campaignID) { + function newCampaign(address payable beneficiary, uint goal) public returns (uint campaignID) { campaignID = numCampaigns++; // campaignID is return variable - // Creates new struct and saves in storage. We leave out the mapping type. + // Creates new struct in memory and copies it to storage. + // We leave out the mapping type, because it is not valid in memory. + // If structs are copied (even from storage to storage), mapping types + // are always omitted, because they cannot be enumerated. campaigns[campaignID] = Campaign(beneficiary, goal, 0, 0); } @@ -832,11 +1096,12 @@ Struct types can be used inside mappings and arrays and they can itself contain mappings and arrays. It is not possible for a struct to contain a member of its own type, -although the struct itself can be the value type of a mapping member. +although the struct itself can be the value type of a mapping member +or it can contain a dynamically-sized array of its type. This restriction is necessary, as the size of the struct has to be finite. Note how in all the functions, a struct type is assigned to a local variable -(of the default storage data location). +with data location ``storage``. This does not copy the struct but only stores a reference so that assignments to members of the local variable actually write to the state. @@ -847,32 +1112,39 @@ assigning it to a local variable, as in .. index:: !mapping Mappings -======== - -Mapping types are declared as ``mapping(_KeyType => _ValueType)``. -Here ``_KeyType`` can be almost any type except for a mapping, a dynamically sized array, a contract, an enum and a struct. -``_ValueType`` can actually be any type, including mappings. - -Mappings can be seen as `hash tables <https://en.wikipedia.org/wiki/Hash_table>`_ which are virtually initialized such that -every possible key exists and is mapped to a value whose byte-representation is -all zeros: a type's :ref:`default value <default-value>`. The similarity ends here, though: The key data is not actually stored -in a mapping, only its ``keccak256`` hash used to look up the value. - -Because of this, mappings do not have a length or a concept of a key or value being "set". - -Mappings are only allowed for state variables (or as storage reference types -in internal functions). - -It is possible to mark mappings ``public`` and have Solidity create a :ref:`getter <visibility-and-getters>`. -The ``_KeyType`` will become a required parameter for the getter and it will -return ``_ValueType``. +-------- -The ``_ValueType`` can be a mapping too. The getter will have one parameter -for each ``_KeyType``, recursively. +You declare mapping types with the syntax ``mapping(_KeyType => _ValueType)``. +The ``_KeyType`` can be any elementary type. This means it can be any of +the built-in value types plus ``bytes`` and ``string``. User-defined +or complex types like contract types, enums, mappings, structs and any array type +apart from ``bytes`` and ``string`` are not allowed. +``_ValueType`` can be any type, including mappings. + +You can think of mappings as `hash tables <https://en.wikipedia.org/wiki/Hash_table>`_, which are virtually initialised +such that every possible key exists and is mapped to a value whose +byte-representation is all zeros, a type's :ref:`default value <default-value>`. The similarity ends there, the key data is not stored in a +mapping, only its ``keccak256`` hash is used to look up the value. + +Because of this, mappings do not have a length or a concept of a key or +value being set. + +Mappings can only have a data location of ``storage`` and thus +are allowed for state variables, as storage reference types +in functions, or as parameters for library functions. +They cannot be used as parameters or return parameters +of contract functions that are publicly visible. + +You can mark variables of mapping type as ``public`` and Solidity creates a +:ref:`getter <visibility-and-getters>` for you. The ``_KeyType`` becomes a +parameter for the getter. If ``_ValueType`` is a value type or a struct, +the getter returns ``_ValueType``. +If ``_ValueType`` is an array or a mapping, the getter has one parameter for +each ``_KeyType``, recursively. For example with a mapping: :: - pragma solidity ^0.4.0; + pragma solidity >=0.4.0 <0.6.0; contract MappingExample { mapping(address => uint) public balances; @@ -886,14 +1158,14 @@ for each ``_KeyType``, recursively. function f() public returns (uint) { MappingExample m = new MappingExample(); m.update(100); - return m.balances(this); + return m.balances(address(this)); } } .. note:: - Mappings are not iterable, but it is possible to implement a data structure on top of them. - For an example, see `iterable mapping <https://github.com/ethereum/dapp-bin/blob/master/library/iterable_mapping.sol>`_. + Mappings are not iterable, but it is possible to implement a data structure + on top of them. For an example, see `iterable mapping <https://github.com/ethereum/dapp-bin/blob/master/library/iterable_mapping.sol>`_. .. index:: assignment, ! delete, lvalue @@ -907,15 +1179,17 @@ If ``a`` is an LValue (i.e. a variable or something that can be assigned to), th delete ------ -``delete a`` assigns the initial value for the type to ``a``. I.e. for integers it is equivalent to ``a = 0``, but it can also be used on arrays, where it assigns a dynamic array of length zero or a static array of the same length with all elements reset. For structs, it assigns a struct with all members reset. +``delete a`` assigns the initial value for the type to ``a``. I.e. for integers it is equivalent to ``a = 0``, but it can also be used on arrays, where it assigns a dynamic array of length zero or a static array of the same length with all elements reset. For structs, it assigns a struct with all members reset. In other words, the value of ``a`` after ``delete a`` is the same as if ``a`` would be declared without assignment, with the following caveat: -``delete`` has no effect on whole mappings (as the keys of mappings may be arbitrary and are generally unknown). So if you delete a struct, it will reset all members that are not mappings and also recurse into the members unless they are mappings. However, individual keys and what they map to can be deleted. +``delete`` has no effect on mappings (as the keys of mappings may be arbitrary and are generally unknown). So if you delete a struct, it will reset all members that are not mappings and also recurse into the members unless they are mappings. However, individual keys and what they map to can be deleted: If ``a`` is a mapping, then ``delete a[x]`` will delete the value stored at ``x``. It is important to note that ``delete a`` really behaves like an assignment to ``a``, i.e. it stores a new object in ``a``. +This distinction is visible when ``a`` is reference variable: It will only reset ``a`` itself, not the +value it referred to previously. :: - pragma solidity ^0.4.0; + pragma solidity >=0.4.0 <0.6.0; contract DeleteExample { uint data; @@ -924,17 +1198,20 @@ It is important to note that ``delete a`` really behaves like an assignment to ` function f() public { uint x = data; delete x; // sets x to 0, does not affect data - delete data; // sets data to 0, does not affect x which still holds a copy + delete data; // sets data to 0, does not affect x uint[] storage y = dataArray; delete dataArray; // this sets dataArray.length to zero, but as uint[] is a complex object, also // y is affected which is an alias to the storage object // On the other hand: "delete y" is not valid, as assignments to local variables // referencing storage objects can only be made from existing storage objects. + assert(y.length == 0); } } .. index:: ! type;conversion, ! cast +.. _types-conversion-elementary-types: + Conversions between Elementary Types ==================================== @@ -948,16 +1225,16 @@ is possible if it makes sense semantically and no information is lost: ``uint8`` is convertible to ``uint16`` and ``int128`` to ``int256``, but ``int8`` is not convertible to ``uint256`` (because ``uint256`` cannot hold e.g. ``-1``). -Furthermore, unsigned integers can be converted to bytes of the same or larger -size, but not vice-versa. Any type that can be converted to ``uint160`` can also -be converted to ``address``. + +For more details, please consult the sections about the types themselves. Explicit Conversions -------------------- If the compiler does not allow implicit conversion but you know what you are doing, an explicit type conversion is sometimes possible. Note that this may -give you some unexpected behaviour so be sure to test to ensure that the +give you some unexpected behaviour and allows you to bypass some security +features of the compiler, so be sure to test that the result is what you want! Take the following example where you are converting a negative ``int8`` to a ``uint``: @@ -969,32 +1246,92 @@ a negative ``int8`` to a ``uint``: At the end of this code snippet, ``x`` will have the value ``0xfffff..fd`` (64 hex characters), which is -3 in the two's complement representation of 256 bits. -If a type is explicitly converted to a smaller type, higher-order bits are +If an integer is explicitly converted to a smaller type, higher-order bits are cut off:: uint32 a = 0x12345678; uint16 b = uint16(a); // b will be 0x5678 now -.. index:: ! type;deduction, ! var +If an integer is explicitly converted to a larger type, it is padded on the left (i.e. at the higher order end). +The result of the conversion will compare equal to the original integer. -.. _type-deduction: + uint16 a = 0x1234; + uint32 b = uint32(a); // b will be 0x00001234 now + assert(a == b); -Type Deduction -============== +Fixed-size bytes types behave differently during conversions. They can be thought of as +sequences of individual bytes and converting to a smaller type will cut off the +sequence:: -For convenience, it is not always necessary to explicitly specify the type of a -variable, the compiler automatically infers it from the type of the first -expression that is assigned to the variable:: + bytes2 a = 0x1234; + bytes1 b = bytes1(a); // b will be 0x12 - uint24 x = 0x123; - var y = x; +If a fixed-size bytes type is explicitly converted to a larger type, it is padded on +the right. Accessing the byte at a fixed index will result in the same value before and +after the conversion (if the index is still in range):: -Here, the type of ``y`` will be ``uint24``. Using ``var`` is not possible for function -parameters or return parameters. + bytes2 a = 0x1234; + bytes4 b = bytes4(a); // b will be 0x12340000 + assert(a[0] == b[0]); + assert(a[1] == b[1]); -.. warning:: - The type is only deduced from the first assignment, so - the loop in the following snippet is infinite, as ``i`` will have the type - ``uint8`` and the highest value of this type is smaller than ``2000``. - ``for (var i = 0; i < 2000; i++) { ... }`` +Since integers and fixed-size byte arrays behave differently when truncating or +padding, explicit conversions between integers and fixed-size byte arrays are only allowed, +if both have the same size. If you want to convert between integers and fixed-size byte arrays of +different size, you have to use intermediate conversions that make the desired truncation and padding +rules explicit:: + + bytes2 a = 0x1234; + uint32 b = uint16(a); // b will be 0x00001234 + uint32 c = uint32(bytes4(a)); // c will be 0x12340000 + uint8 d = uint8(uint16(a)); // d will be 0x34 + uint8 e = uint8(bytes1(a)); // d will be 0x12 + +.. _types-conversion-literals: + +Conversions between Literals and Elementary Types +================================================= + +Integer Types +------------- + +Decimal and hexadecimal number literals can be implicitly converted to any integer type +that is large enough to represent it without truncation:: + + uint8 a = 12; // fine + uint32 b = 1234; // fine + uint16 c = 0x123456; // fails, since it would have to truncate to 0x3456 + +Fixed-Size Byte Arrays +---------------------- + +Decimal number literals cannot be implicitly converted to fixed-size byte arrays. Hexadecimal +number literals can be, but only if the number of hex digits exactly fits the size of the bytes +type. As an exception both decimal and hexadecimal literals which have a value of zero can be +converted to any fixed-size bytes type:: + + bytes2 a = 54321; // not allowed + bytes2 b = 0x12; // not allowed + bytes2 c = 0x123; // not allowed + bytes2 d = 0x1234; // fine + bytes2 e = 0x0012; // fine + bytes4 f = 0; // fine + bytes4 g = 0x0; // fine + +String literals and hex string literals can be implicitly converted to fixed-size byte arrays, +if their number of characters matches the size of the bytes type:: + + bytes2 a = hex"1234"; // fine + bytes2 b = "xy"; // fine + bytes2 c = hex"12"; // not allowed + bytes2 d = hex"123"; // not allowed + bytes2 e = "x"; // not allowed + bytes2 f = "xyz"; // not allowed + +Addresses +--------- + +As described in :ref:`address_literals`, hex literals of the correct size that pass the checksum +test are of ``address`` type. No other literals can be implicitly converted to the ``address`` type. +Explicit conversions from ``bytes20`` or any integer type to ``address`` result in ``address payable``. diff --git a/docs/units-and-global-variables.rst b/docs/units-and-global-variables.rst index a6f8ca87..7f62e71e 100644 --- a/docs/units-and-global-variables.rst +++ b/docs/units-and-global-variables.rst @@ -7,15 +7,25 @@ Units and Globally Available Variables Ether Units =========== -A literal number can take a suffix of ``wei``, ``finney``, ``szabo`` or ``ether`` to convert between the subdenominations of Ether, where Ether currency numbers without a postfix are assumed to be Wei, e.g. ``2 ether == 2000 finney`` evaluates to ``true``. +A literal number can take a suffix of ``wei``, ``finney``, ``szabo`` or ``ether`` to specify a subdenomination of Ether, where Ether numbers without a postfix are assumed to be Wei. + +:: + + assert(1 wei == 1); + assert(1 szabo == 1e12); + assert(1 finney == 1e15); + assert(1 ether == 1e18); + +The only effect of the subdenomination suffix is a multiplication by a power of ten. + .. index:: time, seconds, minutes, hours, days, weeks, years Time Units ========== -Suffixes like ``seconds``, ``minutes``, ``hours``, ``days``, ``weeks`` and -``years`` after literal numbers can be used to convert between units of time where seconds are the base +Suffixes like ``seconds``, ``minutes``, ``hours``, ``days`` and ``weeks`` +after literal numbers can be used to specify units of time where seconds are the base unit and units are considered naively in the following way: * ``1 == 1 seconds`` @@ -23,7 +33,6 @@ unit and units are considered naively in the following way: * ``1 hours == 60 minutes`` * ``1 days == 24 hours`` * ``1 weeks == 7 days`` - * ``1 years == 365 days`` Take care if you perform calendar calculations using these units, because not every year equals 365 days and not even every day has 24 hours @@ -32,7 +41,7 @@ Due to the fact that leap seconds cannot be predicted, an exact calendar library has to be updated by an external oracle. .. note:: - The suffix ``years`` has been deprecated due to the reasons above. + The suffix ``years`` has been removed in version 0.5.0 due to the reasons above. These suffixes cannot be applied to variables. If you want to interpret some input variable in e.g. days, you can do it in the following way:: @@ -56,21 +65,20 @@ or are general-use utility functions. Block and Transaction Properties -------------------------------- -- ``block.blockhash(uint blockNumber) returns (bytes32)``: hash of the given block - only works for 256 most recent, excluding current, blocks - deprecated in version 0.4.22 and replaced by ``blockhash(uint blockNumber)``. -- ``block.coinbase`` (``address``): current block miner's address +- ``blockhash(uint blockNumber) returns (bytes32)``: hash of the given block - only works for 256 most recent, excluding current, blocks +- ``block.coinbase`` (``address payable``): current block miner's address - ``block.difficulty`` (``uint``): current block difficulty - ``block.gaslimit`` (``uint``): current block gaslimit - ``block.number`` (``uint``): current block number - ``block.timestamp`` (``uint``): current block timestamp as seconds since unix epoch - ``gasleft() returns (uint256)``: remaining gas -- ``msg.data`` (``bytes``): complete calldata -- ``msg.gas`` (``uint``): remaining gas - deprecated in version 0.4.21 and to be replaced by ``gasleft()`` -- ``msg.sender`` (``address``): sender of the message (current call) +- ``msg.data`` (``bytes calldata``): complete calldata +- ``msg.sender`` (``address payable``): sender of the message (current call) - ``msg.sig`` (``bytes4``): first four bytes of the calldata (i.e. function identifier) - ``msg.value`` (``uint``): number of wei sent with the message - ``now`` (``uint``): current block timestamp (alias for ``block.timestamp``) - ``tx.gasprice`` (``uint``): gas price of the transaction -- ``tx.origin`` (``address``): sender of the transaction (full call chain) +- ``tx.origin`` (``address payable``): sender of the transaction (full call chain) .. note:: The values of all members of ``msg``, including ``msg.sender`` and @@ -94,22 +102,30 @@ Block and Transaction Properties You can only access the hashes of the most recent 256 blocks, all other values will be zero. +.. note:: + The function ``blockhash`` was previously known as ``block.blockhash``. It was deprecated in + version 0.4.22 and removed in version 0.5.0. + +.. note:: + The function ``gasleft`` was previously known as ``msg.gas``. It was deprecated in + version 0.4.21 and removed in version 0.5.0. + .. index:: abi, encoding, packed -ABI Encoding Functions ----------------------- +ABI Encoding and Decoding Functions +----------------------------------- -- ``abi.encode(...) returns (bytes)``: ABI-encodes the given arguments -- ``abi.encodePacked(...) returns (bytes)``: Performes packed encoding of the given arguments -- ``abi.encodeWithSelector(bytes4 selector, ...) returns (bytes)``: ABI-encodes the given arguments - starting from the second and prepends the given four-byte selector -- ``abi.encodeWithSignature(string signature, ...) returns (bytes)``: Equivalent to ``abi.encodeWithSelector(bytes4(keccak256(signature), ...)``` +- ``abi.decode(bytes memory encodedData, (...)) returns (...)``: ABI-decodes the given data, while the types are given in parentheses as second argument. Example: ``(uint a, uint[2] memory b, bytes memory c) = abi.decode(data, (uint, uint[2], bytes))`` +- ``abi.encode(...) returns (bytes memory)``: ABI-encodes the given arguments +- ``abi.encodePacked(...) returns (bytes memory)``: Performs :ref:`packed encoding <abi_packed_mode>` of the given arguments +- ``abi.encodeWithSelector(bytes4 selector, ...) returns (bytes memory)``: ABI-encodes the given arguments starting from the second and prepends the given four-byte selector +- ``abi.encodeWithSignature(string memory signature, ...) returns (bytes memory)``: Equivalent to ``abi.encodeWithSelector(bytes4(keccak256(bytes(signature))), ...)``` .. note:: - These encoding functions can be used to craft data for function calls without actually - calling a function. Furthermore, ``keccak256(abi.encodePacked(a, b))`` is a more - explicit way to compute ``keccak256(a, b)``, which will be deprecated in future - versions. + These encoding functions can be used to craft data for external function calls without actually + calling an external function. Furthermore, ``keccak256(abi.encodePacked(a, b))`` is a way + to compute the hash of structured data (although be aware that it is possible to + craft a "hash collision" using different inputs types). See the documentation about the :ref:`ABI <ABI>` and the :ref:`tightly packed encoding <abi_packed_mode>` for details about the encoding. @@ -119,15 +135,18 @@ See the documentation about the :ref:`ABI <ABI>` and the Error Handling -------------- +See the dedicated section on :ref:`assert and require<assert-and-require>` for +more details on error handling and when to use which function. + ``assert(bool condition)``: - invalidates the transaction if the condition is not met - to be used for internal errors. + causes an invalid opcode and thus state change reversion if the condition is not met - to be used for internal errors. ``require(bool condition)``: reverts if the condition is not met - to be used for errors in inputs or external components. -``require(bool condition, string message)``: +``require(bool condition, string memory message)``: reverts if the condition is not met - to be used for errors in inputs or external components. Also provides an error message. ``revert()``: abort execution and revert state changes -``revert(string reason)``: +``revert(string memory reason)``: abort execution and revert state changes, providing an explanatory string .. index:: keccak256, ripemd160, sha256, ecrecover, addmod, mulmod, cryptography, @@ -139,54 +158,44 @@ Mathematical and Cryptographic Functions compute ``(x + y) % k`` where the addition is performed with arbitrary precision and does not wrap around at ``2**256``. Assert that ``k != 0`` starting from version 0.5.0. ``mulmod(uint x, uint y, uint k) returns (uint)``: compute ``(x * y) % k`` where the multiplication is performed with arbitrary precision and does not wrap around at ``2**256``. Assert that ``k != 0`` starting from version 0.5.0. -``keccak256(...) returns (bytes32)``: - compute the Ethereum-SHA-3 (Keccak-256) hash of the :ref:`(tightly packed) arguments <abi_packed_mode>` -``sha256(...) returns (bytes32)``: - compute the SHA-256 hash of the :ref:`(tightly packed) arguments <abi_packed_mode>` -``sha3(...) returns (bytes32)``: - alias to ``keccak256`` -``ripemd160(...) returns (bytes20)``: - compute RIPEMD-160 hash of the :ref:`(tightly packed) arguments <abi_packed_mode>` +``keccak256(bytes memory) returns (bytes32)``: + compute the Keccak-256 hash of the input +``sha256(bytes memory) returns (bytes32)``: + compute the SHA-256 hash of the input +``ripemd160(bytes memory) returns (bytes20)``: + compute RIPEMD-160 hash of the input ``ecrecover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) returns (address)``: recover the address associated with the public key from elliptic curve signature or return zero on error (`example usage <https://ethereum.stackexchange.com/q/1777/222>`_) -In the above, "tightly packed" means that the arguments are concatenated without padding. -This means that the following are all identical:: - - keccak256("ab", "c") - keccak256("abc") - keccak256(0x616263) - keccak256(6382179) - keccak256(97, 98, 99) - -If padding is needed, explicit type conversions can be used: ``keccak256("\x00\x12")`` is the -same as ``keccak256(uint16(0x12))``. - -Note that constants will be packed using the minimum number of bytes required to store them. -This means that, for example, ``keccak256(0) == keccak256(uint8(0))`` and -``keccak256(0x12345678) == keccak256(uint32(0x12345678))``. +.. note:: + Function ``ecrecover`` returns an ``address``, and not an ``address + payable``. See :ref:`address payable<address>` for conversion, in case you need + to transfer funds to the recovered address. It might be that you run into Out-of-Gas for ``sha256``, ``ripemd160`` or ``ecrecover`` on a *private blockchain*. The reason for this is that those are implemented as so-called precompiled contracts and these contracts only really exist after they received the first message (although their contract code is hardcoded). Messages to non-existing contracts are more expensive and thus the execution runs into an Out-of-Gas error. A workaround for this problem is to first send e.g. 1 Wei to each of the contracts before you use them in your actual contracts. This is not an issue on the official or test net. -.. index:: balance, send, transfer, call, callcode, delegatecall +.. note:: + There used to be an alias for ``keccak256`` called ``sha3``, which was removed in version 0.5.0. + +.. index:: balance, send, transfer, call, callcode, delegatecall, staticcall .. _address_related: -Address Related ---------------- +Members of Address Types +------------------------ ``<address>.balance`` (``uint256``): balance of the :ref:`address` in Wei -``<address>.transfer(uint256 amount)``: - send given amount of Wei to :ref:`address`, throws on failure, forwards 2300 gas stipend, not adjustable -``<address>.send(uint256 amount) returns (bool)``: +``<address payable>.transfer(uint256 amount)``: + send given amount of Wei to :ref:`address`, reverts on failure, forwards 2300 gas stipend, not adjustable +``<address payable>.send(uint256 amount) returns (bool)``: send given amount of Wei to :ref:`address`, returns ``false`` on failure, forwards 2300 gas stipend, not adjustable -``<address>.call(...) returns (bool)``: - issue low-level ``CALL``, returns ``false`` on failure, forwards all available gas, adjustable -``<address>.callcode(...) returns (bool)``: - issue low-level ``CALLCODE``, returns ``false`` on failure, forwards all available gas, adjustable -``<address>.delegatecall(...) returns (bool)``: - issue low-level ``DELEGATECALL``, returns ``false`` on failure, forwards all available gas, adjustable +``<address>.call(bytes memory) returns (bool, bytes memory)``: + issue low-level ``CALL`` with the given payload, returns success condition and return data, forwards all available gas, adjustable +``<address>.delegatecall(bytes memory) returns (bool, bytes memory)``: + issue low-level ``DELEGATECALL`` with the given payload, returns success condition and return data, forwards all available gas, adjustable +``<address>.staticcall(bytes memory) returns (bool, bytes memory)``: + issue low-level ``STATICCALL`` with the given payload, returns success condition and return data, forwards all available gas, adjustable For more information, see the section on :ref:`address`. @@ -197,14 +206,23 @@ For more information, see the section on :ref:`address`. Use a pattern where the recipient withdraws the money. .. note:: - If storage variables are accessed via a low-level delegatecall, the storage layout of the two contracts + Prior to version 0.5.0, Solidity allowed address members to be accessed by a contract instance, for example ``this.balance``. + This is now forbidden and an explicit conversion to address must be done: ``address(this).balance``. + +.. note:: + If state variables are accessed via a low-level delegatecall, the storage layout of the two contracts must align in order for the called contract to correctly access the storage variables of the calling contract by name. This is of course not the case if storage pointers are passed as function arguments as in the case for the high-level libraries. - - + .. note:: - The use of ``callcode`` is discouraged and will be removed in the future. + Prior to version 0.5.0, ``.call``, ``.delegatecall`` and ``.staticcall`` only returned the + success condition and not the return data. + +.. note:: + Prior to version 0.5.0, there was a member called ``callcode`` with similar but slightly different + semantics than ``delegatecall``. + .. index:: this, selfdestruct @@ -214,11 +232,12 @@ Contract Related ``this`` (current contract's type): the current contract, explicitly convertible to :ref:`address` -``selfdestruct(address recipient)``: +``selfdestruct(address payable recipient)``: destroy the current contract, sending its funds to the given :ref:`address` -``suicide(address recipient)``: - deprecated alias to ``selfdestruct`` - Furthermore, all functions of the current contract are callable directly including the current function. +.. note:: + Prior to version 0.5.0, there was a function called ``suicide`` with the same + semantics as ``selfdestruct``. + diff --git a/docs/using-the-compiler.rst b/docs/using-the-compiler.rst index 1d7cb97b..9ba6caa5 100644 --- a/docs/using-the-compiler.rst +++ b/docs/using-the-compiler.rst @@ -10,50 +10,127 @@ Using the Commandline Compiler ****************************** .. note:: - This section doesn't apply to :ref:`solcjs <solcjs>`. + This section does not apply to :ref:`solcjs <solcjs>`, not even if it is used in commandline mode. One of the build targets of the Solidity repository is ``solc``, the solidity commandline compiler. Using ``solc --help`` provides you with an explanation of all options. The compiler can produce various outputs, ranging from simple binaries and assembly over an abstract syntax tree (parse tree) to estimations of gas usage. If you only want to compile a single file, you run it as ``solc --bin sourceFile.sol`` and it will print the binary. If you want to get some of the more advanced output variants of ``solc``, it is probably better to tell it to output everything to separate files using ``solc -o outputDirectory --bin --ast --asm sourceFile.sol``. -Before you deploy your contract, activate the optimizer while compiling using ``solc --optimize --bin sourceFile.sol``. By default, the optimizer will optimize the contract for 200 runs. If you want to optimize for initial contract deployment and get the smallest output, set it to ``--runs=1``. If you expect many transactions and don't care for higher deployment cost and output size, set ``--runs`` to a high number. +Before you deploy your contract, activate the optimizer when compiling using ``solc --optimize --bin sourceFile.sol``. +By default, the optimizer will optimize the contract assuming it is called 200 times across its lifetime. +If you want the initial contract deployment to be cheaper and the later function executions to be more expensive, +set it to ``--runs=1``. If you expect many transactions and do not care for higher deployment cost and +output size, set ``--runs`` to a high number. The commandline compiler will automatically read imported files from the filesystem, but it is also possible to provide path redirects using ``prefix=path`` in the following way: :: - solc github.com/ethereum/dapp-bin/=/usr/local/lib/dapp-bin/ =/usr/local/lib/fallback file.sol + solc github.com/ethereum/dapp-bin/=/usr/local/lib/dapp-bin/ file.sol This essentially instructs the compiler to search for anything starting with -``github.com/ethereum/dapp-bin/`` under ``/usr/local/lib/dapp-bin`` and if it does not -find the file there, it will look at ``/usr/local/lib/fallback`` (the empty prefix -always matches). ``solc`` will not read files from the filesystem that lie outside of +``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. +files reside, so things like ``import "/etc/passwd";`` only work if you add ``/=/`` as a remapping. + +An empty remapping prefix is not allowed. If there are multiple matches due to remappings, the one with the longest common prefix is selected. For security reasons the compiler has restrictions what directories it can access. Paths (and their subdirectories) of source files specified on the commandline and paths defined by remappings are allowed for import statements, but everything else is rejected. Additional paths (and their subdirectories) can be allowed via the ``--allow-paths /sample/path,/another/sample/path`` switch. -If your contracts use :ref:`libraries <libraries>`, you will notice that the bytecode contains substrings of the form ``__LibraryName______``. You can use ``solc`` as a linker meaning that it will insert the library addresses for you at those points: +If your contracts use :ref:`libraries <libraries>`, you will notice that the bytecode contains substrings of the form ``__$53aea86b7d70b31448b230b20ae141a537$__``. These are placeholders for the actual library addresses. +The placeholder is a 34 character prefix of the hex encoding of the keccak256 hash of the fully qualified library name. +The bytecode file will also contain lines of the form ``// <placeholder> -> <fq library name>`` at the end to help +identify which libraries the placeholders represent. Note that the fully qualified library name +is the path of its source file and the library name separated by ``:``. +You can use ``solc`` as a linker meaning that it will insert the library addresses for you at those points: + +Either add ``--libraries "file.sol:Math:0x1234567890123456789012345678901234567890 file.sol:Heap:0xabCD567890123456789012345678901234567890"`` to your command to provide an address for each library or store the string in a file (one library per line) and run ``solc`` using ``--libraries fileName``. + +If ``solc`` is called with the option ``--link``, all input files are interpreted to be unlinked binaries (hex-encoded) in the ``__$53aea86b7d70b31448b230b20ae141a537$__``-format given above and are linked in-place (if the input is read from stdin, it is written to stdout). All options except ``--libraries`` are ignored (including ``-o``) in this case. + +If ``solc`` is called with the option ``--standard-json``, it will expect a JSON input (as explained below) on the standard input, and return a JSON output on the standard output. This is the recommended interface for more complex and especially automated uses. + +.. note:: + The library placeholder used to be the fully qualified name of the library itself + instead of the hash of it. This format is still supported by ``solc --link`` but + the compiler will no longer output it. This change was made to reduce + the likelihood of a collision between libraries, since only the first 36 characters + of the fully qualified library name could be used. + +.. _evm-version: +.. index:: ! EVM version, compile target + +Setting the EVM version to target +********************************* + +When you compile your contract code you can specify the Ethereum virtual machine +version to compile for to avoid particular features or behaviours. + +.. warning:: -Either add ``--libraries "Math:0x12345678901234567890 Heap:0xabcdef0123456"`` to your command to provide an address for each library or store the string in a file (one library per line) and run ``solc`` using ``--libraries fileName``. + Compiling for the wrong EVM version can result in wrong, strange and failing + behaviour. Please ensure, especially if running a private chain, that you + use matching EVM versions. -If ``solc`` is called with the option ``--link``, all input files are interpreted to be unlinked binaries (hex-encoded) in the ``__LibraryName____``-format given above and are linked in-place (if the input is read from stdin, it is written to stdout). All options except ``--libraries`` are ignored (including ``-o``) in this case. +On the command line, you can select the EVM version as follows: -If ``solc`` is called with the option ``--standard-json``, it will expect a JSON input (as explained below) on the standard input, and return a JSON output on the standard output. +.. code-block:: shell + + solc --evm-version <VERSION> contract.sol + +In the :ref:`standard JSON interface <compiler-api>`, use the ``"evmVersion"`` +key in the ``"settings"`` field: + +.. code-block:: none + + { + "sources": { ... }, + "settings": { + "optimizer": { ... }, + "evmVersion": "<VERSION>" + } + } + +Target options +-------------- + +Below is a list of target EVM versions and the compiler-relevant changes introduced +at each version. Backward compatibility is not guaranteed between each version. + +- ``homestead`` (oldest version) +- ``tangerineWhistle`` + - gas cost for access to other accounts increased, relevant for gas estimation and the optimizer. + - all gas sent by default for external calls, previously a certain amount had to be retained. +- ``spuriousDragon`` + - gas cost for the ``exp`` opcode increased, relevant for gas estimation and the optimizer. +- ``byzantium`` (**default**) + - opcodes ``returndatacopy``, ``returndatasize`` and ``staticcall`` are available in assembly. + - the ``staticcall`` opcode is used when calling non-library view or pure functions, which prevents the functions from modifying state at the EVM level, i.e., even applies when you use invalid type conversions. + - it is possible to access dynamic data returned from function calls. + - ``revert`` opcode introduced, which means that ``revert()`` will not waste gas. +- ``constantinople`` (still in progress) + - opcodes ``shl``, ``shr`` and ``sar`` are available in assembly. + - shifting operators use shifting opcodes and thus need less gas. .. _compiler-api: Compiler Input and Output JSON Description ****************************************** -These JSON formats are used by the compiler API as well as are available through ``solc``. These are subject to change, -some fields are optional (as noted), but it is aimed at to only make backwards compatible changes. +The recommended way to interface with the Solidity compiler especially for +more complex and automated setups is the so-called JSON-input-output interface. +The same interface is provided by all distributions of the compiler. + +The fields are generally subject to change, +some are optional (as noted), but we try to only make backwards compatible changes. The compiler API expects a JSON formatted input and outputs the compilation result in a JSON formatted output. +The following subsections describe the format through an example. Comments are of course not permitted and used here only for explanatory purposes. Input Description @@ -62,7 +139,7 @@ Input Description .. code-block:: none { - // Required: Source code language, such as "Solidity", "serpent", "lll", "assembly", etc. + // Required: Source code language, such as "Solidity", "Vyper", "lll", "assembly", etc. language: "Solidity", // Required sources: @@ -82,6 +159,8 @@ Input Description [ "bzzr://56ab...", "ipfs://Qma...", + // If files are used, their directories should be added to the command line via + // `--allow-paths <path>`. "file:///tmp/path/to/file.sol" ] }, diff --git a/docs/utils/SolidityLexer.py b/docs/utils/SolidityLexer.py deleted file mode 100644 index 50f51cf4..00000000 --- a/docs/utils/SolidityLexer.py +++ /dev/null @@ -1,82 +0,0 @@ -# -*- coding: utf-8 -*- - -import re -import copy - -from pygments.lexer import RegexLexer, ExtendedRegexLexer, bygroups, using, \ - include, this -from pygments.token import Text, Comment, Operator, Keyword, Name, String, \ - Number, Other, Punctuation, Literal - -__all__ = ['SolidityLexer'] - -class SolidityLexer(RegexLexer): - name = "Solidity" - aliases = ['sol', 'solidity'] - filenames = ['*.sol'] - mimetypes = [] - flags = re.DOTALL - tokens = { - 'commentsandwhitespace': [ - (r'\s+', Text), - (r'<!--', Comment), - (r'///', Comment.Special, 'docstringsingle'), - (r'//.*?\n', Comment.Single), - (r'/\*\*', Comment.Special, 'docstringmulti'), - (r'/\*.*?\*/', Comment.Multiline) - ], - 'natspec': [ - (r'@author|@dev|@notice|@return|@param|@title', Keyword), - (r'.[^@*\n]*?', Comment.Special) - ], - 'docstringsingle': [ - (r'\n', Comment.Special, '#pop'), - include('natspec') - ], - 'docstringmulti': [ - (r'\*/', Comment.Special, '#pop'), - include('natspec') - ], - 'slashstartsregex': [ - include('commentsandwhitespace'), - (r'/(\\.|[^[/\\\n]|\[(\\.|[^\]\\\n])*])+/' - r'([gim]+\b|\B)', String.Regex, '#pop'), - (r'(?=/)', Text, ('#pop', 'badregex')), - (r'', Text, '#pop') - ], - 'badregex': [ - (r'\n', Text, '#pop') - ], - 'root': [ - (r'^(?=\s|/|<!--)', Text, 'slashstartsregex'), - include('commentsandwhitespace'), - (r'\+\+|--|\*\*|~|&&|\?|:|\|\||\\(?=\n)|' - r'(<<|>>>?|==?|!=?|[-<>+*%&\|\^/])=?', Operator, 'slashstartsregex'), - (r'[{(\[;,]', Punctuation, 'slashstartsregex'), - (r'[})\].]', Punctuation), - (r'(anonymous|as|assembly|break|constant|continue|do|delete|else|external|for|hex|if|' - r'indexed|internal|import|is|mapping|memory|new|payable|public|pragma|' - r'private|pure|return|returns|storage|super|this|throw|using|view|while)\b', Keyword, 'slashstartsregex'), - (r'(var|function|event|modifier|struct|enum|contract|library|interface)\b', Keyword.Declaration, 'slashstartsregex'), - (r'(bytes|string|address|uint|int|bool|byte|' + - '|'.join( - ['uint%d' % (i + 8) for i in range(0, 256, 8)] + - ['int%d' % (i + 8) for i in range(0, 256, 8)] + - ['bytes%d' % (i + 1) for i in range(0, 32)] + - ['ufixed%dx%d' % ((i), (j + 8)) for i in range(0, 256, 8) for j in range(0, 256 - i, 8)] + - ['fixed%dx%d' % ((i), (j + 8)) for i in range(0, 256, 8) for j in range(0, 256 - i, 8)] - ) + r')\b', Keyword.Type, 'slashstartsregex'), - (r'(wei|szabo|finney|ether|seconds|minutes|hours|days|weeks|years)\b', Keyword.Type, 'slashstartsregex'), - (r'(abstract|after|case|catch|default|final|in|inline|let|match|' - r'null|of|relocatable|static|switch|try|type|typeof)\b', Keyword.Reserved), - (r'(true|false)\b', Keyword.Constant), - (r'(block|msg|tx|now|suicide|selfdestruct|addmod|mulmod|sha3|keccak256|log[0-4]|' - r'sha256|ecrecover|ripemd160|assert|revert|require)', Name.Builtin), - (r'[$a-zA-Z_][a-zA-Z0-9_]*', Name.Other), - (r'[0-9][0-9]*\.[0-9]+([eE][0-9]+)?', Number.Float), - (r'0x[0-9a-fA-F]+', Number.Hex), - (r'[0-9]+([eE][0-9]+)?', Number.Integer), - (r'"(\\\\|\\"|[^"])*"', String.Double), - (r"'(\\\\|\\'|[^'])*'", String.Single), - ] - } diff --git a/docs/yul.rst b/docs/yul.rst new file mode 100644 index 00000000..9e9fac8e --- /dev/null +++ b/docs/yul.rst @@ -0,0 +1,604 @@ +### +Yul +### + +.. _yul: + +.. index:: ! assembly, ! asm, ! evmasm, ! yul, julia, iulia + +Yul (previously also called JULIA or IULIA) is an intermediate language that can +compile to various different backends +(EVM 1.0, EVM 1.5 and eWASM are planned). +Because of that, it is designed to be a usable common denominator of all three +platforms. +It can already be used for "inline assembly" inside Solidity and +future versions of the Solidity compiler will even use Yul as intermediate +language. It should also be easy to build high-level optimizer stages for Yul. + +.. note:: + + Note that the flavour used for "inline assembly" does not have types + (everything is ``u256``) and the built-in functions are identical + to the EVM opcodes. Please resort to the inline assembly documentation + for details. + +The core components of Yul are functions, blocks, variables, literals, +for-loops, if-statements, switch-statements, expressions and assignments to variables. + +Yul is typed, both variables and literals must specify the type with postfix +notation. The supported types are ``bool``, ``u8``, ``s8``, ``u32``, ``s32``, +``u64``, ``s64``, ``u128``, ``s128``, ``u256`` and ``s256``. + +Yul in itself does not even provide operators. If the EVM is targeted, +opcodes will be available as built-in functions, but they can be reimplemented +if the backend changes. For a list of mandatory built-in functions, see the section below. + +The following example program assumes that the EVM opcodes ``mul``, ``div`` +and ``mod`` are available either natively or as functions and computes exponentiation. + +.. code:: + + { + function power(base:u256, exponent:u256) -> result:u256 + { + switch exponent + case 0:u256 { result := 1:u256 } + case 1:u256 { result := base } + default + { + result := power(mul(base, base), div(exponent, 2:u256)) + switch mod(exponent, 2:u256) + case 1:u256 { result := mul(base, result) } + } + } + } + +It is also possible to implement the same function using a for-loop +instead of with recursion. Here, we need the EVM opcodes ``lt`` (less-than) +and ``add`` to be available. + +.. code:: + + { + function power(base:u256, exponent:u256) -> result:u256 + { + result := 1:u256 + for { let i := 0:u256 } lt(i, exponent) { i := add(i, 1:u256) } + { + result := mul(result, base) + } + } + } + +Specification of Yul +==================== + +This chapter describes Yul code. It is usually placed inside a Yul object, which is described in the following chapter. + +Grammar:: + + Block = '{' Statement* '}' + Statement = + Block | + FunctionDefinition | + VariableDeclaration | + Assignment | + If | + Expression | + Switch | + ForLoop | + BreakContinue + FunctionDefinition = + 'function' Identifier '(' TypedIdentifierList? ')' + ( '->' TypedIdentifierList )? Block + VariableDeclaration = + 'let' TypedIdentifierList ( ':=' Expression )? + Assignment = + IdentifierList ':=' Expression + Expression = + FunctionCall | Identifier | Literal + If = + 'if' Expression Block + Switch = + 'switch' Expression ( Case+ Default? | Default ) + Case = + 'case' Literal Block + Default = + 'default' Block + ForLoop = + 'for' Block Expression Block Block + BreakContinue = + 'break' | 'continue' + FunctionCall = + Identifier '(' ( Expression ( ',' Expression )* )? ')' + Identifier = [a-zA-Z_$] [a-zA-Z_$0-9]* + IdentifierList = Identifier ( ',' Identifier)* + TypeName = Identifier | BuiltinTypeName + BuiltinTypeName = 'bool' | [us] ( '8' | '32' | '64' | '128' | '256' ) + TypedIdentifierList = Identifier ':' TypeName ( ',' Identifier ':' TypeName )* + Literal = + (NumberLiteral | StringLiteral | HexLiteral | TrueLiteral | FalseLiteral) ':' TypeName + NumberLiteral = HexNumber | DecimalNumber + HexLiteral = 'hex' ('"' ([0-9a-fA-F]{2})* '"' | '\'' ([0-9a-fA-F]{2})* '\'') + StringLiteral = '"' ([^"\r\n\\] | '\\' .)* '"' + TrueLiteral = 'true' + FalseLiteral = 'false' + HexNumber = '0x' [0-9a-fA-F]+ + DecimalNumber = [0-9]+ + +Restrictions on the Grammar +--------------------------- + +Switches must have at least one case (including the default case). +If all possible values of the expression is covered, the default case should +not be allowed (i.e. a switch with a ``bool`` expression and having both a +true and false case should not allow a default case). + +Every expression evaluates to zero or more values. Identifiers and Literals +evaluate to exactly +one value and function calls evaluate to a number of values equal to the +number of return values of the function called. + +In variable declarations and assignments, the right-hand-side expression +(if present) has to evaluate to a number of values equal to the number of +variables on the left-hand-side. +This is the only situation where an expression evaluating +to more than one value is allowed. + +Expressions that are also statements (i.e. at the block level) have to +evaluate to zero values. + +In all other situations, expressions have to evaluate to exactly one value. + +The ``continue`` and ``break`` statements can only be used inside loop bodies +and have to be in the same function as the loop (or both have to be at the +top level). +The condition part of the for-loop has to evaluate to exactly one value. + +Literals cannot be larger than the their type. The largest type defined is 256-bit wide. + +Scoping Rules +------------- + +Scopes in Yul are tied to Blocks (exceptions are functions and the for loop +as explained below) and all declarations +(``FunctionDefinition``, ``VariableDeclaration``) +introduce new identifiers into these scopes. + +Identifiers are visible in +the block they are defined in (including all sub-nodes and sub-blocks). +As an exception, identifiers defined in the "init" part of the for-loop +(the first block) are visible in all other parts of the for-loop +(but not outside of the loop). +Identifiers declared in the other parts of the for loop respect the regular +syntatical scoping rules. +The parameters and return parameters of functions are visible in the +function body and their names cannot overlap. + +Variables can only be referenced after their declaration. In particular, +variables cannot be referenced in the right hand side of their own variable +declaration. +Functions can be referenced already before their declaration (if they are visible). + +Shadowing is disallowed, i.e. you cannot declare an identifier at a point +where another identifier with the same name is also visible, even if it is +not accessible. + +Inside functions, it is not possible to access a variable that was declared +outside of that function. + +Formal Specification +-------------------- + +We formally specify Yul by providing an evaluation function E overloaded +on the various nodes of the AST. Any functions can have side effects, so +E takes two state objects and the AST node and returns two new +state objects and a variable number of other values. +The two state objects are the global state object +(which in the context of the EVM is the memory, storage and state of the +blockchain) and the local state object (the state of local variables, i.e. a +segment of the stack in the EVM). +If the AST node is a statement, E returns the two state objects and a "mode", +which is used for the ``break`` and ``continue`` statements. +If the AST node is an expression, E returns the two state objects and +as many values as the expression evaluates to. + + +The exact nature of the global state is unspecified for this high level +description. The local state ``L`` is a mapping of identifiers ``i`` to values ``v``, +denoted as ``L[i] = v``. + +For an identifier ``v``, let ``$v`` be the name of the identifier. + +We will use a destructuring notation for the AST nodes. + +.. code:: + + E(G, L, <{St1, ..., Stn}>: Block) = + let G1, L1, mode = E(G, L, St1, ..., Stn) + let L2 be a restriction of L1 to the identifiers of L + G1, L2, mode + E(G, L, St1, ..., Stn: Statement) = + if n is zero: + G, L, regular + else: + let G1, L1, mode = E(G, L, St1) + if mode is regular then + E(G1, L1, St2, ..., Stn) + otherwise + G1, L1, mode + E(G, L, FunctionDefinition) = + G, L, regular + E(G, L, <let var1, ..., varn := rhs>: VariableDeclaration) = + E(G, L, <var1, ..., varn := rhs>: Assignment) + E(G, L, <let var1, ..., varn>: VariableDeclaration) = + let L1 be a copy of L where L1[$vari] = 0 for i = 1, ..., n + G, L1, regular + E(G, L, <var1, ..., varn := rhs>: Assignment) = + let G1, L1, v1, ..., vn = E(G, L, rhs) + let L2 be a copy of L1 where L2[$vari] = vi for i = 1, ..., n + G, L2, regular + E(G, L, <for { i1, ..., in } condition post body>: ForLoop) = + if n >= 1: + let G1, L1, mode = E(G, L, i1, ..., in) + // mode has to be regular due to the syntactic restrictions + let G2, L2, mode = E(G1, L1, for {} condition post body) + // mode has to be regular due to the syntactic restrictions + let L3 be the restriction of L2 to only variables of L + G2, L3, regular + else: + let G1, L1, v = E(G, L, condition) + if v is false: + G1, L1, regular + else: + let G2, L2, mode = E(G1, L, body) + if mode is break: + G2, L2, regular + else: + G3, L3, mode = E(G2, L2, post) + E(G3, L3, for {} condition post body) + E(G, L, break: BreakContinue) = + G, L, break + E(G, L, continue: BreakContinue) = + G, L, continue + E(G, L, <if condition body>: If) = + let G0, L0, v = E(G, L, condition) + if v is true: + E(G0, L0, body) + else: + G0, L0, regular + E(G, L, <switch condition case l1:t1 st1 ... case ln:tn stn>: Switch) = + E(G, L, switch condition case l1:t1 st1 ... case ln:tn stn default {}) + E(G, L, <switch condition case l1:t1 st1 ... case ln:tn stn default st'>: Switch) = + let G0, L0, v = E(G, L, condition) + // i = 1 .. n + // Evaluate literals, context doesn't matter + let _, _, v1 = E(G0, L0, l1) + ... + let _, _, vn = E(G0, L0, ln) + if there exists smallest i such that vi = v: + E(G0, L0, sti) + else: + E(G0, L0, st') + + E(G, L, <name>: Identifier) = + G, L, L[$name] + E(G, L, <fname(arg1, ..., argn)>: FunctionCall) = + G1, L1, vn = E(G, L, argn) + ... + G(n-1), L(n-1), v2 = E(G(n-2), L(n-2), arg2) + Gn, Ln, v1 = E(G(n-1), L(n-1), arg1) + Let <function fname (param1, ..., paramn) -> ret1, ..., retm block> + be the function of name $fname visible at the point of the call. + Let L' be a new local state such that + L'[$parami] = vi and L'[$reti] = 0 for all i. + Let G'', L'', mode = E(Gn, L', block) + G'', Ln, L''[$ret1], ..., L''[$retm] + E(G, L, l: HexLiteral) = G, L, hexString(l), + where hexString decodes l from hex and left-aligns it into 32 bytes + E(G, L, l: StringLiteral) = G, L, utf8EncodeLeftAligned(l), + where utf8EncodeLeftAligned performs a utf8 encoding of l + and aligns it left into 32 bytes + E(G, L, n: HexNumber) = G, L, hex(n) + where hex is the hexadecimal decoding function + E(G, L, n: DecimalNumber) = G, L, dec(n), + where dec is the decimal decoding function + +Type Conversion Functions +------------------------- + +Yul has no support for implicit type conversion and therefore functions exist to provide explicit conversion. +When converting a larger type to a shorter type a runtime exception can occur in case of an overflow. + +Truncating conversions are supported between the following types: + - ``bool`` + - ``u32`` + - ``u64`` + - ``u256`` + - ``s256`` + +For each of these a type conversion function exists having the prototype in the form of ``<input_type>to<output_type>(x:<input_type>) -> y:<output_type>``, +such as ``u32tobool(x:u32) -> y:bool``, ``u256tou32(x:u256) -> y:u32`` or ``s256tou256(x:s256) -> y:u256``. + +.. note:: + + ``u32tobool(x:u32) -> y:bool`` can be implemented as ``y := not(iszerou256(x))`` and + ``booltou32(x:bool) -> y:u32`` can be implemented as ``switch x case true:bool { y := 1:u32 } case false:bool { y := 0:u32 }`` + +Low-level Functions +------------------- + +The following functions must be available: + ++---------------------------------------------------------------------------------------------------------------+ +| *Logic* | ++---------------------------------------------+-----------------------------------------------------------------+ +| not(x:bool) -> z:bool | logical not | ++---------------------------------------------+-----------------------------------------------------------------+ +| and(x:bool, y:bool) -> z:bool | logical and | ++---------------------------------------------+-----------------------------------------------------------------+ +| or(x:bool, y:bool) -> z:bool | logical or | ++---------------------------------------------+-----------------------------------------------------------------+ +| xor(x:bool, y:bool) -> z:bool | xor | ++---------------------------------------------+-----------------------------------------------------------------+ +| *Arithmetic* | ++---------------------------------------------+-----------------------------------------------------------------+ +| addu256(x:u256, y:u256) -> z:u256 | x + y | ++---------------------------------------------+-----------------------------------------------------------------+ +| subu256(x:u256, y:u256) -> z:u256 | x - y | ++---------------------------------------------+-----------------------------------------------------------------+ +| mulu256(x:u256, y:u256) -> z:u256 | x * y | ++---------------------------------------------+-----------------------------------------------------------------+ +| divu256(x:u256, y:u256) -> z:u256 | x / y | ++---------------------------------------------+-----------------------------------------------------------------+ +| divs256(x:s256, y:s256) -> z:s256 | x / y, for signed numbers in two's complement | ++---------------------------------------------+-----------------------------------------------------------------+ +| modu256(x:u256, y:u256) -> z:u256 | x % y | ++---------------------------------------------+-----------------------------------------------------------------+ +| mods256(x:s256, y:s256) -> z:s256 | x % y, for signed numbers in two's complement | ++---------------------------------------------+-----------------------------------------------------------------+ +| signextendu256(i:u256, x:u256) -> z:u256 | sign extend from (i*8+7)th bit counting from least significant | ++---------------------------------------------+-----------------------------------------------------------------+ +| expu256(x:u256, y:u256) -> z:u256 | x to the power of y | ++---------------------------------------------+-----------------------------------------------------------------+ +| addmodu256(x:u256, y:u256, m:u256) -> z:u256| (x + y) % m with arbitrary precision arithmetic | ++---------------------------------------------+-----------------------------------------------------------------+ +| mulmodu256(x:u256, y:u256, m:u256) -> z:u256| (x * y) % m with arbitrary precision arithmetic | ++---------------------------------------------+-----------------------------------------------------------------+ +| ltu256(x:u256, y:u256) -> z:bool | true if x < y, false otherwise | ++---------------------------------------------+-----------------------------------------------------------------+ +| gtu256(x:u256, y:u256) -> z:bool | true if x > y, false otherwise | ++---------------------------------------------+-----------------------------------------------------------------+ +| sltu256(x:s256, y:s256) -> z:bool | true if x < y, false otherwise | +| | (for signed numbers in two's complement) | ++---------------------------------------------+-----------------------------------------------------------------+ +| sgtu256(x:s256, y:s256) -> z:bool | true if x > y, false otherwise | +| | (for signed numbers in two's complement) | ++---------------------------------------------+-----------------------------------------------------------------+ +| equ256(x:u256, y:u256) -> z:bool | true if x == y, false otherwise | ++---------------------------------------------+-----------------------------------------------------------------+ +| iszerou256(x:u256) -> z:bool | true if x == 0, false otherwise | ++---------------------------------------------+-----------------------------------------------------------------+ +| notu256(x:u256) -> z:u256 | ~x, every bit of x is negated | ++---------------------------------------------+-----------------------------------------------------------------+ +| andu256(x:u256, y:u256) -> z:u256 | bitwise and of x and y | ++---------------------------------------------+-----------------------------------------------------------------+ +| oru256(x:u256, y:u256) -> z:u256 | bitwise or of x and y | ++---------------------------------------------+-----------------------------------------------------------------+ +| xoru256(x:u256, y:u256) -> z:u256 | bitwise xor of x and y | ++---------------------------------------------+-----------------------------------------------------------------+ +| shlu256(x:u256, y:u256) -> z:u256 | logical left shift of x by y | ++---------------------------------------------+-----------------------------------------------------------------+ +| shru256(x:u256, y:u256) -> z:u256 | logical right shift of x by y | ++---------------------------------------------+-----------------------------------------------------------------+ +| saru256(x:u256, y:u256) -> z:u256 | arithmetic right shift of x by y | ++---------------------------------------------+-----------------------------------------------------------------+ +| byte(n:u256, x:u256) -> v:u256 | nth byte of x, where the most significant byte is the 0th byte | +| | Cannot this be just replaced by and256(shr256(n, x), 0xff) and | +| | let it be optimised out by the EVM backend? | ++---------------------------------------------+-----------------------------------------------------------------+ +| *Memory and storage* | ++---------------------------------------------+-----------------------------------------------------------------+ +| mload(p:u256) -> v:u256 | mem[p..(p+32)) | ++---------------------------------------------+-----------------------------------------------------------------+ +| mstore(p:u256, v:u256) | mem[p..(p+32)) := v | ++---------------------------------------------+-----------------------------------------------------------------+ +| mstore8(p:u256, v:u256) | mem[p] := v & 0xff - only modifies a single byte | ++---------------------------------------------+-----------------------------------------------------------------+ +| sload(p:u256) -> v:u256 | storage[p] | ++---------------------------------------------+-----------------------------------------------------------------+ +| sstore(p:u256, v:u256) | storage[p] := v | ++---------------------------------------------+-----------------------------------------------------------------+ +| msize() -> size:u256 | size of memory, i.e. largest accessed memory index, albeit due | +| | due to the memory extension function, which extends by words, | +| | this will always be a multiple of 32 bytes | ++---------------------------------------------+-----------------------------------------------------------------+ +| *Execution control* | ++---------------------------------------------+-----------------------------------------------------------------+ +| create(v:u256, p:u256, n:u256) | create new contract with code mem[p..(p+n)) and send v wei | +| | and return the new address | ++---------------------------------------------+-----------------------------------------------------------------+ +| create2(v:u256, p:u256, n:u256, s:u256) | create new contract with code mem[p...(p+n)) at address | +| | keccak256(0xff . this . s . keccak256(mem[p...(p+n))) | +| | and send v wei and return the new address, where ``0xff`` is a | +| | 8 byte value, ``this`` is the current contract's address | +| | as a 20 byte value and ``s`` is a big-endian 256-bit value | ++---------------------------------------------+-----------------------------------------------------------------+ +| call(g:u256, a:u256, v:u256, in:u256, | call contract at address a with input mem[in..(in+insize)) | +| insize:u256, out:u256, | providing g gas and v wei and output area | +| outsize:u256) | mem[out..(out+outsize)) returning 0 on error (eg. out of gas) | +| -> r:u256 | and 1 on success | ++---------------------------------------------+-----------------------------------------------------------------+ +| callcode(g:u256, a:u256, v:u256, in:u256, | identical to ``call`` but only use the code from a | +| insize:u256, out:u256, | and stay in the context of the | +| outsize:u256) -> r:u256 | current contract otherwise | ++---------------------------------------------+-----------------------------------------------------------------+ +| delegatecall(g:u256, a:u256, in:u256, | identical to ``callcode``, | +| insize:u256, out:u256, | but also keep ``caller`` | +| outsize:u256) -> r:u256 | and ``callvalue`` | ++---------------------------------------------+-----------------------------------------------------------------+ +| abort() | abort (equals to invalid instruction on EVM) | ++---------------------------------------------+-----------------------------------------------------------------+ +| return(p:u256, s:u256) | end execution, return data mem[p..(p+s)) | ++---------------------------------------------+-----------------------------------------------------------------+ +| revert(p:u256, s:u256) | end execution, revert state changes, return data mem[p..(p+s)) | ++---------------------------------------------+-----------------------------------------------------------------+ +| selfdestruct(a:u256) | end execution, destroy current contract and send funds to a | ++---------------------------------------------+-----------------------------------------------------------------+ +| log0(p:u256, s:u256) | log without topics and data mem[p..(p+s)) | ++---------------------------------------------+-----------------------------------------------------------------+ +| log1(p:u256, s:u256, t1:u256) | log with topic t1 and data mem[p..(p+s)) | ++---------------------------------------------+-----------------------------------------------------------------+ +| log2(p:u256, s:u256, t1:u256, t2:u256) | log with topics t1, t2 and data mem[p..(p+s)) | ++---------------------------------------------+-----------------------------------------------------------------+ +| log3(p:u256, s:u256, t1:u256, t2:u256, | log with topics t, t2, t3 and data mem[p..(p+s)) | +| t3:u256) | | ++---------------------------------------------+-----------------------------------------------------------------+ +| log4(p:u256, s:u256, t1:u256, t2:u256, | log with topics t1, t2, t3, t4 and data mem[p..(p+s)) | +| t3:u256, t4:u256) | | ++---------------------------------------------+-----------------------------------------------------------------+ +| *State queries* | ++---------------------------------------------+-----------------------------------------------------------------+ +| blockcoinbase() -> address:u256 | current mining beneficiary | ++---------------------------------------------+-----------------------------------------------------------------+ +| blockdifficulty() -> difficulty:u256 | difficulty of the current block | ++---------------------------------------------+-----------------------------------------------------------------+ +| blockgaslimit() -> limit:u256 | block gas limit of the current block | ++---------------------------------------------+-----------------------------------------------------------------+ +| blockhash(b:u256) -> hash:u256 | hash of block nr b - only for last 256 blocks excluding current | ++---------------------------------------------+-----------------------------------------------------------------+ +| blocknumber() -> block:u256 | current block number | ++---------------------------------------------+-----------------------------------------------------------------+ +| blocktimestamp() -> timestamp:u256 | timestamp of the current block in seconds since the epoch | ++---------------------------------------------+-----------------------------------------------------------------+ +| txorigin() -> address:u256 | transaction sender | ++---------------------------------------------+-----------------------------------------------------------------+ +| txgasprice() -> price:u256 | gas price of the transaction | ++---------------------------------------------+-----------------------------------------------------------------+ +| gasleft() -> gas:u256 | gas still available to execution | ++---------------------------------------------+-----------------------------------------------------------------+ +| balance(a:u256) -> v:u256 | wei balance at address a | ++---------------------------------------------+-----------------------------------------------------------------+ +| this() -> address:u256 | address of the current contract / execution context | ++---------------------------------------------+-----------------------------------------------------------------+ +| caller() -> address:u256 | call sender (excluding delegatecall) | ++---------------------------------------------+-----------------------------------------------------------------+ +| callvalue() -> v:u256 | wei sent together with the current call | ++---------------------------------------------+-----------------------------------------------------------------+ +| calldataload(p:u256) -> v:u256 | call data starting from position p (32 bytes) | ++---------------------------------------------+-----------------------------------------------------------------+ +| calldatasize() -> v:u256 | size of call data in bytes | ++---------------------------------------------+-----------------------------------------------------------------+ +| calldatacopy(t:u256, f:u256, s:u256) | copy s bytes from calldata at position f to mem at position t | ++---------------------------------------------+-----------------------------------------------------------------+ +| codesize() -> size:u256 | size of the code of the current contract / execution context | ++---------------------------------------------+-----------------------------------------------------------------+ +| codecopy(t:u256, f:u256, s:u256) | copy s bytes from code at position f to mem at position t | ++---------------------------------------------+-----------------------------------------------------------------+ +| extcodesize(a:u256) -> size:u256 | size of the code at address a | ++---------------------------------------------+-----------------------------------------------------------------+ +| extcodecopy(a:u256, t:u256, f:u256, s:u256) | like codecopy(t, f, s) but take code at address a | ++---------------------------------------------+-----------------------------------------------------------------+ +| extcodehash(a:u256) | code hash of address a | ++---------------------------------------------+-----------------------------------------------------------------+ +| *Others* | ++---------------------------------------------+-----------------------------------------------------------------+ +| discard(unused:bool) | discard value | ++---------------------------------------------+-----------------------------------------------------------------+ +| discardu256(unused:u256) | discard value | ++---------------------------------------------+-----------------------------------------------------------------+ +| splitu256tou64(x:u256) -> (x1:u64, x2:u64, | split u256 to four u64's | +| x3:u64, x4:u64) | | ++---------------------------------------------+-----------------------------------------------------------------+ +| combineu64tou256(x1:u64, x2:u64, x3:u64, | combine four u64's into a single u256 | +| x4:u64) -> (x:u256) | | ++---------------------------------------------+-----------------------------------------------------------------+ +| keccak256(p:u256, s:u256) -> v:u256 | keccak(mem[p...(p+s))) | ++---------------------------------------------+-----------------------------------------------------------------+ + +Backends +-------- + +Backends or targets are the translators from Yul to a specific bytecode. Each of the backends can expose functions +prefixed with the name of the backend. We reserve ``evm_`` and ``ewasm_`` prefixes for the two proposed backends. + +Backend: EVM +------------ + +The EVM target will have all the underlying EVM opcodes exposed with the `evm_` prefix. + +Backend: "EVM 1.5" +------------------ + +TBD + +Backend: eWASM +-------------- + +TBD + +Specification of Yul Object +=========================== + +Grammar:: + + TopLevelObject = 'object' '{' Code? ( Object | Data )* '}' + Object = 'object' StringLiteral '{' Code? ( Object | Data )* '}' + Code = 'code' Block + Data = 'data' StringLiteral HexLiteral + HexLiteral = 'hex' ('"' ([0-9a-fA-F]{2})* '"' | '\'' ([0-9a-fA-F]{2})* '\'') + StringLiteral = '"' ([^"\r\n\\] | '\\' .)* '"' + +Above, ``Block`` refers to ``Block`` in the Yul code grammar explained in the previous chapter. + +An example Yul Object is shown below: + +.. code:: + + // Code consists of a single object. A single "code" node is the code of the object. + // Every (other) named object or data section is serialized and + // made accessible to the special built-in functions datacopy / dataoffset / datasize + object { + code { + let size = datasize("runtime") + let offset = allocate(size) + // This will turn into a memory->memory copy for eWASM and + // a codecopy for EVM + datacopy(dataoffset("runtime"), offset, size) + // this is a constructor and the runtime code is returned + return(offset, size) + } + + data "Table2" hex"4123" + + object "runtime" { + code { + // runtime code + + let size = datasize("Contract2") + let offset = allocate(size) + // This will turn into a memory->memory copy for eWASM and + // a codecopy for EVM + datacopy(dataoffset("Contract2"), offset, size) + // constructor parameter is a single number 0x1234 + mstore(add(offset, size), 0x1234) + create(offset, add(size, 32)) + } + + // Embedded object. Use case is that the outside is a factory contract, + // and Contract2 is the code to be created by the factory + object "Contract2" { + code { + // code here ... + } + + object "runtime" { + code { + // code here ... + } + } + + data "Table1" hex"4123" + } + } + } diff --git a/libdevcore/Algorithms.h b/libdevcore/Algorithms.h index b2540668..7fe2472d 100644 --- a/libdevcore/Algorithms.h +++ b/libdevcore/Algorithms.h @@ -32,11 +32,13 @@ template <typename V> class CycleDetector { public: + using Visitor = std::function<void(V const&, CycleDetector&, size_t)>; + /// Initializes the cycle detector /// @param _visit function that is given the current vertex /// and is supposed to call @a run on all /// adjacent vertices. - explicit CycleDetector(std::function<void(V const&, CycleDetector&)> _visit): + explicit CycleDetector(Visitor _visit): m_visit(std::move(_visit)) { } @@ -55,7 +57,7 @@ public: m_processing.insert(&_vertex); m_depth++; - m_visit(_vertex, *this); + m_visit(_vertex, *this, m_depth); m_depth--; if (m_firstCycleVertex && m_depth == 1) m_firstCycleVertex = &_vertex; @@ -66,7 +68,7 @@ public: } private: - std::function<void(V const&, CycleDetector&)> m_visit; + Visitor m_visit; std::set<V const*> m_processing; std::set<V const*> m_processed; size_t m_depth = 0; diff --git a/libdevcore/Common.h b/libdevcore/Common.h index 2543855d..6208424e 100644 --- a/libdevcore/Common.h +++ b/libdevcore/Common.h @@ -40,7 +40,6 @@ #include <libdevcore/vector_ref.h> #if defined(__GNUC__) -#pragma warning(push) #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wunused-parameter" #endif // defined(__GNUC__) @@ -57,7 +56,6 @@ #include <boost/multiprecision/cpp_int.hpp> #if defined(__GNUC__) -#pragma warning(pop) #pragma GCC diagnostic pop #endif // defined(__GNUC__) @@ -66,15 +64,13 @@ #include <functional> #include <string> -using byte = uint8_t; - namespace dev { // Binary data types. -using bytes = std::vector<byte>; -using bytesRef = vector_ref<byte>; -using bytesConstRef = vector_ref<byte const>; +using bytes = std::vector<uint8_t>; +using bytesRef = vector_ref<uint8_t>; +using bytesConstRef = vector_ref<uint8_t const>; // Numeric types. using bigint = boost::multiprecision::number<boost::multiprecision::cpp_int_backend<>>; diff --git a/libdevcore/CommonData.cpp b/libdevcore/CommonData.cpp index 445d11cd..8d2639c9 100644 --- a/libdevcore/CommonData.cpp +++ b/libdevcore/CommonData.cpp @@ -22,7 +22,7 @@ #include <libdevcore/CommonData.h> #include <libdevcore/Exceptions.h> #include <libdevcore/Assertions.h> -#include <libdevcore/SHA3.h> +#include <libdevcore/Keccak256.h> #include <boost/algorithm/string.hpp> @@ -64,7 +64,7 @@ bytes dev::fromHex(std::string const& _s, WhenError _throw) int h = fromHex(_s[i], WhenError::DontThrow); int l = fromHex(_s[i + 1], WhenError::DontThrow); if (h != -1 && l != -1) - ret.push_back((byte)(h * 16 + l)); + ret.push_back((uint8_t)(h * 16 + l)); else if (_throw == WhenError::Throw) BOOST_THROW_EXCEPTION(BadHexCharacter()); else @@ -76,18 +76,18 @@ bytes dev::fromHex(std::string const& _s, WhenError _throw) bool dev::passesAddressChecksum(string const& _str, bool _strict) { - string s = _str.substr(0, 2) == "0x" ? _str.substr(2) : _str; + string s = _str.substr(0, 2) == "0x" ? _str : "0x" + _str; - if (s.length() != 40) + if (s.length() != 42) return false; if (!_strict && ( - _str.find_first_of("abcdef") == string::npos || - _str.find_first_of("ABCDEF") == string::npos + s.find_first_of("abcdef") == string::npos || + s.find_first_of("ABCDEF") == string::npos )) return true; - return _str == dev::getChecksummedAddress(_str); + return s == dev::getChecksummedAddress(s); } string dev::getChecksummedAddress(string const& _addr) @@ -110,3 +110,26 @@ string dev::getChecksummedAddress(string const& _addr) } return ret; } + +bool dev::isValidHex(string const& _string) +{ + if (_string.substr(0, 2) != "0x") + return false; + if (_string.find_first_not_of("0123456789abcdefABCDEF", 2) != string::npos) + return false; + return true; +} + +bool dev::isValidDecimal(string const& _string) +{ + if (_string.empty()) + return false; + if (_string == "0") + return true; + // No leading zeros + if (_string.front() == '0') + return false; + if (_string.find_first_not_of("0123456789") != string::npos) + return false; + return true; +} diff --git a/libdevcore/CommonData.h b/libdevcore/CommonData.h index e410af5c..fedd3af2 100644 --- a/libdevcore/CommonData.h +++ b/libdevcore/CommonData.h @@ -25,11 +25,14 @@ #include <libdevcore/Common.h> +#include <boost/optional.hpp> + #include <vector> #include <type_traits> #include <cstring> #include <string> #include <set> +#include <functional> namespace dev { @@ -85,7 +88,7 @@ inline std::string asString(bytesConstRef _b) /// Converts a string to a byte array containing the string's (byte) data. inline bytes asBytes(std::string const& _b) { - return bytes((byte const*)_b.data(), (byte const*)(_b.data() + _b.size())); + return bytes((uint8_t const*)_b.data(), (uint8_t const*)(_b.data() + _b.size())); } // Big-endian to/from host endian conversion functions. @@ -114,7 +117,7 @@ inline T fromBigEndian(_In const& _bytes) { T ret = (T)0; for (auto i: _bytes) - ret = (T)((ret << 8) | (byte)(typename std::make_unsigned<typename _In::value_type>::type)i); + ret = (T)((ret << 8) | (uint8_t)(typename std::make_unsigned<typename _In::value_type>::type)i); return ret; } inline bytes toBigEndian(u256 _val) { bytes ret(32); toBigEndian(_val, ret); return ret; } @@ -132,7 +135,7 @@ inline bytes toCompactBigEndian(T _val, unsigned _min = 0) toBigEndian(_val, ret); return ret; } -inline bytes toCompactBigEndian(byte _val, unsigned _min = 0) +inline bytes toCompactBigEndian(uint8_t _val, unsigned _min = 0) { return (_min || _val) ? bytes{ _val } : bytes{}; } @@ -229,6 +232,37 @@ bool contains(T const& _t, V const& _v) return std::end(_t) != std::find(std::begin(_t), std::end(_t), _v); } + +/// Function that iterates over a vector, calling a function on each of its +/// elements. If that function returns a vector, the element is replaced by +/// the returned vector. During the iteration, the original vector is only valid +/// on the current element and after that. The actual replacement takes +/// place at the end, but already visited elements might be invalidated. +/// If nothing is replaced, no copy is performed. +template <typename T, typename F> +void iterateReplacing(std::vector<T>& _vector, const F& _f) +{ + // Concept: _f must be Callable, must accept param T&, must return optional<vector<T>> + bool useModified = false; + std::vector<T> modifiedVector; + for (size_t i = 0; i < _vector.size(); ++i) + { + if (boost::optional<std::vector<T>> r = _f(_vector[i])) + { + if (!useModified) + { + std::move(_vector.begin(), _vector.begin() + i, back_inserter(modifiedVector)); + useModified = true; + } + modifiedVector += std::move(*r); + } + else if (useModified) + modifiedVector.emplace_back(std::move(_vector[i])); + } + if (useModified) + _vector = std::move(modifiedVector); +} + /// @returns true iff @a _str passess the hex address checksum test. /// @param _strict if false, hex strings with only uppercase or only lowercase letters /// are considered valid. @@ -238,4 +272,7 @@ bool passesAddressChecksum(std::string const& _str, bool _strict); /// @param hex strings that look like an address std::string getChecksummedAddress(std::string const& _addr); +bool isValidHex(std::string const& _string); +bool isValidDecimal(std::string const& _string); + } diff --git a/libdevcore/CommonIO.cpp b/libdevcore/CommonIO.cpp index 0063a8d4..cc730575 100644 --- a/libdevcore/CommonIO.cpp +++ b/libdevcore/CommonIO.cpp @@ -23,7 +23,6 @@ #include <iostream> #include <cstdlib> #include <fstream> -#include <stdio.h> #if defined(_WIN32) #include <windows.h> #else @@ -81,45 +80,6 @@ string dev::readStandardInput() return ret; } -void dev::writeFile(std::string const& _file, bytesConstRef _data, bool _writeDeleteRename) -{ - namespace fs = boost::filesystem; - if (_writeDeleteRename) - { - fs::path tempPath = fs::unique_path(_file + "-%%%%%%"); - writeFile(tempPath.string(), _data, false); - // will delete _file if it exists - fs::rename(tempPath, _file); - } - else - { - // create directory if not existent - fs::path p(_file); - if (!fs::exists(p.parent_path())) - { - fs::create_directories(p.parent_path()); - try - { - fs::permissions(p.parent_path(), fs::owner_all); - } - catch (...) - { - } - } - - ofstream s(_file, ios::trunc | ios::binary); - s.write(reinterpret_cast<char const*>(_data.data()), _data.size()); - assertThrow(s, FileError, "Could not write to file: " + _file); - try - { - fs::permissions(_file, fs::owner_read|fs::owner_write); - } - catch (...) - { - } - } -} - #if defined(_WIN32) class DisableConsoleBuffering { @@ -187,3 +147,23 @@ boost::filesystem::path dev::weaklyCanonicalFilesystemPath(boost::filesystem::pa return head / tail; } } + +string dev::absolutePath(string const& _path, string const& _reference) +{ + boost::filesystem::path p(_path); + // Anything that does not start with `.` is an absolute path. + if (p.begin() == p.end() || (*p.begin() != "." && *p.begin() != "..")) + return _path; + boost::filesystem::path result(_reference); + result.remove_filename(); + for (boost::filesystem::path::iterator it = p.begin(); it != p.end(); ++it) + if (*it == "..") + result = result.parent_path(); + else if (*it != ".") + result /= *it; + return result.generic_string(); +} + +string dev::sanitizePath(string const& _path) { + return boost::filesystem::path(_path).generic_string(); +} diff --git a/libdevcore/CommonIO.h b/libdevcore/CommonIO.h index 9ba68e74..b9f941ea 100644 --- a/libdevcore/CommonIO.h +++ b/libdevcore/CommonIO.h @@ -41,14 +41,6 @@ std::string readStandardInput(); /// Retrieve and returns a character from standard input (without waiting for EOL). int readStandardInputChar(); -/// Write the given binary data into the given file, replacing the file if it pre-exists. -/// Throws exception on error. -/// @param _writeDeleteRename useful not to lose any data: If set, first writes to another file in -/// the same directory and then moves that file. -void writeFile(std::string const& _file, bytesConstRef _data, bool _writeDeleteRename = false); -/// Write the given binary data into the given file, replacing the file if it pre-exists. -inline void writeFile(std::string const& _file, bytes const& _data, bool _writeDeleteRename = false) { writeFile(_file, bytesConstRef(&_data), _writeDeleteRename); } -inline void writeFile(std::string const& _file, std::string const& _data, bool _writeDeleteRename = false) { writeFile(_file, bytesConstRef(_data), _writeDeleteRename); } /// Converts arbitrary value to string representation using std::stringstream. template <class _T> std::string toString(_T const& _t) @@ -62,4 +54,10 @@ std::string toString(_T const& _t) /// Should be replaced by the boost implementation as soon as support for boost<1.60 can be dropped. boost::filesystem::path weaklyCanonicalFilesystemPath(boost::filesystem::path const &_path); +/// @returns the absolute path corresponding to @a _path relative to @a _reference. +std::string absolutePath(std::string const& _path, std::string const& _reference); + +/// Helper function to return path converted strings. +std::string sanitizePath(std::string const& _path); + } diff --git a/libdevcore/Exceptions.cpp b/libdevcore/Exceptions.cpp index f204dbc2..cff5abf4 100644 --- a/libdevcore/Exceptions.cpp +++ b/libdevcore/Exceptions.cpp @@ -17,8 +17,6 @@ #include <libdevcore/Exceptions.h> -#include <boost/lexical_cast.hpp> - using namespace std; using namespace dev; @@ -27,7 +25,7 @@ char const* Exception::what() const noexcept // Return the comment if available. if (string const* cmt = comment()) return cmt->data(); - + // Fallback to base what(). // Boost accepts nullptr, but the C++ standard doesn't // and crashes on some platforms. @@ -43,7 +41,7 @@ string Exception::lineInfo() const ret += *file; ret += ':'; if (line) - ret += boost::lexical_cast<string>(*line); + ret += to_string(*line); return ret; } diff --git a/libdevcore/FixedHash.h b/libdevcore/FixedHash.h index cd6e1da1..24b89840 100644 --- a/libdevcore/FixedHash.h +++ b/libdevcore/FixedHash.h @@ -79,7 +79,7 @@ public: operator Arith() const { return fromBigEndian<Arith>(m_data); } /// @returns true iff this is the empty hash. - explicit operator bool() const { return std::any_of(m_data.begin(), m_data.end(), [](byte _b) { return _b != 0; }); } + explicit operator bool() const { return std::any_of(m_data.begin(), m_data.end(), [](uint8_t _b) { return _b != 0; }); } // The obvious comparison operators. bool operator==(FixedHash const& _c) const { return m_data == _c.m_data; } @@ -90,9 +90,9 @@ public: FixedHash operator~() const { FixedHash ret; for (unsigned i = 0; i < N; ++i) ret[i] = ~m_data[i]; return ret; } /// @returns a particular byte from the hash. - byte& operator[](unsigned _i) { return m_data[_i]; } + uint8_t& operator[](unsigned _i) { return m_data[_i]; } /// @returns a particular byte from the hash. - byte operator[](unsigned _i) const { return m_data[_i]; } + uint8_t operator[](unsigned _i) const { return m_data[_i]; } /// @returns the hash as a user-readable hex string. std::string hex() const { return toHex(ref()); } @@ -104,19 +104,19 @@ public: bytesConstRef ref() const { return bytesConstRef(m_data.data(), N); } /// @returns a mutable byte pointer to the object's data. - byte* data() { return m_data.data(); } + uint8_t* data() { return m_data.data(); } /// @returns a constant byte pointer to the object's data. - byte const* data() const { return m_data.data(); } + uint8_t const* data() const { return m_data.data(); } /// @returns a copy of the object's data as a byte vector. bytes asBytes() const { return bytes(data(), data() + N); } /// @returns a mutable reference to the object's data as an STL array. - std::array<byte, N>& asArray() { return m_data; } + std::array<uint8_t, N>& asArray() { return m_data; } /// @returns a constant reference to the object's data as an STL array. - std::array<byte, N> const& asArray() const { return m_data; } + std::array<uint8_t, N> const& asArray() const { return m_data; } /// Returns the index of the first bit set to one, or size() * 8 if no bits are set. inline unsigned firstBitSet() const @@ -137,7 +137,7 @@ public: void clear() { m_data.fill(0); } private: - std::array<byte, N> m_data; ///< The binary data. + std::array<uint8_t, N> m_data; ///< The binary data. }; /// Stream I/O for the FixedHash class. diff --git a/libdevcore/Keccak256.cpp b/libdevcore/Keccak256.cpp new file mode 100644 index 00000000..7933fc7e --- /dev/null +++ b/libdevcore/Keccak256.cpp @@ -0,0 +1,186 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see <http://www.gnu.org/licenses/>. +*/ +/** @file SHA3.cpp + * @author Gav Wood <i@gavwood.com> + * @date 2014 + */ + +#include <libdevcore/Keccak256.h> + +#include <cstdint> +#include <cstdio> +#include <cstdlib> +#include <cstring> + +using namespace std; +using namespace dev; + +namespace dev +{ + +namespace +{ + +/** libkeccak-tiny + * + * A single-file implementation of SHA-3 and SHAKE. + * + * Implementor: David Leon Gil + * License: CC0, attribution kindly requested. Blame taken too, + * but not liability. + */ + +/******** The Keccak-f[1600] permutation ********/ + +/*** Constants. ***/ +static const uint8_t rho[24] = \ + { 1, 3, 6, 10, 15, 21, + 28, 36, 45, 55, 2, 14, + 27, 41, 56, 8, 25, 43, + 62, 18, 39, 61, 20, 44}; +static const uint8_t pi[24] = \ + {10, 7, 11, 17, 18, 3, + 5, 16, 8, 21, 24, 4, + 15, 23, 19, 13, 12, 2, + 20, 14, 22, 9, 6, 1}; +static const uint64_t RC[24] = \ + {1ULL, 0x8082ULL, 0x800000000000808aULL, 0x8000000080008000ULL, + 0x808bULL, 0x80000001ULL, 0x8000000080008081ULL, 0x8000000000008009ULL, + 0x8aULL, 0x88ULL, 0x80008009ULL, 0x8000000aULL, + 0x8000808bULL, 0x800000000000008bULL, 0x8000000000008089ULL, 0x8000000000008003ULL, + 0x8000000000008002ULL, 0x8000000000000080ULL, 0x800aULL, 0x800000008000000aULL, + 0x8000000080008081ULL, 0x8000000000008080ULL, 0x80000001ULL, 0x8000000080008008ULL}; + +/*** Helper macros to unroll the permutation. ***/ +#define rol(x, s) (((x) << s) | ((x) >> (64 - s))) +#define REPEAT6(e) e e e e e e +#define REPEAT24(e) REPEAT6(e e e e) +#define REPEAT5(e) e e e e e +#define FOR5(v, s, e) \ + v = 0; \ + REPEAT5(e; v += s;) + +/*** Keccak-f[1600] ***/ +static inline void keccakf(void* state) { + uint64_t* a = (uint64_t*)state; + uint64_t b[5] = {0}; + + for (int i = 0; i < 24; i++) + { + uint8_t x, y; + // Theta + FOR5(x, 1, + b[x] = 0; + FOR5(y, 5, + b[x] ^= a[x + y]; )) + FOR5(x, 1, + FOR5(y, 5, + a[y + x] ^= b[(x + 4) % 5] ^ rol(b[(x + 1) % 5], 1); )) + // Rho and pi + uint64_t t = a[1]; + x = 0; + REPEAT24(b[0] = a[pi[x]]; + a[pi[x]] = rol(t, rho[x]); + t = b[0]; + x++; ) + // Chi + FOR5(y, + 5, + FOR5(x, 1, + b[x] = a[y + x];) + FOR5(x, 1, + a[y + x] = b[x] ^ ((~b[(x + 1) % 5]) & b[(x + 2) % 5]); )) + // Iota + a[0] ^= RC[i]; + } +} + +/******** The FIPS202-defined functions. ********/ + +/*** Some helper macros. ***/ + +#define _(S) do { S } while (0) +#define FOR(i, ST, L, S) \ + _(for (size_t i = 0; i < L; i += ST) { S; }) +#define mkapply_ds(NAME, S) \ + static inline void NAME(uint8_t* dst, \ + const uint8_t* src, \ + size_t len) { \ + FOR(i, 1, len, S); \ + } +#define mkapply_sd(NAME, S) \ + static inline void NAME(const uint8_t* src, \ + uint8_t* dst, \ + size_t len) { \ + FOR(i, 1, len, S); \ + } + +mkapply_ds(xorin, dst[i] ^= src[i]) // xorin +mkapply_sd(setout, dst[i] = src[i]) // setout + +#define P keccakf +#define Plen 200 + +// Fold P*F over the full blocks of an input. +#define foldP(I, L, F) \ + while (L >= rate) { \ + F(a, I, rate); \ + P(a); \ + I += rate; \ + L -= rate; \ + } + +/** The sponge-based hash construction. **/ +inline void hash( + uint8_t* out, + size_t outlen, + const uint8_t* in, + size_t inlen, + size_t rate, + uint8_t delim +) +{ + uint8_t a[Plen] = {0}; + // Absorb input. + foldP(in, inlen, xorin); + // Xor in the DS and pad frame. + a[inlen] ^= delim; + a[rate - 1] ^= 0x80; + // Xor in the last block. + xorin(a, in, inlen); + // Apply P + P(a); + // Squeeze output. + foldP(out, outlen, setout); + setout(a, out, outlen); + memset(a, 0, 200); +} + +} + +h256 keccak256(bytesConstRef _input) +{ + h256 output; + // Parameters used: + // The 0x01 is the specific padding for keccak (sha3 uses 0x06) and + // the way the round size (or window or whatever it was) is calculated. + // 200 - (256 / 4) is the "rate" + hash(output.data(), output.size, _input.data(), _input.size(), 200 - (256 / 4), 0x01); + return output; +} + +} diff --git a/libdevcore/Keccak256.h b/libdevcore/Keccak256.h new file mode 100644 index 00000000..0d5f69bb --- /dev/null +++ b/libdevcore/Keccak256.h @@ -0,0 +1,45 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see <http://www.gnu.org/licenses/>. +*/ +/** @file SHA3.h + * @author Gav Wood <i@gavwood.com> + * @date 2014 + * + * The FixedHash fixed-size "hash" container type. + */ + +#pragma once + +#include <libdevcore/FixedHash.h> + +#include <string> + +namespace dev +{ + +/// Calculate Keccak-256 hash of the given input, returning as a 256-bit hash. +h256 keccak256(bytesConstRef _input); + +/// Calculate Keccak-256 hash of the given input, returning as a 256-bit hash. +inline h256 keccak256(bytes const& _input) { return keccak256(bytesConstRef(&_input)); } + +/// Calculate Keccak-256 hash of the given input (presented as a binary-filled string), returning as a 256-bit hash. +inline h256 keccak256(std::string const& _input) { return keccak256(bytesConstRef(_input)); } + +/// Calculate Keccak-256 hash of the given input (presented as a FixedHash), returns a 256-bit hash. +template<unsigned N> inline h256 keccak256(FixedHash<N> const& _input) { return keccak256(_input.ref()); } + +} diff --git a/libdevcore/SHA3.cpp b/libdevcore/SHA3.cpp deleted file mode 100644 index b0e40ccb..00000000 --- a/libdevcore/SHA3.cpp +++ /dev/null @@ -1,240 +0,0 @@ -/* - This file is part of solidity. - - solidity is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - solidity is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with solidity. If not, see <http://www.gnu.org/licenses/>. -*/ -/** @file SHA3.cpp - * @author Gav Wood <i@gavwood.com> - * @date 2014 - */ - -#include "SHA3.h" -#include <cstdint> -#include <cstdio> -#include <cstdlib> -#include <cstring> - -using namespace std; -using namespace dev; - -namespace dev -{ - -namespace keccak -{ - -/** libkeccak-tiny - * - * A single-file implementation of SHA-3 and SHAKE. - * - * Implementor: David Leon Gil - * License: CC0, attribution kindly requested. Blame taken too, - * but not liability. - */ - -#define decshake(bits) \ - int shake##bits(uint8_t*, size_t, const uint8_t*, size_t); - -#define decsha3(bits) \ - int sha3_##bits(uint8_t*, size_t, const uint8_t*, size_t); - -#define deckeccak(bits) \ - int keccak##bits(uint8_t*, size_t, const uint8_t*, size_t); - -decshake(128) -decshake(256) -decsha3(224) -decsha3(256) -decsha3(384) -decsha3(512) -deckeccak(224) -deckeccak(256) -deckeccak(384) -deckeccak(512) - -/******** The Keccak-f[1600] permutation ********/ - -/*** Constants. ***/ -static const uint8_t rho[24] = \ - { 1, 3, 6, 10, 15, 21, - 28, 36, 45, 55, 2, 14, - 27, 41, 56, 8, 25, 43, - 62, 18, 39, 61, 20, 44}; -static const uint8_t pi[24] = \ - {10, 7, 11, 17, 18, 3, - 5, 16, 8, 21, 24, 4, - 15, 23, 19, 13, 12, 2, - 20, 14, 22, 9, 6, 1}; -static const uint64_t RC[24] = \ - {1ULL, 0x8082ULL, 0x800000000000808aULL, 0x8000000080008000ULL, - 0x808bULL, 0x80000001ULL, 0x8000000080008081ULL, 0x8000000000008009ULL, - 0x8aULL, 0x88ULL, 0x80008009ULL, 0x8000000aULL, - 0x8000808bULL, 0x800000000000008bULL, 0x8000000000008089ULL, 0x8000000000008003ULL, - 0x8000000000008002ULL, 0x8000000000000080ULL, 0x800aULL, 0x800000008000000aULL, - 0x8000000080008081ULL, 0x8000000000008080ULL, 0x80000001ULL, 0x8000000080008008ULL}; - -/*** Helper macros to unroll the permutation. ***/ -#define rol(x, s) (((x) << s) | ((x) >> (64 - s))) -#define REPEAT6(e) e e e e e e -#define REPEAT24(e) REPEAT6(e e e e) -#define REPEAT5(e) e e e e e -#define FOR5(v, s, e) \ - v = 0; \ - REPEAT5(e; v += s;) - -/*** Keccak-f[1600] ***/ -static inline void keccakf(void* state) { - uint64_t* a = (uint64_t*)state; - uint64_t b[5] = {0}; - - for (int i = 0; i < 24; i++) { - uint8_t x, y; - // Theta - FOR5(x, 1, - b[x] = 0; - FOR5(y, 5, - b[x] ^= a[x + y]; )) - FOR5(x, 1, - FOR5(y, 5, - a[y + x] ^= b[(x + 4) % 5] ^ rol(b[(x + 1) % 5], 1); )) - // Rho and pi - uint64_t t = a[1]; - x = 0; - REPEAT24(b[0] = a[pi[x]]; - a[pi[x]] = rol(t, rho[x]); - t = b[0]; - x++; ) - // Chi - FOR5(y, - 5, - FOR5(x, 1, - b[x] = a[y + x];) - FOR5(x, 1, - a[y + x] = b[x] ^ ((~b[(x + 1) % 5]) & b[(x + 2) % 5]); )) - // Iota - a[0] ^= RC[i]; - } -} - -/******** The FIPS202-defined functions. ********/ - -/*** Some helper macros. ***/ - -#define _(S) do { S } while (0) -#define FOR(i, ST, L, S) \ - _(for (size_t i = 0; i < L; i += ST) { S; }) -#define mkapply_ds(NAME, S) \ - static inline void NAME(uint8_t* dst, \ - const uint8_t* src, \ - size_t len) { \ - FOR(i, 1, len, S); \ - } -#define mkapply_sd(NAME, S) \ - static inline void NAME(const uint8_t* src, \ - uint8_t* dst, \ - size_t len) { \ - FOR(i, 1, len, S); \ - } - -mkapply_ds(xorin, dst[i] ^= src[i]) // xorin -mkapply_sd(setout, dst[i] = src[i]) // setout - -#define P keccakf -#define Plen 200 - -// Fold P*F over the full blocks of an input. -#define foldP(I, L, F) \ - while (L >= rate) { \ - F(a, I, rate); \ - P(a); \ - I += rate; \ - L -= rate; \ - } - -/** The sponge-based hash construction. **/ -static inline int hash(uint8_t* out, size_t outlen, - const uint8_t* in, size_t inlen, - size_t rate, uint8_t delim) { - if ((out == NULL) || ((in == NULL) && inlen != 0) || (rate >= Plen)) { - return -1; - } - uint8_t a[Plen] = {0}; - // Absorb input. - foldP(in, inlen, xorin); - // Xor in the DS and pad frame. - a[inlen] ^= delim; - a[rate - 1] ^= 0x80; - // Xor in the last block. - xorin(a, in, inlen); - // Apply P - P(a); - // Squeeze output. - foldP(out, outlen, setout); - setout(a, out, outlen); - memset(a, 0, 200); - return 0; -} - -/*** Helper macros to define SHA3 and SHAKE instances. ***/ -#define defshake(bits) \ - int shake##bits(uint8_t* out, size_t outlen, \ - const uint8_t* in, size_t inlen) { \ - return hash(out, outlen, in, inlen, 200 - (bits / 4), 0x1f); \ - } -#define defsha3(bits) \ - int sha3_##bits(uint8_t* out, size_t outlen, \ - const uint8_t* in, size_t inlen) { \ - if (outlen > (bits/8)) { \ - return -1; \ - } \ - return hash(out, outlen, in, inlen, 200 - (bits / 4), 0x06); \ - } -#define defkeccak(bits) \ - int keccak##bits(uint8_t* out, size_t outlen, \ - const uint8_t* in, size_t inlen) { \ - if (outlen > (bits/8)) { \ - return -1; \ - } \ - return hash(out, outlen, in, inlen, 200 - (bits / 4), 0x01); \ - } - -/*** FIPS202 SHAKE VOFs ***/ -defshake(128) -defshake(256) - -/*** FIPS202 SHA3 FOFs ***/ -defsha3(224) -defsha3(256) -defsha3(384) -defsha3(512) - -/*** KECCAK FOFs ***/ -defkeccak(224) -defkeccak(256) -defkeccak(384) -defkeccak(512) - -} - -bool keccak256(bytesConstRef _input, bytesRef o_output) -{ - // FIXME: What with unaligned memory? - if (o_output.size() != 32) - return false; - keccak::keccak256(o_output.data(), 32, _input.data(), _input.size()); -// keccak::keccak(ret.data(), 32, (uint64_t const*)_input.data(), _input.size()); - return true; -} - -} diff --git a/libdevcore/SHA3.h b/libdevcore/SHA3.h deleted file mode 100644 index d1e2cc98..00000000 --- a/libdevcore/SHA3.h +++ /dev/null @@ -1,51 +0,0 @@ -/* - This file is part of solidity. - - solidity is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - solidity is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with solidity. If not, see <http://www.gnu.org/licenses/>. -*/ -/** @file SHA3.h - * @author Gav Wood <i@gavwood.com> - * @date 2014 - * - * The FixedHash fixed-size "hash" container type. - */ - -#pragma once - -#include <libdevcore/FixedHash.h> - -#include <string> - -namespace dev -{ - -// Keccak-256 convenience routines. - -/// Calculate Keccak-256 hash of the given input and load it into the given output. -/// @returns false if o_output.size() != 32. -bool keccak256(bytesConstRef _input, bytesRef o_output); - -/// Calculate Keccak-256 hash of the given input, returning as a 256-bit hash. -inline h256 keccak256(bytesConstRef _input) { h256 ret; keccak256(_input, ret.ref()); return ret; } - -/// Calculate Keccak-256 hash of the given input, returning as a 256-bit hash. -inline h256 keccak256(bytes const& _input) { return keccak256(bytesConstRef(&_input)); } - -/// Calculate Keccak-256 hash of the given input (presented as a binary-filled string), returning as a 256-bit hash. -inline h256 keccak256(std::string const& _input) { return keccak256(bytesConstRef(_input)); } - -/// Calculate Keccak-256 hash of the given input (presented as a FixedHash), returns a 256-bit hash. -template<unsigned N> inline h256 keccak256(FixedHash<N> const& _input) { return keccak256(_input.ref()); } - -} diff --git a/libdevcore/StringUtils.cpp b/libdevcore/StringUtils.cpp index 2ff86bd5..50bf7cce 100644 --- a/libdevcore/StringUtils.cpp +++ b/libdevcore/StringUtils.cpp @@ -29,13 +29,16 @@ using namespace std; using namespace dev; -bool dev::stringWithinDistance(string const& _str1, string const& _str2, size_t _maxDistance) +bool dev::stringWithinDistance(string const& _str1, string const& _str2, size_t _maxDistance, size_t _lenThreshold) { if (_str1 == _str2) return true; size_t n1 = _str1.size(); size_t n2 = _str2.size(); + if (_lenThreshold > 0 && n1 * n2 > _lenThreshold) + return false; + size_t distance = stringDistance(_str1, _str2); // if distance is not greater than _maxDistance, and distance is strictly less than length of both names, they can be considered similar @@ -85,17 +88,11 @@ size_t dev::stringDistance(string const& _str1, string const& _str2) string dev::quotedAlternativesList(vector<string> const& suggestions) { - if (suggestions.empty()) - return ""; - if (suggestions.size() == 1) - return "\"" + suggestions.front() + "\""; - - string choices = "\"" + suggestions.front() + "\""; - for (size_t i = 1; i + 1 < suggestions.size(); ++i) - choices += ", \"" + suggestions[i] + "\""; + vector<string> quotedSuggestions; - choices += " or \"" + suggestions.back() + "\""; + for (auto& suggestion: suggestions) + quotedSuggestions.push_back("\"" + suggestion + "\""); - return choices; + return joinHumanReadable(quotedSuggestions, ", ", " or "); } diff --git a/libdevcore/StringUtils.h b/libdevcore/StringUtils.h index acd93e32..b02b9d12 100644 --- a/libdevcore/StringUtils.h +++ b/libdevcore/StringUtils.h @@ -30,10 +30,46 @@ namespace dev { // Calculates the Damerau–Levenshtein distance between _str1 and _str2 and returns true if that distance is not greater than _maxDistance -bool stringWithinDistance(std::string const& _str1, std::string const& _str2, size_t _maxDistance); +// if _lenThreshold > 0 and the product of the strings length is greater than _lenThreshold, the function will return false +bool stringWithinDistance(std::string const& _str1, std::string const& _str2, size_t _maxDistance, size_t _lenThreshold = 0); // Calculates the Damerau–Levenshtein distance between _str1 and _str2 size_t stringDistance(std::string const& _str1, std::string const& _str2); // Return a string having elements of suggestions as quoted, alternative suggestions. e.g. "a", "b" or "c" std::string quotedAlternativesList(std::vector<std::string> const& suggestions); +/// Joins collection of strings into one string with separators between, last separator can be different. +/// @param _list collection of strings to join +/// @param _separator defaults to ", " +/// @param _lastSeparator (optional) will be used to separate last two strings instead of _separator +/// @example join(vector<string>{"a", "b", "c"}, "; ", " or ") == "a; b or c" +template<class T> +std::string joinHumanReadable +( + T const& _list, + std::string const& _separator = ", ", + std::string const& _lastSeparator = "" +) +{ + auto const itEnd = end(_list); + + std::string result; + + for (auto it = begin(_list); it != itEnd; ) + { + std::string element = *it; + bool first = (it == begin(_list)); + ++it; + if (!first) + { + if (it == itEnd && !_lastSeparator.empty()) + result += _lastSeparator; // last iteration + else + result += _separator; + } + result += std::move(element); + } + + return result; +} + } diff --git a/libdevcore/SwarmHash.cpp b/libdevcore/SwarmHash.cpp index 1c718200..3b8d2f3e 100644 --- a/libdevcore/SwarmHash.cpp +++ b/libdevcore/SwarmHash.cpp @@ -19,7 +19,7 @@ #include <libdevcore/SwarmHash.h> -#include <libdevcore/SHA3.h> +#include <libdevcore/Keccak256.h> using namespace std; using namespace dev; diff --git a/libdevcore/Visitor.h b/libdevcore/Visitor.h new file mode 100644 index 00000000..4030c928 --- /dev/null +++ b/libdevcore/Visitor.h @@ -0,0 +1,128 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see <http://www.gnu.org/licenses/>. +*/ +/** + * Visitor templates. + */ + +#pragma once + +#include <functional> +#include <boost/variant/static_visitor.hpp> + +namespace dev +{ + +/// Generic visitor used as follows: +/// boost::apply_visitor(GenericVisitor<Class1, Class2>( +/// [](Class1& _c) { _c.f(); }, +/// [](Class2& _c) { _c.g(); } +/// ), variant); +/// This one does not have a fallback and will fail at +/// compile-time if you do not specify all variants. + +template <class...> +struct GenericVisitor{}; + +template <class Visitable, class... Others> +struct GenericVisitor<Visitable, Others...>: public GenericVisitor<Others...> +{ + using GenericVisitor<Others...>::operator (); + explicit GenericVisitor( + std::function<void(Visitable&)> _visitor, + std::function<void(Others&)>... _otherVisitors + ): + GenericVisitor<Others...>(std::move(_otherVisitors)...), + m_visitor(std::move(_visitor)) + {} + + void operator()(Visitable& _v) const { m_visitor(_v); } + + std::function<void(Visitable&)> m_visitor; +}; +template <> +struct GenericVisitor<>: public boost::static_visitor<> { + void operator()() const {} +}; + +/// Generic visitor with fallback: +/// boost::apply_visitor(GenericFallbackVisitor<Class1, Class2>( +/// [](Class1& _c) { _c.f(); }, +/// [](Class2& _c) { _c.g(); } +/// ), variant); +/// This one DOES have a fallback and will NOT fail at +/// compile-time if you do not specify all variants. + +template <class...> +struct GenericFallbackVisitor{}; + +template <class Visitable, class... Others> +struct GenericFallbackVisitor<Visitable, Others...>: public GenericFallbackVisitor<Others...> +{ + explicit GenericFallbackVisitor( + std::function<void(Visitable&)> _visitor, + std::function<void(Others&)>... _otherVisitors + ): + GenericFallbackVisitor<Others...>(std::move(_otherVisitors)...), + m_visitor(std::move(_visitor)) + {} + + using GenericFallbackVisitor<Others...>::operator (); + void operator()(Visitable& _v) const { m_visitor(_v); } + + std::function<void(Visitable&)> m_visitor; +}; +template <> +struct GenericFallbackVisitor<>: public boost::static_visitor<> { + template <class T> + void operator()(T&) const { } +}; + +/// Generic visitor with fallback that can return a value: +/// boost::apply_visitor(GenericFallbackReturnsVisitor<ReturnType, Class1, Class2>( +/// [](Class1& _c) { return _c.f(); }, +/// [](Class2& _c) { return _c.g(); } +/// ), variant); +/// This one DOES have a fallback and will NOT fail at +/// compile-time if you do not specify all variants. +/// The fallback {}-constructs the return value. + +template <class R, class...> +struct GenericFallbackReturnsVisitor{}; + +template <class R, class Visitable, class... Others> +struct GenericFallbackReturnsVisitor<R, Visitable, Others...>: public GenericFallbackReturnsVisitor<R, Others...> +{ + explicit GenericFallbackReturnsVisitor( + std::function<R(Visitable&)> _visitor, + std::function<R(Others&)>... _otherVisitors + ): + GenericFallbackReturnsVisitor<R, Others...>(std::move(_otherVisitors)...), + m_visitor(std::move(_visitor)) + {} + + using GenericFallbackReturnsVisitor<R, Others...>::operator (); + R operator()(Visitable& _v) const { return m_visitor(_v); } + + std::function<R(Visitable&)> m_visitor; +}; +template <class R> +struct GenericFallbackReturnsVisitor<R>: public boost::static_visitor<R> { + template <class T> + R operator()(T&) const { return {}; } +}; + +} diff --git a/libdevcore/boost_multiprecision_number_compare_bug_workaround.hpp b/libdevcore/boost_multiprecision_number_compare_bug_workaround.hpp index dae591df..2568e17d 100644 --- a/libdevcore/boost_multiprecision_number_compare_bug_workaround.hpp +++ b/libdevcore/boost_multiprecision_number_compare_bug_workaround.hpp @@ -1,8 +1,8 @@ -// This is a copy of boost/multiprecision/detail/number_compare.hpp from boost 1.59 to replace buggy version from 1.58. +// This is a copy of boost/multiprecision/detail/number_compare.hpp from boost 1.59 to replace buggy version from 1.58. #ifdef BOOST_MP_COMPARE_HPP -#error This bug workaround header must be included before original boost/multiprecision/detail/number_compare.hpp +#error This bug workaround header must be included before original boost/multiprecision/detail/number_compare.hpp #endif /////////////////////////////////////////////////////////////////////////////// @@ -150,11 +150,11 @@ template <class B, expression_template_option ET> struct is_valid_mixed_compare<number<B, ET>, number<B, ET> > : public mpl::false_ {}; template <class B, expression_template_option ET, class tag, class Arg1, class Arg2, class Arg3, class Arg4> -struct is_valid_mixed_compare<number<B, ET>, expression<tag, Arg1, Arg2, Arg3, Arg4> > +struct is_valid_mixed_compare<number<B, ET>, expression<tag, Arg1, Arg2, Arg3, Arg4> > : public mpl::bool_<is_convertible<expression<tag, Arg1, Arg2, Arg3, Arg4>, number<B, ET> >::value> {}; template <class tag, class Arg1, class Arg2, class Arg3, class Arg4, class B, expression_template_option ET> -struct is_valid_mixed_compare<expression<tag, Arg1, Arg2, Arg3, Arg4>, number<B, ET> > +struct is_valid_mixed_compare<expression<tag, Arg1, Arg2, Arg3, Arg4>, number<B, ET> > : public mpl::bool_<is_convertible<expression<tag, Arg1, Arg2, Arg3, Arg4>, number<B, ET> >::value> {}; template <class Backend, expression_template_option ExpressionTemplates> @@ -196,7 +196,7 @@ inline bool operator == (const number<Backend, ExpressionTemplates>& a, const nu return eval_eq(a.backend(), b.backend()); } template <class Backend, expression_template_option ExpressionTemplates, class Arithmetic> -inline typename enable_if_c<detail::is_valid_mixed_compare<number<Backend, ExpressionTemplates>, Arithmetic>::value, bool>::type +inline typename enable_if_c<detail::is_valid_mixed_compare<number<Backend, ExpressionTemplates>, Arithmetic>::value, bool>::type operator == (const number<Backend, ExpressionTemplates>& a, const Arithmetic& b) { using default_ops::eval_eq; @@ -204,7 +204,7 @@ inline typename enable_if_c<detail::is_valid_mixed_compare<number<Backend, Expre return eval_eq(a.backend(), number<Backend, ExpressionTemplates>::canonical_value(b)); } template <class Arithmetic, class Backend, expression_template_option ExpressionTemplates> -inline typename enable_if_c<detail::is_valid_mixed_compare<number<Backend, ExpressionTemplates>, Arithmetic>::value, bool>::type +inline typename enable_if_c<detail::is_valid_mixed_compare<number<Backend, ExpressionTemplates>, Arithmetic>::value, bool>::type operator == (const Arithmetic& a, const number<Backend, ExpressionTemplates>& b) { using default_ops::eval_eq; @@ -212,7 +212,7 @@ inline typename enable_if_c<detail::is_valid_mixed_compare<number<Backend, Expre return eval_eq(b.backend(), number<Backend, ExpressionTemplates>::canonical_value(a)); } template <class Arithmetic, class Tag, class A1, class A2, class A3, class A4> -inline typename enable_if_c<detail::is_valid_mixed_compare<typename detail::expression<Tag, A1, A2, A3, A4>::result_type, Arithmetic>::value, bool>::type +inline typename enable_if_c<detail::is_valid_mixed_compare<typename detail::expression<Tag, A1, A2, A3, A4>::result_type, Arithmetic>::value, bool>::type operator == (const Arithmetic& a, const detail::expression<Tag, A1, A2, A3, A4>& b) { typedef typename detail::expression<Tag, A1, A2, A3, A4>::result_type result_type; @@ -222,7 +222,7 @@ inline typename enable_if_c<detail::is_valid_mixed_compare<typename detail::expr return eval_eq(t.backend(), result_type::canonical_value(a)); } template <class Tag, class A1, class A2, class A3, class A4, class Arithmetic> -inline typename enable_if_c<detail::is_valid_mixed_compare<typename detail::expression<Tag, A1, A2, A3, A4>::result_type, Arithmetic>::value, bool>::type +inline typename enable_if_c<detail::is_valid_mixed_compare<typename detail::expression<Tag, A1, A2, A3, A4>::result_type, Arithmetic>::value, bool>::type operator == (const detail::expression<Tag, A1, A2, A3, A4>& a, const Arithmetic& b) { typedef typename detail::expression<Tag, A1, A2, A3, A4>::result_type result_type; @@ -232,7 +232,7 @@ inline typename enable_if_c<detail::is_valid_mixed_compare<typename detail::expr return eval_eq(t.backend(), result_type::canonical_value(b)); } template <class Tag, class A1, class A2, class A3, class A4, class Tagb, class A1b, class A2b, class A3b, class A4b> -inline typename enable_if<is_same<typename detail::expression<Tag, A1, A2, A3, A4>::result_type, typename detail::expression<Tagb, A1b, A2b, A3b, A4b>::result_type>, bool>::type +inline typename enable_if<is_same<typename detail::expression<Tag, A1, A2, A3, A4>::result_type, typename detail::expression<Tagb, A1b, A2b, A3b, A4b>::result_type>, bool>::type operator == (const detail::expression<Tag, A1, A2, A3, A4>& a, const detail::expression<Tagb, A1b, A2b, A3b, A4b>& b) { using default_ops::eval_eq; @@ -250,7 +250,7 @@ inline bool operator != (const number<Backend, ExpressionTemplates>& a, const nu return !eval_eq(a.backend(), b.backend()); } template <class Backend, expression_template_option ExpressionTemplates, class Arithmetic> -inline typename enable_if_c<detail::is_valid_mixed_compare<number<Backend, ExpressionTemplates>, Arithmetic>::value, bool>::type +inline typename enable_if_c<detail::is_valid_mixed_compare<number<Backend, ExpressionTemplates>, Arithmetic>::value, bool>::type operator != (const number<Backend, ExpressionTemplates>& a, const Arithmetic& b) { using default_ops::eval_eq; @@ -258,7 +258,7 @@ inline typename enable_if_c<detail::is_valid_mixed_compare<number<Backend, Expre return !eval_eq(a.backend(), number<Backend, et_on>::canonical_value(b)); } template <class Arithmetic, class Backend, expression_template_option ExpressionTemplates> -inline typename enable_if_c<detail::is_valid_mixed_compare<number<Backend, ExpressionTemplates>, Arithmetic>::value, bool>::type +inline typename enable_if_c<detail::is_valid_mixed_compare<number<Backend, ExpressionTemplates>, Arithmetic>::value, bool>::type operator != (const Arithmetic& a, const number<Backend, ExpressionTemplates>& b) { using default_ops::eval_eq; @@ -266,7 +266,7 @@ inline typename enable_if_c<detail::is_valid_mixed_compare<number<Backend, Expre return !eval_eq(b.backend(), number<Backend, et_on>::canonical_value(a)); } template <class Arithmetic, class Tag, class A1, class A2, class A3, class A4> -inline typename enable_if_c<detail::is_valid_mixed_compare<typename detail::expression<Tag, A1, A2, A3, A4>::result_type, Arithmetic>::value, bool>::type +inline typename enable_if_c<detail::is_valid_mixed_compare<typename detail::expression<Tag, A1, A2, A3, A4>::result_type, Arithmetic>::value, bool>::type operator != (const Arithmetic& a, const detail::expression<Tag, A1, A2, A3, A4>& b) { typedef typename detail::expression<Tag, A1, A2, A3, A4>::result_type result_type; @@ -276,7 +276,7 @@ inline typename enable_if_c<detail::is_valid_mixed_compare<typename detail::expr return !eval_eq(t.backend(), result_type::canonical_value(a)); } template <class Tag, class A1, class A2, class A3, class A4, class Arithmetic> -inline typename enable_if_c<detail::is_valid_mixed_compare<typename detail::expression<Tag, A1, A2, A3, A4>::result_type, Arithmetic>::value, bool>::type +inline typename enable_if_c<detail::is_valid_mixed_compare<typename detail::expression<Tag, A1, A2, A3, A4>::result_type, Arithmetic>::value, bool>::type operator != (const detail::expression<Tag, A1, A2, A3, A4>& a, const Arithmetic& b) { typedef typename detail::expression<Tag, A1, A2, A3, A4>::result_type result_type; @@ -286,7 +286,7 @@ inline typename enable_if_c<detail::is_valid_mixed_compare<typename detail::expr return !eval_eq(t.backend(), result_type::canonical_value(b)); } template <class Tag, class A1, class A2, class A3, class A4, class Tagb, class A1b, class A2b, class A3b, class A4b> -inline typename enable_if<is_same<typename detail::expression<Tag, A1, A2, A3, A4>::result_type, typename detail::expression<Tagb, A1b, A2b, A3b, A4b>::result_type>, bool>::type +inline typename enable_if<is_same<typename detail::expression<Tag, A1, A2, A3, A4>::result_type, typename detail::expression<Tagb, A1b, A2b, A3b, A4b>::result_type>, bool>::type operator != (const detail::expression<Tag, A1, A2, A3, A4>& a, const detail::expression<Tagb, A1b, A2b, A3b, A4b>& b) { using default_ops::eval_eq; @@ -304,7 +304,7 @@ inline bool operator < (const number<Backend, ExpressionTemplates>& a, const num return eval_lt(a.backend(), b.backend()); } template <class Backend, expression_template_option ExpressionTemplates, class Arithmetic> -inline typename enable_if_c<detail::is_valid_mixed_compare<number<Backend, ExpressionTemplates>, Arithmetic>::value, bool>::type +inline typename enable_if_c<detail::is_valid_mixed_compare<number<Backend, ExpressionTemplates>, Arithmetic>::value, bool>::type operator < (const number<Backend, ExpressionTemplates>& a, const Arithmetic& b) { using default_ops::eval_lt; @@ -312,7 +312,7 @@ inline typename enable_if_c<detail::is_valid_mixed_compare<number<Backend, Expre return eval_lt(a.backend(), number<Backend, ExpressionTemplates>::canonical_value(b)); } template <class Arithmetic, class Backend, expression_template_option ExpressionTemplates> -inline typename enable_if_c<detail::is_valid_mixed_compare<number<Backend, ExpressionTemplates>, Arithmetic>::value, bool>::type +inline typename enable_if_c<detail::is_valid_mixed_compare<number<Backend, ExpressionTemplates>, Arithmetic>::value, bool>::type operator < (const Arithmetic& a, const number<Backend, ExpressionTemplates>& b) { using default_ops::eval_gt; @@ -320,7 +320,7 @@ inline typename enable_if_c<detail::is_valid_mixed_compare<number<Backend, Expre return eval_gt(b.backend(), number<Backend, ExpressionTemplates>::canonical_value(a)); } template <class Arithmetic, class Tag, class A1, class A2, class A3, class A4> -inline typename enable_if_c<detail::is_valid_mixed_compare<typename detail::expression<Tag, A1, A2, A3, A4>::result_type, Arithmetic>::value, bool>::type +inline typename enable_if_c<detail::is_valid_mixed_compare<typename detail::expression<Tag, A1, A2, A3, A4>::result_type, Arithmetic>::value, bool>::type operator < (const Arithmetic& a, const detail::expression<Tag, A1, A2, A3, A4>& b) { typedef typename detail::expression<Tag, A1, A2, A3, A4>::result_type result_type; @@ -330,7 +330,7 @@ inline typename enable_if_c<detail::is_valid_mixed_compare<typename detail::expr return eval_gt(t.backend(), result_type::canonical_value(a)); } template <class Tag, class A1, class A2, class A3, class A4, class Arithmetic> -inline typename enable_if_c<detail::is_valid_mixed_compare<typename detail::expression<Tag, A1, A2, A3, A4>::result_type, Arithmetic>::value, bool>::type +inline typename enable_if_c<detail::is_valid_mixed_compare<typename detail::expression<Tag, A1, A2, A3, A4>::result_type, Arithmetic>::value, bool>::type operator < (const detail::expression<Tag, A1, A2, A3, A4>& a, const Arithmetic& b) { typedef typename detail::expression<Tag, A1, A2, A3, A4>::result_type result_type; @@ -340,7 +340,7 @@ inline typename enable_if_c<detail::is_valid_mixed_compare<typename detail::expr return eval_lt(t.backend(), result_type::canonical_value(b)); } template <class Tag, class A1, class A2, class A3, class A4, class Tagb, class A1b, class A2b, class A3b, class A4b> -inline typename enable_if<is_same<typename detail::expression<Tag, A1, A2, A3, A4>::result_type, typename detail::expression<Tagb, A1b, A2b, A3b, A4b>::result_type>, bool>::type +inline typename enable_if<is_same<typename detail::expression<Tag, A1, A2, A3, A4>::result_type, typename detail::expression<Tagb, A1b, A2b, A3b, A4b>::result_type>, bool>::type operator < (const detail::expression<Tag, A1, A2, A3, A4>& a, const detail::expression<Tagb, A1b, A2b, A3b, A4b>& b) { using default_ops::eval_lt; @@ -358,7 +358,7 @@ inline bool operator > (const number<Backend, ExpressionTemplates>& a, const num return eval_gt(a.backend(), b.backend()); } template <class Backend, expression_template_option ExpressionTemplates, class Arithmetic> -inline typename enable_if_c<detail::is_valid_mixed_compare<number<Backend, ExpressionTemplates>, Arithmetic>::value, bool>::type +inline typename enable_if_c<detail::is_valid_mixed_compare<number<Backend, ExpressionTemplates>, Arithmetic>::value, bool>::type operator > (const number<Backend, ExpressionTemplates>& a, const Arithmetic& b) { using default_ops::eval_gt; @@ -366,7 +366,7 @@ inline typename enable_if_c<detail::is_valid_mixed_compare<number<Backend, Expre return eval_gt(a.backend(), number<Backend, ExpressionTemplates>::canonical_value(b)); } template <class Arithmetic, class Backend, expression_template_option ExpressionTemplates> -inline typename enable_if_c<detail::is_valid_mixed_compare<number<Backend, ExpressionTemplates>, Arithmetic>::value, bool>::type +inline typename enable_if_c<detail::is_valid_mixed_compare<number<Backend, ExpressionTemplates>, Arithmetic>::value, bool>::type operator > (const Arithmetic& a, const number<Backend, ExpressionTemplates>& b) { using default_ops::eval_lt; @@ -374,7 +374,7 @@ inline typename enable_if_c<detail::is_valid_mixed_compare<number<Backend, Expre return eval_lt(b.backend(), number<Backend, ExpressionTemplates>::canonical_value(a)); } template <class Arithmetic, class Tag, class A1, class A2, class A3, class A4> -inline typename enable_if_c<detail::is_valid_mixed_compare<typename detail::expression<Tag, A1, A2, A3, A4>::result_type, Arithmetic>::value, bool>::type +inline typename enable_if_c<detail::is_valid_mixed_compare<typename detail::expression<Tag, A1, A2, A3, A4>::result_type, Arithmetic>::value, bool>::type operator > (const Arithmetic& a, const detail::expression<Tag, A1, A2, A3, A4>& b) { typedef typename detail::expression<Tag, A1, A2, A3, A4>::result_type result_type; @@ -384,7 +384,7 @@ inline typename enable_if_c<detail::is_valid_mixed_compare<typename detail::expr return a > t; } template <class Tag, class A1, class A2, class A3, class A4, class Arithmetic> -inline typename enable_if_c<detail::is_valid_mixed_compare<typename detail::expression<Tag, A1, A2, A3, A4>::result_type, Arithmetic>::value, bool>::type +inline typename enable_if_c<detail::is_valid_mixed_compare<typename detail::expression<Tag, A1, A2, A3, A4>::result_type, Arithmetic>::value, bool>::type operator > (const detail::expression<Tag, A1, A2, A3, A4>& a, const Arithmetic& b) { typedef typename detail::expression<Tag, A1, A2, A3, A4>::result_type result_type; @@ -394,7 +394,7 @@ inline typename enable_if_c<detail::is_valid_mixed_compare<typename detail::expr return t > b; } template <class Tag, class A1, class A2, class A3, class A4, class Tagb, class A1b, class A2b, class A3b, class A4b> -inline typename enable_if<is_same<typename detail::expression<Tag, A1, A2, A3, A4>::result_type, typename detail::expression<Tagb, A1b, A2b, A3b, A4b>::result_type>, bool>::type +inline typename enable_if<is_same<typename detail::expression<Tag, A1, A2, A3, A4>::result_type, typename detail::expression<Tagb, A1b, A2b, A3b, A4b>::result_type>, bool>::type operator > (const detail::expression<Tag, A1, A2, A3, A4>& a, const detail::expression<Tagb, A1b, A2b, A3b, A4b>& b) { using default_ops::eval_gt; @@ -412,7 +412,7 @@ inline bool operator <= (const number<Backend, ExpressionTemplates>& a, const nu return !eval_gt(a.backend(), b.backend()); } template <class Backend, expression_template_option ExpressionTemplates, class Arithmetic> -inline typename enable_if_c<detail::is_valid_mixed_compare<number<Backend, ExpressionTemplates>, Arithmetic>::value, bool>::type +inline typename enable_if_c<detail::is_valid_mixed_compare<number<Backend, ExpressionTemplates>, Arithmetic>::value, bool>::type operator <= (const number<Backend, ExpressionTemplates>& a, const Arithmetic& b) { using default_ops::eval_gt; @@ -420,7 +420,7 @@ inline typename enable_if_c<detail::is_valid_mixed_compare<number<Backend, Expre return !eval_gt(a.backend(), number<Backend, ExpressionTemplates>::canonical_value(b)); } template <class Arithmetic, class Backend, expression_template_option ExpressionTemplates> -inline typename enable_if_c<detail::is_valid_mixed_compare<number<Backend, ExpressionTemplates>, Arithmetic>::value, bool>::type +inline typename enable_if_c<detail::is_valid_mixed_compare<number<Backend, ExpressionTemplates>, Arithmetic>::value, bool>::type operator <= (const Arithmetic& a, const number<Backend, ExpressionTemplates>& b) { using default_ops::eval_lt; @@ -428,7 +428,7 @@ inline typename enable_if_c<detail::is_valid_mixed_compare<number<Backend, Expre return !eval_lt(b.backend(), number<Backend, ExpressionTemplates>::canonical_value(a)); } template <class Arithmetic, class Tag, class A1, class A2, class A3, class A4> -inline typename enable_if_c<detail::is_valid_mixed_compare<typename detail::expression<Tag, A1, A2, A3, A4>::result_type, Arithmetic>::value, bool>::type +inline typename enable_if_c<detail::is_valid_mixed_compare<typename detail::expression<Tag, A1, A2, A3, A4>::result_type, Arithmetic>::value, bool>::type operator <= (const Arithmetic& a, const detail::expression<Tag, A1, A2, A3, A4>& b) { typedef typename detail::expression<Tag, A1, A2, A3, A4>::result_type result_type; @@ -440,7 +440,7 @@ inline typename enable_if_c<detail::is_valid_mixed_compare<typename detail::expr return !eval_lt(t.backend(), result_type::canonical_value(a)); } template <class Tag, class A1, class A2, class A3, class A4, class Arithmetic> -inline typename enable_if_c<detail::is_valid_mixed_compare<typename detail::expression<Tag, A1, A2, A3, A4>::result_type, Arithmetic>::value, bool>::type +inline typename enable_if_c<detail::is_valid_mixed_compare<typename detail::expression<Tag, A1, A2, A3, A4>::result_type, Arithmetic>::value, bool>::type operator <= (const detail::expression<Tag, A1, A2, A3, A4>& a, const Arithmetic& b) { typedef typename detail::expression<Tag, A1, A2, A3, A4>::result_type result_type; @@ -450,7 +450,7 @@ inline typename enable_if_c<detail::is_valid_mixed_compare<typename detail::expr return !eval_gt(t.backend(), result_type::canonical_value(b)); } template <class Tag, class A1, class A2, class A3, class A4, class Tagb, class A1b, class A2b, class A3b, class A4b> -inline typename enable_if<is_same<typename detail::expression<Tag, A1, A2, A3, A4>::result_type, typename detail::expression<Tagb, A1b, A2b, A3b, A4b>::result_type>, bool>::type +inline typename enable_if<is_same<typename detail::expression<Tag, A1, A2, A3, A4>::result_type, typename detail::expression<Tagb, A1b, A2b, A3b, A4b>::result_type>, bool>::type operator <= (const detail::expression<Tag, A1, A2, A3, A4>& a, const detail::expression<Tagb, A1b, A2b, A3b, A4b>& b) { using default_ops::eval_gt; @@ -468,7 +468,7 @@ inline bool operator >= (const number<Backend, ExpressionTemplates>& a, const nu return !eval_lt(a.backend(), b.backend()); } template <class Backend, expression_template_option ExpressionTemplates, class Arithmetic> -inline typename enable_if_c<detail::is_valid_mixed_compare<number<Backend, ExpressionTemplates>, Arithmetic>::value, bool>::type +inline typename enable_if_c<detail::is_valid_mixed_compare<number<Backend, ExpressionTemplates>, Arithmetic>::value, bool>::type operator >= (const number<Backend, ExpressionTemplates>& a, const Arithmetic& b) { using default_ops::eval_lt; @@ -476,7 +476,7 @@ inline typename enable_if_c<detail::is_valid_mixed_compare<number<Backend, Expre return !eval_lt(a.backend(), number<Backend, ExpressionTemplates>::canonical_value(b)); } template <class Arithmetic, class Backend, expression_template_option ExpressionTemplates> -inline typename enable_if_c<detail::is_valid_mixed_compare<number<Backend, ExpressionTemplates>, Arithmetic>::value, bool>::type +inline typename enable_if_c<detail::is_valid_mixed_compare<number<Backend, ExpressionTemplates>, Arithmetic>::value, bool>::type operator >= (const Arithmetic& a, const number<Backend, ExpressionTemplates>& b) { using default_ops::eval_gt; @@ -484,7 +484,7 @@ inline typename enable_if_c<detail::is_valid_mixed_compare<number<Backend, Expre return !eval_gt(b.backend(), number<Backend, ExpressionTemplates>::canonical_value(a)); } template <class Arithmetic, class Tag, class A1, class A2, class A3, class A4> -inline typename enable_if_c<detail::is_valid_mixed_compare<typename detail::expression<Tag, A1, A2, A3, A4>::result_type, Arithmetic>::value, bool>::type +inline typename enable_if_c<detail::is_valid_mixed_compare<typename detail::expression<Tag, A1, A2, A3, A4>::result_type, Arithmetic>::value, bool>::type operator >= (const Arithmetic& a, const detail::expression<Tag, A1, A2, A3, A4>& b) { typedef typename detail::expression<Tag, A1, A2, A3, A4>::result_type result_type; @@ -494,7 +494,7 @@ inline typename enable_if_c<detail::is_valid_mixed_compare<typename detail::expr return !eval_gt(t.backend(), result_type::canonical_value(a)); } template <class Tag, class A1, class A2, class A3, class A4, class Arithmetic> -inline typename enable_if_c<detail::is_valid_mixed_compare<typename detail::expression<Tag, A1, A2, A3, A4>::result_type, Arithmetic>::value, bool>::type +inline typename enable_if_c<detail::is_valid_mixed_compare<typename detail::expression<Tag, A1, A2, A3, A4>::result_type, Arithmetic>::value, bool>::type operator >= (const detail::expression<Tag, A1, A2, A3, A4>& a, const Arithmetic& b) { typedef typename detail::expression<Tag, A1, A2, A3, A4>::result_type result_type; @@ -504,7 +504,7 @@ inline typename enable_if_c<detail::is_valid_mixed_compare<typename detail::expr return !eval_lt(t.backend(), result_type::canonical_value(b)); } template <class Tag, class A1, class A2, class A3, class A4, class Tagb, class A1b, class A2b, class A3b, class A4b> -inline typename enable_if<is_same<typename detail::expression<Tag, A1, A2, A3, A4>::result_type, typename detail::expression<Tagb, A1b, A2b, A3b, A4b>::result_type>, bool>::type +inline typename enable_if<is_same<typename detail::expression<Tag, A1, A2, A3, A4>::result_type, typename detail::expression<Tagb, A1b, A2b, A3b, A4b>::result_type>, bool>::type operator >= (const detail::expression<Tag, A1, A2, A3, A4>& a, const detail::expression<Tagb, A1b, A2b, A3b, A4b>& b) { using default_ops::eval_lt; diff --git a/libevmasm/Assembly.cpp b/libevmasm/Assembly.cpp index b71bc80c..e63194a0 100644 --- a/libevmasm/Assembly.cpp +++ b/libevmasm/Assembly.cpp @@ -41,10 +41,19 @@ void Assembly::append(Assembly const& _a) auto newDeposit = m_deposit + _a.deposit(); for (AssemblyItem i: _a.m_items) { - if (i.type() == Tag || i.type() == PushTag) + switch (i.type()) + { + case Tag: + case PushTag: i.setData(i.data() + m_usedTags); - else if (i.type() == PushSub || i.type() == PushSubSize) + break; + case PushSub: + case PushSubSize: i.setData(i.data() + m_subs.size()); + break; + default: + break; + } append(i); } m_deposit = newDeposit; @@ -69,6 +78,21 @@ void Assembly::append(Assembly const& _a, int _deposit) append(Instruction::POP); } +AssemblyItem const& Assembly::append(AssemblyItem const& _i) +{ + assertThrow(m_deposit >= 0, AssemblyException, "Stack underflow."); + m_deposit += _i.deposit(); + m_items.push_back(_i); + if (m_items.back().location().isEmpty() && !m_currentSourceLocation.isEmpty()) + m_items.back().setLocation(m_currentSourceLocation); + return back(); +} + +void Assembly::injectStart(AssemblyItem const& _i) +{ + m_items.insert(m_items.begin(), _i); +} + unsigned Assembly::bytesRequired(unsigned subTagSize) const { for (unsigned tagSize = subTagSize; true; ++tagSize) @@ -264,7 +288,7 @@ Json::Value Assembly::assemblyJSON(StringMap const& _sourceCodes) const createJsonValue("PUSH [ErrorTag]", i.location().start, i.location().end, "")); else collection.append( - createJsonValue("PUSH [tag]", i.location().start, i.location().end, string(i.data()))); + createJsonValue("PUSH [tag]", i.location().start, i.location().end, dev::toString(i.data()))); break; case PushSub: collection.append( @@ -290,7 +314,7 @@ Json::Value Assembly::assemblyJSON(StringMap const& _sourceCodes) const break; case Tag: collection.append( - createJsonValue("tag", i.location().start, i.location().end, string(i.data()))); + createJsonValue("tag", i.location().start, i.location().end, dev::toString(i.data()))); collection.append( createJsonValue("JUMPDEST", i.location().start, i.location().end)); break; @@ -323,16 +347,6 @@ Json::Value Assembly::assemblyJSON(StringMap const& _sourceCodes) const return root; } -AssemblyItem const& Assembly::append(AssemblyItem const& _i) -{ - assertThrow(m_deposit >= 0, AssemblyException, "Stack underflow."); - m_deposit += _i.deposit(); - m_items.push_back(_i); - if (m_items.back().location().isEmpty() && !m_currentSourceLocation.isEmpty()) - m_items.back().setLocation(m_currentSourceLocation); - return back(); -} - AssemblyItem Assembly::namedTag(string const& _name) { assertThrow(!_name.empty(), AssemblyException, "Empty named tag."); @@ -348,11 +362,6 @@ AssemblyItem Assembly::newPushLibraryAddress(string const& _identifier) return AssemblyItem(PushLibraryAddress, h); } -void Assembly::injectStart(AssemblyItem const& _i) -{ - m_items.insert(m_items.begin(), _i); -} - Assembly& Assembly::optimise(bool _enable, EVMVersion _evmVersion, bool _isCreation, size_t _runs) { OptimiserSettings settings; @@ -516,14 +525,14 @@ LinkerObject const& Assembly::assemble() const multimap<size_t, size_t> subRef; vector<unsigned> sizeRef; ///< Pointers to code locations where the size of the program is inserted unsigned bytesPerTag = dev::bytesRequired(bytesRequiredForCode); - byte tagPush = (byte)Instruction::PUSH1 - 1 + bytesPerTag; + uint8_t tagPush = (uint8_t)Instruction::PUSH1 - 1 + bytesPerTag; unsigned bytesRequiredIncludingData = bytesRequiredForCode + 1 + m_auxiliaryData.size(); for (auto const& sub: m_subs) bytesRequiredIncludingData += sub->assemble().bytecode.size(); unsigned bytesPerDataRef = dev::bytesRequired(bytesRequiredIncludingData); - byte dataRefPush = (byte)Instruction::PUSH1 - 1 + bytesPerDataRef; + uint8_t dataRefPush = (uint8_t)Instruction::PUSH1 - 1 + bytesPerDataRef; ret.bytecode.reserve(bytesRequiredIncludingData); for (AssemblyItem const& i: m_items) @@ -535,25 +544,25 @@ LinkerObject const& Assembly::assemble() const switch (i.type()) { case Operation: - ret.bytecode.push_back((byte)i.instruction()); + ret.bytecode.push_back((uint8_t)i.instruction()); break; case PushString: { - ret.bytecode.push_back((byte)Instruction::PUSH32); + ret.bytecode.push_back((uint8_t)Instruction::PUSH32); unsigned ii = 0; for (auto j: m_strings.at((h256)i.data())) if (++ii > 32) break; else - ret.bytecode.push_back((byte)j); + ret.bytecode.push_back((uint8_t)j); while (ii++ < 32) ret.bytecode.push_back(0); break; } case Push: { - byte b = max<unsigned>(1, dev::bytesRequired(i.data())); - ret.bytecode.push_back((byte)Instruction::PUSH1 - 1 + b); + uint8_t b = max<unsigned>(1, dev::bytesRequired(i.data())); + ret.bytecode.push_back((uint8_t)Instruction::PUSH1 - 1 + b); ret.bytecode.resize(ret.bytecode.size() + b); bytesRef byr(&ret.bytecode.back() + 1 - b, b); toBigEndian(i.data(), byr); @@ -580,8 +589,8 @@ LinkerObject const& Assembly::assemble() const { auto s = m_subs.at(size_t(i.data()))->assemble().bytecode.size(); i.setPushedValue(u256(s)); - byte b = max<unsigned>(1, dev::bytesRequired(s)); - ret.bytecode.push_back((byte)Instruction::PUSH1 - 1 + b); + uint8_t b = max<unsigned>(1, dev::bytesRequired(s)); + ret.bytecode.push_back((uint8_t)Instruction::PUSH1 - 1 + b); ret.bytecode.resize(ret.bytecode.size() + b); bytesRef byr(&ret.bytecode.back() + 1 - b, b); toBigEndian(s, byr); @@ -595,12 +604,12 @@ LinkerObject const& Assembly::assemble() const break; } case PushLibraryAddress: - ret.bytecode.push_back(byte(Instruction::PUSH20)); + ret.bytecode.push_back(uint8_t(Instruction::PUSH20)); ret.linkReferences[ret.bytecode.size()] = m_libraries.at(i.data()); ret.bytecode.resize(ret.bytecode.size() + 20); break; case PushDeployTimeAddress: - ret.bytecode.push_back(byte(Instruction::PUSH20)); + ret.bytecode.push_back(uint8_t(Instruction::PUSH20)); ret.bytecode.resize(ret.bytecode.size() + 20); break; case Tag: @@ -609,7 +618,7 @@ LinkerObject const& Assembly::assemble() const assertThrow(ret.bytecode.size() < 0xffffffffL, AssemblyException, "Tag too large."); assertThrow(m_tagPositionsInBytecode[size_t(i.data())] == size_t(-1), AssemblyException, "Duplicate tag position."); m_tagPositionsInBytecode[size_t(i.data())] = ret.bytecode.size(); - ret.bytecode.push_back((byte)Instruction::JUMPDEST); + ret.bytecode.push_back((uint8_t)Instruction::JUMPDEST); break; default: BOOST_THROW_EXCEPTION(InvalidOpcode()); @@ -617,8 +626,8 @@ LinkerObject const& Assembly::assemble() const } if (!m_subs.empty() || !m_data.empty() || !m_auxiliaryData.empty()) - // Append a STOP just to be sure. - ret.bytecode.push_back(0); + // Append an INVALID here to help tests find miscompilation. + ret.bytecode.push_back(uint8_t(Instruction::INVALID)); for (size_t i = 0; i < m_subs.size(); ++i) { diff --git a/libevmasm/Assembly.h b/libevmasm/Assembly.h index 1ed9b859..8ef36923 100644 --- a/libevmasm/Assembly.h +++ b/libevmasm/Assembly.h @@ -27,7 +27,7 @@ #include <libdevcore/Common.h> #include <libdevcore/Assertions.h> -#include <libdevcore/SHA3.h> +#include <libdevcore/Keccak256.h> #include <json/json.h> @@ -52,18 +52,19 @@ public: /// Returns a tag identified by the given name. Creates it if it does not yet exist. AssemblyItem namedTag(std::string const& _name); AssemblyItem newData(bytes const& _data) { h256 h(dev::keccak256(asString(_data))); m_data[h] = _data; return AssemblyItem(PushData, h); } + bytes const& data(h256 const& _i) const { return m_data.at(_i); } AssemblyItem newSub(AssemblyPointer const& _sub) { m_subs.push_back(_sub); return AssemblyItem(PushSub, m_subs.size() - 1); } Assembly const& sub(size_t _sub) const { return *m_subs.at(_sub); } Assembly& sub(size_t _sub) { return *m_subs.at(_sub); } - AssemblyItem newPushString(std::string const& _data) { h256 h(dev::keccak256(_data)); m_strings[h] = _data; return AssemblyItem(PushString, h); } AssemblyItem newPushSubSize(u256 const& _subId) { return AssemblyItem(PushSubSize, _subId); } AssemblyItem newPushLibraryAddress(std::string const& _identifier); - void append(Assembly const& _a); - void append(Assembly const& _a, int _deposit); AssemblyItem const& append(AssemblyItem const& _i); AssemblyItem const& append(std::string const& _data) { return append(newPushString(_data)); } AssemblyItem const& append(bytes const& _data) { return append(newData(_data)); } + + template <class T> Assembly& operator<<(T const& _d) { append(_d); return *this; } + /// Pushes the final size of the current assembly itself. Use this when the code is modified /// after compilation and CODESIZE is not an option. void appendProgramSize() { append(AssemblyItem(PushProgramSize)); } @@ -84,12 +85,9 @@ public: /// Appends @a _data literally to the very end of the bytecode. void appendAuxiliaryDataToEnd(bytes const& _data) { m_auxiliaryData += _data; } - template <class T> Assembly& operator<<(T const& _d) { append(_d); return *this; } + /// Returns the assembly items. AssemblyItems const& items() const { return m_items; } - AssemblyItem const& back() const { return m_items.back(); } - std::string backString() const { return m_items.size() && m_items.back().type() == PushString ? m_strings.at((h256)m_items.back().data()) : std::string(); } - void injectStart(AssemblyItem const& _i); int deposit() const { return m_deposit; } void adjustDeposit(int _adjustment) { m_deposit += _adjustment; assertThrow(m_deposit >= 0, InvalidDeposit, ""); } void setDeposit(int _deposit) { m_deposit = _deposit; assertThrow(m_deposit >= 0, InvalidDeposit, ""); } @@ -97,9 +95,8 @@ public: /// Changes the source location used for each appended item. void setSourceLocation(SourceLocation const& _location) { m_currentSourceLocation = _location; } - /// Assembles the assembly into bytecode. The assembly should not be modified after this call. + /// Assembles the assembly into bytecode. The assembly should not be modified after this call, since the assembled version is cached. LinkerObject const& assemble() const; - bytes const& data(h256 const& _i) const { return m_data.at(_i); } struct OptimiserSettings { @@ -140,6 +137,18 @@ public: StringMap const& _sourceCodes = StringMap() ) const; +public: + // These features are only used by LLL + AssemblyItem newPushString(std::string const& _data) { h256 h(dev::keccak256(_data)); m_strings[h] = _data; return AssemblyItem(PushString, h); } + + void append(Assembly const& _a); + void append(Assembly const& _a, int _deposit); + + void injectStart(AssemblyItem const& _i); + + AssemblyItem const& back() const { return m_items.back(); } + std::string backString() const { return m_items.size() && m_items.back().type() == PushString ? m_strings.at((h256)m_items.back().data()) : std::string(); } + 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 diff --git a/libevmasm/AssemblyItem.h b/libevmasm/AssemblyItem.h index 5319a2b6..6187e18f 100644 --- a/libevmasm/AssemblyItem.h +++ b/libevmasm/AssemblyItem.h @@ -69,7 +69,7 @@ public: m_location(_location) { if (m_type == Operation) - m_instruction = Instruction(byte(_data)); + m_instruction = Instruction(uint8_t(_data)); else m_data = std::make_shared<u256>(_data); } diff --git a/libevmasm/CommonSubexpressionEliminator.cpp b/libevmasm/CommonSubexpressionEliminator.cpp index 293cb02c..04926986 100644 --- a/libevmasm/CommonSubexpressionEliminator.cpp +++ b/libevmasm/CommonSubexpressionEliminator.cpp @@ -23,7 +23,7 @@ #include <functional> #include <boost/range/adaptor/reversed.hpp> -#include <libdevcore/SHA3.h> +#include <libdevcore/Keccak256.h> #include <libevmasm/CommonSubexpressionEliminator.h> #include <libevmasm/AssemblyItem.h> @@ -160,7 +160,7 @@ AssemblyItems CSECodeGenerator::generateCode( if (seqNr < _initialSequenceNumber) // Invalid sequenced operation. // @todo quick fix for now. Proper fix needs to choose representative with higher - // sequence number during dependency analyis. + // sequence number during dependency analysis. BOOST_THROW_EXCEPTION(StackTooDeepException()); sequencedExpressions.insert(make_pair(seqNr, id)); } diff --git a/libevmasm/ConstantOptimiser.cpp b/libevmasm/ConstantOptimiser.cpp index d0b6843c..9844ba3a 100644 --- a/libevmasm/ConstantOptimiser.cpp +++ b/libevmasm/ConstantOptimiser.cpp @@ -93,15 +93,8 @@ bigint ConstantOptimisationMethod::simpleRunGas(AssemblyItems const& _items) bigint ConstantOptimisationMethod::dataGas(bytes const& _data) const { - if (m_params.isCreation) - { - bigint gas; - for (auto b: _data) - gas += b ? GasCosts::txDataNonZeroGas : GasCosts::txDataZeroGas; - return gas; - } - else - return GasCosts::createDataGas * dataSize(); + assertThrow(_data.size() > 0, OptimizerException, "Empty bytecode generated."); + return bigint(GasMeter::dataGas(_data, m_params.isCreation)); } size_t ConstantOptimisationMethod::bytesRequired(AssemblyItems const& _items) diff --git a/libevmasm/ConstantOptimiser.h b/libevmasm/ConstantOptimiser.h index f0deb387..2c753fa8 100644 --- a/libevmasm/ConstantOptimiser.h +++ b/libevmasm/ConstantOptimiser.h @@ -75,8 +75,6 @@ public: virtual AssemblyItems execute(Assembly& _assembly) const = 0; protected: - size_t dataSize() const { return std::max<size_t>(1, dev::bytesRequired(m_value)); } - /// @returns the run gas for the given items ignoring special gas costs static bigint simpleRunGas(AssemblyItems const& _items); /// @returns the gas needed to store the given data literally diff --git a/libevmasm/ControlFlowGraph.h b/libevmasm/ControlFlowGraph.h index ebef543f..f0c9356c 100644 --- a/libevmasm/ControlFlowGraph.h +++ b/libevmasm/ControlFlowGraph.h @@ -39,7 +39,7 @@ using KnownStatePointer = std::shared_ptr<KnownState>; /** * Identifier for a block, coincides with the tag number of an AssemblyItem but adds a special - * ID for the inital block. + * ID for the initial block. */ class BlockId { diff --git a/libevmasm/ExpressionClasses.cpp b/libevmasm/ExpressionClasses.cpp index 69b33ec5..42a1819a 100644 --- a/libevmasm/ExpressionClasses.cpp +++ b/libevmasm/ExpressionClasses.cpp @@ -184,6 +184,7 @@ string ExpressionClasses::fullDAGToString(ExpressionClasses::Id _id) const ExpressionClasses::Id ExpressionClasses::tryToSimplify(Expression const& _expr) { static Rules rules; + assertThrow(rules.isInitialized(), OptimizerException, "Rule list not properly initialized."); if ( !_expr.item || diff --git a/libevmasm/GasMeter.cpp b/libevmasm/GasMeter.cpp index caa06fc0..d98b3efa 100644 --- a/libevmasm/GasMeter.cpp +++ b/libevmasm/GasMeter.cpp @@ -101,8 +101,8 @@ GasMeter::GasConsumption GasMeter::estimateMax(AssemblyItem const& _item, bool _ break; case Instruction::KECCAK256: gas = GasCosts::keccak256Gas; - gas += wordGas(GasCosts::keccak256WordGas, m_state->relativeStackElement(-1)); gas += memoryGas(0, -1); + gas += wordGas(GasCosts::keccak256WordGas, m_state->relativeStackElement(-1)); break; case Instruction::CALLDATACOPY: case Instruction::CODECOPY: @@ -114,6 +114,9 @@ GasMeter::GasConsumption GasMeter::estimateMax(AssemblyItem const& _item, bool _ case Instruction::EXTCODESIZE: gas = GasCosts::extCodeGas(m_evmVersion); break; + case Instruction::EXTCODEHASH: + gas = GasCosts::balanceGas(m_evmVersion); + break; case Instruction::EXTCODECOPY: gas = GasCosts::extCodeGas(m_evmVersion); gas += memoryGas(-1, -3); @@ -125,8 +128,7 @@ GasMeter::GasConsumption GasMeter::estimateMax(AssemblyItem const& _item, bool _ case Instruction::LOG3: case Instruction::LOG4: { - unsigned n = unsigned(_item.instruction()) - unsigned(Instruction::LOG0); - gas = GasCosts::logGas + GasCosts::logTopicGas * n; + gas = GasCosts::logGas + GasCosts::logTopicGas * getLogNumber(_item.instruction()); gas += memoryGas(0, -1); if (u256 const* value = classes.knownConstant(m_state->relativeStackElement(-1))) gas += GasCosts::logDataGas * (*value); @@ -215,7 +217,7 @@ GasMeter::GasConsumption GasMeter::memoryGas(ExpressionClasses::Id _position) if (!value) return GasConsumption::infinite(); if (*value < m_largestMemoryAccess) - return GasConsumption(u256(0)); + return GasConsumption(0); u256 previous = m_largestMemoryAccess; m_largestMemoryAccess = *value; auto memGas = [=](u256 const& pos) -> u256 @@ -258,4 +260,16 @@ unsigned GasMeter::runGas(Instruction _instruction) return 0; } - +u256 GasMeter::dataGas(bytes const& _data, bool _inCreation) +{ + bigint gas = 0; + if (_inCreation) + { + for (auto b: _data) + gas += (b != 0) ? GasCosts::txDataNonZeroGas : GasCosts::txDataZeroGas; + } + else + gas = bigint(GasCosts::createDataGas) * _data.size(); + assertThrow(gas < bigint(u256(-1)), OptimizerException, "Gas cost exceeds 256 bits."); + return u256(gas); +} diff --git a/libevmasm/GasMeter.h b/libevmasm/GasMeter.h index b131802f..da90b028 100644 --- a/libevmasm/GasMeter.h +++ b/libevmasm/GasMeter.h @@ -112,9 +112,10 @@ public: static GasConsumption infinite() { return GasConsumption(0, true); } GasConsumption& operator+=(GasConsumption const& _other); - bool operator<(GasConsumption const& _other) const { return this->tuple() < _other.tuple(); } - - std::tuple<bool const&, u256 const&> tuple() const { return std::tie(isInfinite, value); } + bool operator<(GasConsumption const& _other) const + { + return std::make_pair(isInfinite, value) < std::make_pair(_other.isInfinite, _other.value); + } u256 value; bool isInfinite; @@ -135,6 +136,11 @@ public: /// change with EVM versions) static unsigned runGas(Instruction _instruction); + /// @returns the gas cost of the supplied data, depending whether it is in creation code, or not. + /// In case of @a _inCreation, the data is only sent as a transaction and is not stored, whereas + /// otherwise code will be stored and have to pay "createDataGas" cost. + static u256 dataGas(bytes const& _data, bool _inCreation); + private: /// @returns _multiplier * (_value + 31) / 32, if _value is a known constant and infinite otherwise. GasConsumption wordGas(u256 const& _multiplier, ExpressionClasses::Id _value); diff --git a/libevmasm/Instruction.cpp b/libevmasm/Instruction.cpp index f9bbad2c..cf98c938 100644 --- a/libevmasm/Instruction.cpp +++ b/libevmasm/Instruction.cpp @@ -21,6 +21,7 @@ #include "./Instruction.h" +#include <algorithm> #include <functional> #include <libdevcore/Common.h> #include <libdevcore/CommonIO.h> @@ -72,6 +73,7 @@ const std::map<std::string, Instruction> dev::solidity::c_instructions = { "EXTCODECOPY", Instruction::EXTCODECOPY }, { "RETURNDATASIZE", Instruction::RETURNDATASIZE }, { "RETURNDATACOPY", Instruction::RETURNDATACOPY }, + { "EXTCODEHASH", Instruction::EXTCODEHASH }, { "BLOCKHASH", Instruction::BLOCKHASH }, { "COINBASE", Instruction::COINBASE }, { "TIMESTAMP", Instruction::TIMESTAMP }, @@ -215,6 +217,7 @@ static const std::map<Instruction, InstructionInfo> c_instructionInfo = { Instruction::EXTCODECOPY, { "EXTCODECOPY", 0, 4, 0, true, Tier::ExtCode } }, { Instruction::RETURNDATASIZE, {"RETURNDATASIZE", 0, 0, 1, false, Tier::Base } }, { Instruction::RETURNDATACOPY, {"RETURNDATACOPY", 0, 3, 0, true, Tier::VeryLow } }, + { Instruction::EXTCODEHASH, { "EXTCODEHASH", 0, 1, 1, false, Tier::Balance } }, { Instruction::BLOCKHASH, { "BLOCKHASH", 0, 1, 1, false, Tier::Ext } }, { Instruction::COINBASE, { "COINBASE", 0, 0, 1, false, Tier::Base } }, { Instruction::TIMESTAMP, { "TIMESTAMP", 0, 0, 1, false, Tier::Base } }, @@ -325,13 +328,20 @@ void dev::solidity::eachInstruction( size_t additional = 0; if (isValidInstruction(instr)) additional = instructionInfo(instr).additional; + u256 data; - for (size_t i = 0; i < additional; ++i) + + // fill the data with the additional data bytes from the instruction stream + while (additional > 0 && std::next(it) < _mem.end()) { data <<= 8; - if (++it < _mem.end()) - data |= *it; + data |= *++it; + --additional; } + + // pad the remaining number of additional octets with zeros + data <<= 8 * additional; + _onInstruction(instr, data); } } diff --git a/libevmasm/Instruction.h b/libevmasm/Instruction.h index dc116f88..539a83b0 100644 --- a/libevmasm/Instruction.h +++ b/libevmasm/Instruction.h @@ -82,6 +82,7 @@ enum class Instruction: uint8_t EXTCODECOPY, ///< copy external code (from another contract) RETURNDATASIZE = 0x3d, ///< get size of return data buffer RETURNDATACOPY = 0x3e, ///< copy return data in current environment to memory + EXTCODEHASH = 0x3f, ///< get external code hash (from another contract) BLOCKHASH = 0x40, ///< get hash of most recent complete block COINBASE, ///< get the block's coinbase address @@ -192,8 +193,8 @@ enum class Instruction: uint8_t CALLCODE, ///< message-call with another account's code only RETURN, ///< halt execution returning output data DELEGATECALL, ///< like CALLCODE but keeps caller's value and sender + CREATE2 = 0xf5, ///< create new account with associated code at address `sha3(0xff + sender + salt + init code) % 2**160` STATICCALL = 0xfa, ///< like CALL but disallow state modifications - CREATE2 = 0xfb, ///< create new account with associated code at address `sha3(sender + salt + sha3(init code)) % 2**160` REVERT = 0xfd, ///< halt execution, revert state and return output data INVALID = 0xfe, ///< invalid instruction for expressing runtime errors (e.g., division-by-zero) @@ -218,22 +219,34 @@ inline bool isSwapInstruction(Instruction _inst) return Instruction::SWAP1 <= _inst && _inst <= Instruction::SWAP16; } +/// @returns true if the instruction is a LOG +inline bool isLogInstruction(Instruction _inst) +{ + return Instruction::LOG0 <= _inst && _inst <= Instruction::LOG4; +} + /// @returns the number of PUSH Instruction _inst inline unsigned getPushNumber(Instruction _inst) { - return (byte)_inst - unsigned(Instruction::PUSH1) + 1; + return (uint8_t)_inst - unsigned(Instruction::PUSH1) + 1; } /// @returns the number of DUP Instruction _inst inline unsigned getDupNumber(Instruction _inst) { - return (byte)_inst - unsigned(Instruction::DUP1) + 1; + return (uint8_t)_inst - unsigned(Instruction::DUP1) + 1; } /// @returns the number of SWAP Instruction _inst inline unsigned getSwapNumber(Instruction _inst) { - return (byte)_inst - unsigned(Instruction::SWAP1) + 1; + return (uint8_t)_inst - unsigned(Instruction::SWAP1) + 1; +} + +/// @returns the number of LOG Instruction _inst +inline unsigned getLogNumber(Instruction _inst) +{ + return (uint8_t)_inst - unsigned(Instruction::LOG0); } /// @returns the PUSH<_number> instruction diff --git a/libevmasm/KnownState.cpp b/libevmasm/KnownState.cpp index e2f10f22..b6c1bcc9 100644 --- a/libevmasm/KnownState.cpp +++ b/libevmasm/KnownState.cpp @@ -23,7 +23,7 @@ #include "KnownState.h" #include <functional> -#include <libdevcore/SHA3.h> +#include <libdevcore/Keccak256.h> #include <libevmasm/AssemblyItem.h> using namespace std; @@ -121,28 +121,33 @@ KnownState::StoreOperation KnownState::feedItem(AssemblyItem const& _item, bool vector<Id> arguments(info.args); for (int i = 0; i < info.args; ++i) arguments[i] = stackElement(m_stackHeight - i, _item.location()); - - if (_item.instruction() == Instruction::SSTORE) + switch (_item.instruction()) + { + case Instruction::SSTORE: op = storeInStorage(arguments[0], arguments[1], _item.location()); - else if (_item.instruction() == Instruction::SLOAD) + break; + case Instruction::SLOAD: setStackElement( m_stackHeight + _item.deposit(), loadFromStorage(arguments[0], _item.location()) ); - else if (_item.instruction() == Instruction::MSTORE) + break; + case Instruction::MSTORE: op = storeInMemory(arguments[0], arguments[1], _item.location()); - else if (_item.instruction() == Instruction::MLOAD) + break; + case Instruction::MLOAD: setStackElement( m_stackHeight + _item.deposit(), loadFromMemory(arguments[0], _item.location()) ); - else if (_item.instruction() == Instruction::KECCAK256) + break; + case Instruction::KECCAK256: setStackElement( m_stackHeight + _item.deposit(), applyKeccak256(arguments.at(0), arguments.at(1), _item.location()) ); - else - { + break; + default: bool invMem = SemanticInformation::invalidatesMemory(_item.instruction()); bool invStor = SemanticInformation::invalidatesStorage(_item.instruction()); // We could be a bit more fine-grained here (CALL only invalidates part of diff --git a/libevmasm/KnownState.h b/libevmasm/KnownState.h index 8568b163..cd50550e 100644 --- a/libevmasm/KnownState.h +++ b/libevmasm/KnownState.h @@ -29,12 +29,18 @@ #include <tuple> #include <memory> #include <ostream> -#pragma warning(push) -#pragma GCC diagnostic push + +#if defined(__clang__) +#pragma clang diagnostic push #pragma clang diagnostic ignored "-Wredeclared-class-member" +#endif // defined(__clang__) + #include <boost/bimap.hpp> -#pragma warning(pop) -#pragma GCC diagnostic pop + +#if defined(__clang__) +#pragma clang diagnostic pop +#endif // defined(__clang__) + #include <libdevcore/CommonIO.h> #include <libdevcore/Exceptions.h> #include <libevmasm/ExpressionClasses.h> diff --git a/libevmasm/LinkerObject.cpp b/libevmasm/LinkerObject.cpp index 8b7d9e06..6eec54ea 100644 --- a/libevmasm/LinkerObject.cpp +++ b/libevmasm/LinkerObject.cpp @@ -21,6 +21,7 @@ #include <libevmasm/LinkerObject.h> #include <libdevcore/CommonData.h> +#include <libdevcore/Keccak256.h> using namespace dev; using namespace dev::eth; @@ -50,14 +51,19 @@ string LinkerObject::toHex() const for (auto const& ref: linkReferences) { size_t pos = ref.first * 2; - string const& name = ref.second; + string hash = libraryPlaceholder(ref.second); hex[pos] = hex[pos + 1] = hex[pos + 38] = hex[pos + 39] = '_'; for (size_t i = 0; i < 36; ++i) - hex[pos + 2 + i] = i < name.size() ? name[i] : '_'; + hex[pos + 2 + i] = hash.at(i); } return hex; } +string LinkerObject::libraryPlaceholder(string const& _libraryName) +{ + return "$" + keccak256(_libraryName).hex().substr(0, 34) + "$"; +} + h160 const* LinkerObject::matchLibrary( string const& _linkRefName, @@ -68,7 +74,7 @@ LinkerObject::matchLibrary( if (it != _libraryAddresses.end()) return &it->second; // If the user did not supply a fully qualified library name, - // try to match only the simple libary name + // try to match only the simple library name size_t colon = _linkRefName.find(':'); if (colon == string::npos) return nullptr; diff --git a/libevmasm/LinkerObject.h b/libevmasm/LinkerObject.h index 152487b4..92890803 100644 --- a/libevmasm/LinkerObject.h +++ b/libevmasm/LinkerObject.h @@ -50,6 +50,11 @@ struct LinkerObject /// addresses by placeholders. std::string toHex() const; + /// @returns a 36 character string that is used as a placeholder for the library + /// address (enclosed by `__` on both sides). The placeholder is the hex representation + /// of the first 18 bytes of the keccak-256 hash of @a _libraryName. + static std::string libraryPlaceholder(std::string const& _libraryName); + private: static h160 const* matchLibrary( std::string const& _linkRefName, diff --git a/libevmasm/PathGasMeter.h b/libevmasm/PathGasMeter.h index 9537b176..fb821684 100644 --- a/libevmasm/PathGasMeter.h +++ b/libevmasm/PathGasMeter.h @@ -57,6 +57,16 @@ public: GasMeter::GasConsumption estimateMax(size_t _startIndex, std::shared_ptr<KnownState> const& _state); + static GasMeter::GasConsumption estimateMax( + AssemblyItems const& _items, + solidity::EVMVersion _evmVersion, + size_t _startIndex, + std::shared_ptr<KnownState> const& _state + ) + { + return PathGasMeter(_items, _evmVersion).estimateMax(_startIndex, _state); + } + private: /// Adds a new path item to the queue, but only if we do not already have /// a higher gas usage at that point. diff --git a/libevmasm/PeepholeOptimiser.cpp b/libevmasm/PeepholeOptimiser.cpp index 8a39de24..6d8e1df6 100644 --- a/libevmasm/PeepholeOptimiser.cpp +++ b/libevmasm/PeepholeOptimiser.cpp @@ -249,6 +249,23 @@ struct TagConjunctions: SimplePeepholeOptimizerMethod<TagConjunctions, 3> } }; +struct TruthyAnd: SimplePeepholeOptimizerMethod<TruthyAnd, 3> +{ + static bool applySimple( + AssemblyItem const& _push, + AssemblyItem const& _not, + AssemblyItem const& _and, + std::back_insert_iterator<AssemblyItems> + ) + { + return ( + _push.type() == Push && _push.data() == 0 && + _not == Instruction::NOT && + _and == Instruction::AND + ); + } +}; + /// Removes everything after a JUMP (or similar) until the next JUMPDEST. struct UnreachableCode { @@ -305,7 +322,7 @@ bool PeepholeOptimiser::optimise() { OptimiserState state {m_items, 0, std::back_inserter(m_optimisedItems)}; while (state.i < m_items.size()) - applyMethods(state, PushPop(), OpPop(), DoublePush(), DoubleSwap(), CommutativeSwap(), SwapComparison(), JumpToNext(), UnreachableCode(), TagConjunctions(), Identity()); + applyMethods(state, PushPop(), OpPop(), DoublePush(), DoubleSwap(), CommutativeSwap(), SwapComparison(), JumpToNext(), UnreachableCode(), TagConjunctions(), TruthyAnd(), Identity()); if (m_optimisedItems.size() < m_items.size() || ( m_optimisedItems.size() == m_items.size() && ( eth::bytesRequired(m_optimisedItems, 3) < eth::bytesRequired(m_items, 3) || diff --git a/libevmasm/RuleList.h b/libevmasm/RuleList.h index 2b7da01b..874a8929 100644 --- a/libevmasm/RuleList.h +++ b/libevmasm/RuleList.h @@ -44,12 +44,11 @@ template <class S> S modWorkaround(S const& _a, S const& _b) return (S)(bigint(_a) % bigint(_b)); } -/// @returns a list of simplification rules given certain match placeholders. -/// A, B and C should represent constants, X and Y arbitrary expressions. -/// The simplifications should neven change the order of evaluation of -/// arbitrary operations. +// This part of simplificationRuleList below was split out to prevent +// stack overflows in the JavaScript optimizer for emscripten builds +// that affected certain browser versions. template <class Pattern> -std::vector<SimplificationRule<Pattern>> simplificationRuleList( +std::vector<SimplificationRule<Pattern>> simplificationRuleListPart1( Pattern A, Pattern B, Pattern C, @@ -57,9 +56,8 @@ std::vector<SimplificationRule<Pattern>> simplificationRuleList( Pattern Y ) { - std::vector<SimplificationRule<Pattern>> rules; - rules += std::vector<SimplificationRule<Pattern>>{ - // arithmetics on constants + return std::vector<SimplificationRule<Pattern>> { + // arithmetic on constants {{Instruction::ADD, {A, B}}, [=]{ return A.d() + B.d(); }, false}, {{Instruction::MUL, {A, B}}, [=]{ return A.d() * B.d(); }, false}, {{Instruction::SUB, {A, B}}, [=]{ return A.d() - B.d(); }, false}, @@ -162,6 +160,22 @@ std::vector<SimplificationRule<Pattern>> simplificationRuleList( {{Instruction::OR, {X, {Instruction::NOT, {X}}}}, [=]{ return ~u256(0); }, true}, {{Instruction::OR, {{Instruction::NOT, {X}}, X}}, [=]{ return ~u256(0); }, true}, }; +} + + +// This part of simplificationRuleList below was split out to prevent +// stack overflows in the JavaScript optimizer for emscripten builds +// that affected certain browser versions. +template <class Pattern> +std::vector<SimplificationRule<Pattern>> simplificationRuleListPart2( + Pattern A, + Pattern B, + Pattern, + Pattern X, + Pattern Y +) +{ + std::vector<SimplificationRule<Pattern>> rules; // Replace MOD X, <power-of-two> with AND X, <power-of-two> - 1 for (size_t i = 0; i < 256; ++i) @@ -292,5 +306,24 @@ std::vector<SimplificationRule<Pattern>> simplificationRuleList( return rules; } +/// @returns a list of simplification rules given certain match placeholders. +/// A, B and C should represent constants, X and Y arbitrary expressions. +/// The simplifications should never change the order of evaluation of +/// arbitrary operations. +template <class Pattern> +std::vector<SimplificationRule<Pattern>> simplificationRuleList( + Pattern A, + Pattern B, + Pattern C, + Pattern X, + Pattern Y +) +{ + std::vector<SimplificationRule<Pattern>> rules; + rules += simplificationRuleListPart1(A, B, C, X, Y); + rules += simplificationRuleListPart2(A, B, C, X, Y); + return rules; +} + } } diff --git a/libevmasm/SemanticInformation.cpp b/libevmasm/SemanticInformation.cpp index 71267ee8..78f3c9c7 100644 --- a/libevmasm/SemanticInformation.cpp +++ b/libevmasm/SemanticInformation.cpp @@ -151,6 +151,7 @@ bool SemanticInformation::isDeterministic(AssemblyItem const& _item) case Instruction::MSIZE: // depends on previous writes and reads, not only on content case Instruction::BALANCE: // depends on previous calls case Instruction::EXTCODESIZE: + case Instruction::EXTCODEHASH: case Instruction::RETURNDATACOPY: // depends on previous calls case Instruction::RETURNDATASIZE: return false; @@ -172,6 +173,7 @@ bool SemanticInformation::movable(Instruction _instruction) case Instruction::KECCAK256: case Instruction::BALANCE: case Instruction::EXTCODESIZE: + case Instruction::EXTCODEHASH: case Instruction::RETURNDATASIZE: case Instruction::SLOAD: case Instruction::PC: @@ -233,6 +235,7 @@ bool SemanticInformation::invalidInPureFunctions(Instruction _instruction) case Instruction::GASPRICE: case Instruction::EXTCODESIZE: case Instruction::EXTCODECOPY: + case Instruction::EXTCODEHASH: case Instruction::BLOCKHASH: case Instruction::COINBASE: case Instruction::TIMESTAMP: diff --git a/libevmasm/SimplificationRules.cpp b/libevmasm/SimplificationRules.cpp index 53a5f9fc..120d1787 100644 --- a/libevmasm/SimplificationRules.cpp +++ b/libevmasm/SimplificationRules.cpp @@ -21,16 +21,19 @@ * Container for equivalence classes of expressions for use in common subexpression elimination. */ +#include <libevmasm/SimplificationRules.h> + #include <libevmasm/ExpressionClasses.h> -#include <utility> -#include <functional> -#include <boost/range/adaptor/reversed.hpp> -#include <boost/noncopyable.hpp> #include <libevmasm/Assembly.h> #include <libevmasm/CommonSubexpressionEliminator.h> -#include <libevmasm/SimplificationRules.h> - #include <libevmasm/RuleList.h> +#include <libdevcore/Assertions.h> + +#include <boost/range/adaptor/reversed.hpp> +#include <boost/noncopyable.hpp> + +#include <utility> +#include <functional> using namespace std; using namespace dev; @@ -45,7 +48,7 @@ SimplificationRule<Pattern> const* Rules::findFirstMatch( resetMatchGroups(); assertThrow(_expr.item, OptimizerException, ""); - for (auto const& rule: m_rules[byte(_expr.item->instruction())]) + for (auto const& rule: m_rules[uint8_t(_expr.item->instruction())]) { if (rule.pattern.matches(_expr, _classes)) return &rule; @@ -54,6 +57,11 @@ SimplificationRule<Pattern> const* Rules::findFirstMatch( return nullptr; } +bool Rules::isInitialized() const +{ + return !m_rules[uint8_t(Instruction::ADD)].empty(); +} + void Rules::addRules(std::vector<SimplificationRule<Pattern>> const& _rules) { for (auto const& r: _rules) @@ -62,12 +70,12 @@ void Rules::addRules(std::vector<SimplificationRule<Pattern>> const& _rules) void Rules::addRule(SimplificationRule<Pattern> const& _rule) { - m_rules[byte(_rule.pattern.instruction())].push_back(_rule); + m_rules[uint8_t(_rule.pattern.instruction())].push_back(_rule); } Rules::Rules() { - // Multiple occurences of one of these inside one rule must match the same equivalence class. + // Multiple occurrences of one of these inside one rule must match the same equivalence class. // Constants. Pattern A(Push); Pattern B(Push); @@ -82,6 +90,7 @@ Rules::Rules() Y.setMatchGroup(5, m_matchGroups); addRules(simplificationRuleList(A, B, C, X, Y)); + assertThrow(isInitialized(), OptimizerException, "Rule list not properly initialized."); } Pattern::Pattern(Instruction _instruction, std::vector<Pattern> const& _arguments): diff --git a/libevmasm/SimplificationRules.h b/libevmasm/SimplificationRules.h index 53f7e595..fbe5a2b0 100644 --- a/libevmasm/SimplificationRules.h +++ b/libevmasm/SimplificationRules.h @@ -26,6 +26,8 @@ #include <libevmasm/ExpressionClasses.h> #include <libevmasm/SimplificationRule.h> +#include <boost/noncopyable.hpp> + #include <functional> #include <vector> @@ -53,6 +55,10 @@ public: ExpressionClasses const& _classes ); + /// Checks whether the rulelist is non-empty. This is usually enforced + /// by the constructor, but we had some issues with static initialization. + bool isInitialized() const; + private: void addRules(std::vector<SimplificationRule<Pattern>> const& _rules); void addRule(SimplificationRule<Pattern> const& _rule); diff --git a/libjulia/ASTDataForward.h b/libjulia/ASTDataForward.h deleted file mode 100644 index 143b9c46..00000000 --- a/libjulia/ASTDataForward.h +++ /dev/null @@ -1,54 +0,0 @@ -/* - This file is part of solidity. - - solidity is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - solidity is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with solidity. If not, see <http://www.gnu.org/licenses/>. -*/ -/** - * @date 2017 - * Pull in some identifiers from the solidity::assembly namespace. - */ - -#pragma once - -#include <libsolidity/inlineasm/AsmDataForward.h> - -namespace dev -{ -namespace julia -{ - -using Instruction = solidity::assembly::Instruction; -using Literal = solidity::assembly::Literal; -using Label = solidity::assembly::Label; -using StackAssignment = solidity::assembly::StackAssignment; -using Identifier = solidity::assembly::Identifier; -using Assignment = solidity::assembly::Assignment; -using VariableDeclaration = solidity::assembly::VariableDeclaration; -using FunctionalInstruction = solidity::assembly::FunctionalInstruction; -using FunctionDefinition = solidity::assembly::FunctionDefinition; -using FunctionCall = solidity::assembly::FunctionCall; -using If = solidity::assembly::If; -using Case = solidity::assembly::Case; -using Switch = solidity::assembly::Switch; -using ForLoop = solidity::assembly::ForLoop; -using ExpressionStatement = solidity::assembly::ExpressionStatement; -using Block = solidity::assembly::Block; - -using TypedName = solidity::assembly::TypedName; - -using Expression = boost::variant<FunctionalInstruction, FunctionCall, Identifier, Literal>; -using Statement = boost::variant<ExpressionStatement, Instruction, Label, StackAssignment, Assignment, VariableDeclaration, FunctionDefinition, If, Switch, ForLoop, Block>; - -} -} diff --git a/libjulia/Exceptions.h b/libjulia/Exceptions.h deleted file mode 100644 index 20ab6520..00000000 --- a/libjulia/Exceptions.h +++ /dev/null @@ -1,35 +0,0 @@ -/* - This file is part of solidity. - - solidity is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - solidity is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with solidity. If not, see <http://www.gnu.org/licenses/>. -*/ -/** - * Exceptions in Julia. - */ - -#pragma once - -#include <libdevcore/Exceptions.h> -#include <libdevcore/Assertions.h> - -namespace dev -{ -namespace julia -{ - -struct IuliaException: virtual Exception {}; -struct OptimizerException: virtual IuliaException {}; - -} -} diff --git a/libjulia/backends/evm/AbstractAssembly.h b/libjulia/backends/evm/AbstractAssembly.h deleted file mode 100644 index 8e90a912..00000000 --- a/libjulia/backends/evm/AbstractAssembly.h +++ /dev/null @@ -1,119 +0,0 @@ -/* - This file is part of solidity. - - solidity is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - solidity is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with solidity. If not, see <http://www.gnu.org/licenses/>. -*/ -/** - * @date 2017 - * Abstract assembly interface, subclasses of which are to be used with the generic - * bytecode generator. - */ - -#pragma once - -#include <libdevcore/CommonData.h> - -#include <functional> - -namespace dev -{ -struct SourceLocation; -namespace solidity -{ -enum class Instruction: uint8_t; -namespace assembly -{ -struct Instruction; -struct Identifier; -} -} -namespace julia -{ - -/// -/// Assembly class that abstracts both the libevmasm assembly and the new julia evm assembly. -/// -class AbstractAssembly -{ -public: - using LabelID = size_t; - - virtual ~AbstractAssembly() {} - - /// Set a new source location valid starting from the next instruction. - virtual void setSourceLocation(SourceLocation const& _location) = 0; - /// Retrieve the current height of the stack. This does not have to be zero - /// at the beginning. - virtual int stackHeight() const = 0; - /// Append an EVM instruction. - virtual void appendInstruction(solidity::Instruction _instruction) = 0; - /// Append a constant. - virtual void appendConstant(u256 const& _constant) = 0; - /// Append a label. - virtual void appendLabel(LabelID _labelId) = 0; - /// Append a label reference. - virtual void appendLabelReference(LabelID _labelId) = 0; - /// Generate a new unique label. - virtual LabelID newLabelId() = 0; - /// Returns a label identified by the given name. Creates it if it does not yet exist. - virtual LabelID namedLabel(std::string const& _name) = 0; - /// Append a reference to a to-be-linked symobl. - /// Currently, we assume that the value is always a 20 byte number. - virtual void appendLinkerSymbol(std::string const& _name) = 0; - - /// Append a jump instruction. - /// @param _stackDiffAfter the stack adjustment after this instruction. - /// This is helpful to stack height analysis if there is no continuing control flow. - virtual void appendJump(int _stackDiffAfter) = 0; - - /// Append a jump-to-immediate operation. - /// @param _stackDiffAfter the stack adjustment after this instruction. - virtual void appendJumpTo(LabelID _labelId, int _stackDiffAfter = 0) = 0; - /// Append a jump-to-if-immediate operation. - virtual void appendJumpToIf(LabelID _labelId) = 0; - /// Start a subroutine identified by @a _labelId that takes @a _arguments - /// stack slots as arguments. - virtual void appendBeginsub(LabelID _labelId, int _arguments) = 0; - /// Call a subroutine identified by @a _labelId, taking @a _arguments from the - /// stack upon call and putting @a _returns arguments onto the stack upon return. - virtual void appendJumpsub(LabelID _labelId, int _arguments, int _returns) = 0; - /// Return from a subroutine. - /// @param _stackDiffAfter the stack adjustment after this instruction. - virtual void appendReturnsub(int _returns, int _stackDiffAfter = 0) = 0; - - /// Append the assembled size as a constant. - virtual void appendAssemblySize() = 0; -}; - -enum class IdentifierContext { LValue, RValue }; - -/// Object that is used to resolve references and generate code for access to identifiers external -/// to inline assembly (not used in standalone assembly mode). -struct ExternalIdentifierAccess -{ - using Resolver = std::function<size_t(solidity::assembly::Identifier const&, IdentifierContext, bool /*_crossesFunctionBoundary*/)>; - /// Resolve a an external reference given by the identifier in the given context. - /// @returns the size of the value (number of stack slots) or size_t(-1) if not found. - Resolver resolve; - using CodeGenerator = std::function<void(solidity::assembly::Identifier const&, IdentifierContext, julia::AbstractAssembly&)>; - /// Generate code for retrieving the value (rvalue context) or storing the value (lvalue context) - /// of an identifier. The code should be appended to the assembly. In rvalue context, the value is supposed - /// to be put onto the stack, in lvalue context, the value is assumed to be at the top of the stack. - CodeGenerator generateCode; -}; - - - -} -} diff --git a/libjulia/backends/evm/EVMAssembly.cpp b/libjulia/backends/evm/EVMAssembly.cpp deleted file mode 100644 index 07ad05c9..00000000 --- a/libjulia/backends/evm/EVMAssembly.cpp +++ /dev/null @@ -1,202 +0,0 @@ -/* - This file is part of solidity. - - solidity is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - solidity is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with solidity. If not, see <http://www.gnu.org/licenses/>. -*/ -/** - * Assembly interface for EVM and EVM1.5. - */ - -#include <libjulia/backends/evm/EVMAssembly.h> - -#include <libevmasm/Instruction.h> - -#include <libsolidity/interface/Exceptions.h> - -using namespace std; -using namespace dev; -using namespace dev::julia; - -namespace -{ -/// Size of labels in bytes. Four-byte labels are required by some EVM1.5 instructions. -size_t constexpr labelReferenceSize = 4; - -size_t constexpr assemblySizeReferenceSize = 4; -} - - -void EVMAssembly::setSourceLocation(SourceLocation const&) -{ - // Ignored for now; -} - -void EVMAssembly::appendInstruction(solidity::Instruction _instr) -{ - m_bytecode.push_back(byte(_instr)); - m_stackHeight += solidity::instructionInfo(_instr).ret - solidity::instructionInfo(_instr).args; -} - -void EVMAssembly::appendConstant(u256 const& _constant) -{ - bytes data = toCompactBigEndian(_constant, 1); - appendInstruction(solidity::pushInstruction(data.size())); - m_bytecode += data; -} - -void EVMAssembly::appendLabel(LabelID _labelId) -{ - setLabelToCurrentPosition(_labelId); - appendInstruction(solidity::Instruction::JUMPDEST); -} - -void EVMAssembly::appendLabelReference(LabelID _labelId) -{ - solAssert(!m_evm15, "Cannot use plain label references in EMV1.5 mode."); - // @TODO we now always use labelReferenceSize for all labels, it could be shortened - // for some of them. - appendInstruction(solidity::pushInstruction(labelReferenceSize)); - m_labelReferences[m_bytecode.size()] = _labelId; - m_bytecode += bytes(labelReferenceSize); -} - -EVMAssembly::LabelID EVMAssembly::newLabelId() -{ - m_labelPositions[m_nextLabelId] = size_t(-1); - return m_nextLabelId++; -} - -AbstractAssembly::LabelID EVMAssembly::namedLabel(string const& _name) -{ - solAssert(!_name.empty(), ""); - if (!m_namedLabels.count(_name)) - m_namedLabels[_name] = newLabelId(); - return m_namedLabels[_name]; -} - -void EVMAssembly::appendLinkerSymbol(string const&) -{ - solAssert(false, "Linker symbols not yet implemented."); -} - -void EVMAssembly::appendJump(int _stackDiffAfter) -{ - solAssert(!m_evm15, "Plain JUMP used for EVM 1.5"); - appendInstruction(solidity::Instruction::JUMP); - m_stackHeight += _stackDiffAfter; -} - -void EVMAssembly::appendJumpTo(LabelID _labelId, int _stackDiffAfter) -{ - if (m_evm15) - { - m_bytecode.push_back(byte(solidity::Instruction::JUMPTO)); - appendLabelReferenceInternal(_labelId); - m_stackHeight += _stackDiffAfter; - } - else - { - appendLabelReference(_labelId); - appendJump(_stackDiffAfter); - } -} - -void EVMAssembly::appendJumpToIf(LabelID _labelId) -{ - if (m_evm15) - { - m_bytecode.push_back(byte(solidity::Instruction::JUMPIF)); - appendLabelReferenceInternal(_labelId); - m_stackHeight--; - } - else - { - appendLabelReference(_labelId); - appendInstruction(solidity::Instruction::JUMPI); - } -} - -void EVMAssembly::appendBeginsub(LabelID _labelId, int _arguments) -{ - solAssert(m_evm15, "BEGINSUB used for EVM 1.0"); - solAssert(_arguments >= 0, ""); - setLabelToCurrentPosition(_labelId); - m_bytecode.push_back(byte(solidity::Instruction::BEGINSUB)); - m_stackHeight += _arguments; -} - -void EVMAssembly::appendJumpsub(LabelID _labelId, int _arguments, int _returns) -{ - solAssert(m_evm15, "JUMPSUB used for EVM 1.0"); - solAssert(_arguments >= 0 && _returns >= 0, ""); - m_bytecode.push_back(byte(solidity::Instruction::JUMPSUB)); - appendLabelReferenceInternal(_labelId); - m_stackHeight += _returns - _arguments; -} - -void EVMAssembly::appendReturnsub(int _returns, int _stackDiffAfter) -{ - solAssert(m_evm15, "RETURNSUB used for EVM 1.0"); - solAssert(_returns >= 0, ""); - m_bytecode.push_back(byte(solidity::Instruction::RETURNSUB)); - m_stackHeight += _stackDiffAfter - _returns; -} - -eth::LinkerObject EVMAssembly::finalize() -{ - size_t bytecodeSize = m_bytecode.size(); - for (auto const& ref: m_assemblySizePositions) - updateReference(ref, assemblySizeReferenceSize, u256(bytecodeSize)); - - for (auto const& ref: m_labelReferences) - { - size_t referencePos = ref.first; - solAssert(m_labelPositions.count(ref.second), ""); - size_t labelPos = m_labelPositions.at(ref.second); - solAssert(labelPos != size_t(-1), "Undefined but allocated label used."); - updateReference(referencePos, labelReferenceSize, u256(labelPos)); - } - - eth::LinkerObject obj; - obj.bytecode = m_bytecode; - return obj; -} - -void EVMAssembly::setLabelToCurrentPosition(LabelID _labelId) -{ - solAssert(m_labelPositions.count(_labelId), "Label not found."); - solAssert(m_labelPositions[_labelId] == size_t(-1), "Label already set."); - m_labelPositions[_labelId] = m_bytecode.size(); -} - -void EVMAssembly::appendLabelReferenceInternal(LabelID _labelId) -{ - m_labelReferences[m_bytecode.size()] = _labelId; - m_bytecode += bytes(labelReferenceSize); -} - -void EVMAssembly::appendAssemblySize() -{ - appendInstruction(solidity::pushInstruction(assemblySizeReferenceSize)); - m_assemblySizePositions.push_back(m_bytecode.size()); - m_bytecode += bytes(assemblySizeReferenceSize); -} - -void EVMAssembly::updateReference(size_t pos, size_t size, u256 value) -{ - solAssert(m_bytecode.size() >= size && pos <= m_bytecode.size() - size, ""); - solAssert(value < (u256(1) << (8 * size)), ""); - for (size_t i = 0; i < size; i++) - m_bytecode[pos + i] = byte((value >> (8 * (size - i - 1))) & 0xff); -} diff --git a/libjulia/backends/evm/EVMAssembly.h b/libjulia/backends/evm/EVMAssembly.h deleted file mode 100644 index 593cee6a..00000000 --- a/libjulia/backends/evm/EVMAssembly.h +++ /dev/null @@ -1,97 +0,0 @@ -/* - This file is part of solidity. - - solidity is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - solidity is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with solidity. If not, see <http://www.gnu.org/licenses/>. -*/ -/** - * Assembly interface for EVM and EVM1.5. - */ - -#pragma once - -#include <libjulia/backends/evm/AbstractAssembly.h> - -#include <libevmasm/LinkerObject.h> - -#include <map> - -namespace dev -{ -namespace julia -{ - -class EVMAssembly: public AbstractAssembly -{ -public: - explicit EVMAssembly(bool _evm15 = false): m_evm15(_evm15) { } - virtual ~EVMAssembly() {} - - /// Set a new source location valid starting from the next instruction. - virtual void setSourceLocation(SourceLocation const& _location) override; - /// Retrieve the current height of the stack. This does not have to be zero - /// at the beginning. - virtual int stackHeight() const override { return m_stackHeight; } - /// Append an EVM instruction. - virtual void appendInstruction(solidity::Instruction _instruction) override; - /// Append a constant. - virtual void appendConstant(u256 const& _constant) override; - /// Append a label. - virtual void appendLabel(LabelID _labelId) override; - /// Append a label reference. - virtual void appendLabelReference(LabelID _labelId) override; - /// Generate a new unique label. - virtual LabelID newLabelId() override; - /// Returns a label identified by the given name. Creates it if it does not yet exist. - virtual LabelID namedLabel(std::string const& _name) override; - /// Append a reference to a to-be-linked symobl. - /// Currently, we assume that the value is always a 20 byte number. - virtual void appendLinkerSymbol(std::string const& _name) override; - - /// Append a jump instruction. - /// @param _stackDiffAfter the stack adjustment after this instruction. - virtual void appendJump(int _stackDiffAfter) override; - /// Append a jump-to-immediate operation. - virtual void appendJumpTo(LabelID _labelId, int _stackDiffAfter) override; - /// Append a jump-to-if-immediate operation. - virtual void appendJumpToIf(LabelID _labelId) override; - /// Start a subroutine. - virtual void appendBeginsub(LabelID _labelId, int _arguments) override; - /// Call a subroutine. - virtual void appendJumpsub(LabelID _labelId, int _arguments, int _returns) override; - /// Return from a subroutine. - virtual void appendReturnsub(int _returns, int _stackDiffAfter) override; - - /// Append the assembled size as a constant. - virtual void appendAssemblySize() override; - - /// Resolves references inside the bytecode and returns the linker object. - eth::LinkerObject finalize(); - -private: - void setLabelToCurrentPosition(AbstractAssembly::LabelID _labelId); - void appendLabelReferenceInternal(AbstractAssembly::LabelID _labelId); - void updateReference(size_t pos, size_t size, u256 value); - - bool m_evm15 = false; ///< if true, switch to evm1.5 mode - LabelID m_nextLabelId = 0; - int m_stackHeight = 0; - bytes m_bytecode; - std::map<std::string, LabelID> m_namedLabels; - std::map<LabelID, size_t> m_labelPositions; - std::map<size_t, LabelID> m_labelReferences; - std::vector<size_t> m_assemblySizePositions; -}; - -} -} diff --git a/libjulia/backends/evm/EVMCodeTransform.cpp b/libjulia/backends/evm/EVMCodeTransform.cpp deleted file mode 100644 index 2a97429b..00000000 --- a/libjulia/backends/evm/EVMCodeTransform.cpp +++ /dev/null @@ -1,555 +0,0 @@ -/* - This file is part of solidity. - - solidity is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - solidity is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with solidity. If not, see <http://www.gnu.org/licenses/>. -*/ -/** - * Common code generator for translating Julia / inline assembly to EVM and EVM1.5. - */ - -#include <libjulia/backends/evm/EVMCodeTransform.h> - -#include <libsolidity/inlineasm/AsmAnalysisInfo.h> -#include <libsolidity/inlineasm/AsmData.h> - -#include <libsolidity/interface/Exceptions.h> - -#include <boost/range/adaptor/reversed.hpp> - -using namespace std; -using namespace dev; -using namespace dev::julia; -using namespace dev::solidity; - -using Scope = dev::solidity::assembly::Scope; - -void CodeTransform::operator()(VariableDeclaration const& _varDecl) -{ - solAssert(m_scope, ""); - - int const numVariables = _varDecl.variables.size(); - int height = m_assembly.stackHeight(); - if (_varDecl.value) - { - boost::apply_visitor(*this, *_varDecl.value); - expectDeposit(numVariables, height); - } - else - { - int variablesLeft = numVariables; - while (variablesLeft--) - m_assembly.appendConstant(u256(0)); - } - for (auto const& variable: _varDecl.variables) - { - auto& var = boost::get<Scope::Variable>(m_scope->identifiers.at(variable.name)); - m_context->variableStackHeights[&var] = height++; - } - checkStackHeight(&_varDecl); -} - -void CodeTransform::operator()(Assignment const& _assignment) -{ - int height = m_assembly.stackHeight(); - boost::apply_visitor(*this, *_assignment.value); - expectDeposit(_assignment.variableNames.size(), height); - - m_assembly.setSourceLocation(_assignment.location); - generateMultiAssignment(_assignment.variableNames); - checkStackHeight(&_assignment); -} - -void CodeTransform::operator()(StackAssignment const& _assignment) -{ - m_assembly.setSourceLocation(_assignment.location); - generateAssignment(_assignment.variableName); - checkStackHeight(&_assignment); -} - -void CodeTransform::operator()(ExpressionStatement const& _statement) -{ - m_assembly.setSourceLocation(_statement.location); - boost::apply_visitor(*this, _statement.expression); - checkStackHeight(&_statement); -} - -void CodeTransform::operator()(Label const& _label) -{ - m_assembly.setSourceLocation(_label.location); - solAssert(m_scope, ""); - solAssert(m_scope->identifiers.count(_label.name), ""); - Scope::Label& label = boost::get<Scope::Label>(m_scope->identifiers.at(_label.name)); - m_assembly.appendLabel(labelID(label)); - checkStackHeight(&_label); -} - -void CodeTransform::operator()(FunctionCall const& _call) -{ - solAssert(m_scope, ""); - - m_assembly.setSourceLocation(_call.location); - EVMAssembly::LabelID returnLabel(-1); // only used for evm 1.0 - if (!m_evm15) - { - returnLabel = m_assembly.newLabelId(); - m_assembly.appendLabelReference(returnLabel); - m_stackAdjustment++; - } - - Scope::Function* function = nullptr; - solAssert(m_scope->lookup(_call.functionName.name, Scope::NonconstVisitor( - [=](Scope::Variable&) { solAssert(false, "Expected function name."); }, - [=](Scope::Label&) { solAssert(false, "Expected function name."); }, - [&](Scope::Function& _function) { function = &_function; } - )), "Function name not found."); - solAssert(function, ""); - solAssert(function->arguments.size() == _call.arguments.size(), ""); - for (auto const& arg: _call.arguments | boost::adaptors::reversed) - visitExpression(arg); - m_assembly.setSourceLocation(_call.location); - if (m_evm15) - m_assembly.appendJumpsub(functionEntryID(_call.functionName.name, *function), function->arguments.size(), function->returns.size()); - else - { - m_assembly.appendJumpTo(functionEntryID(_call.functionName.name, *function), function->returns.size() - function->arguments.size() - 1); - m_assembly.appendLabel(returnLabel); - m_stackAdjustment--; - } - checkStackHeight(&_call); -} - -void CodeTransform::operator()(FunctionalInstruction const& _instruction) -{ - if (m_evm15 && ( - _instruction.instruction == solidity::Instruction::JUMP || - _instruction.instruction == solidity::Instruction::JUMPI - )) - { - bool const isJumpI = _instruction.instruction == solidity::Instruction::JUMPI; - if (isJumpI) - { - solAssert(_instruction.arguments.size() == 2, ""); - visitExpression(_instruction.arguments.at(1)); - } - else - { - solAssert(_instruction.arguments.size() == 1, ""); - } - m_assembly.setSourceLocation(_instruction.location); - auto label = labelFromIdentifier(boost::get<assembly::Identifier>(_instruction.arguments.at(0))); - if (isJumpI) - m_assembly.appendJumpToIf(label); - else - m_assembly.appendJumpTo(label); - } - else - { - for (auto const& arg: _instruction.arguments | boost::adaptors::reversed) - visitExpression(arg); - m_assembly.setSourceLocation(_instruction.location); - m_assembly.appendInstruction(_instruction.instruction); - } - checkStackHeight(&_instruction); -} - -void CodeTransform::operator()(assembly::Identifier const& _identifier) -{ - m_assembly.setSourceLocation(_identifier.location); - // First search internals, then externals. - solAssert(m_scope, ""); - if (m_scope->lookup(_identifier.name, Scope::NonconstVisitor( - [=](Scope::Variable& _var) - { - if (int heightDiff = variableHeightDiff(_var, false)) - m_assembly.appendInstruction(solidity::dupInstruction(heightDiff)); - else - // Store something to balance the stack - m_assembly.appendConstant(u256(0)); - }, - [=](Scope::Label& _label) - { - m_assembly.appendLabelReference(labelID(_label)); - }, - [=](Scope::Function&) - { - solAssert(false, "Function not removed during desugaring."); - } - ))) - { - return; - } - solAssert( - m_identifierAccess.generateCode, - "Identifier not found and no external access available." - ); - m_identifierAccess.generateCode(_identifier, IdentifierContext::RValue, m_assembly); - checkStackHeight(&_identifier); -} - -void CodeTransform::operator()(assembly::Literal const& _literal) -{ - m_assembly.setSourceLocation(_literal.location); - if (_literal.kind == assembly::LiteralKind::Number) - m_assembly.appendConstant(u256(_literal.value)); - else if (_literal.kind == assembly::LiteralKind::Boolean) - { - if (_literal.value == "true") - m_assembly.appendConstant(u256(1)); - else - m_assembly.appendConstant(u256(0)); - } - else - { - solAssert(_literal.value.size() <= 32, ""); - m_assembly.appendConstant(u256(h256(_literal.value, h256::FromBinary, h256::AlignLeft))); - } - checkStackHeight(&_literal); -} - -void CodeTransform::operator()(assembly::Instruction const& _instruction) -{ - solAssert(!m_evm15 || _instruction.instruction != solidity::Instruction::JUMP, "Bare JUMP instruction used for EVM1.5"); - solAssert(!m_evm15 || _instruction.instruction != solidity::Instruction::JUMPI, "Bare JUMPI instruction used for EVM1.5"); - m_assembly.setSourceLocation(_instruction.location); - m_assembly.appendInstruction(_instruction.instruction); - checkStackHeight(&_instruction); -} - -void CodeTransform::operator()(If const& _if) -{ - visitExpression(*_if.condition); - m_assembly.setSourceLocation(_if.location); - m_assembly.appendInstruction(solidity::Instruction::ISZERO); - AbstractAssembly::LabelID end = m_assembly.newLabelId(); - m_assembly.appendJumpToIf(end); - (*this)(_if.body); - m_assembly.setSourceLocation(_if.location); - m_assembly.appendLabel(end); - checkStackHeight(&_if); -} - -void CodeTransform::operator()(Switch const& _switch) -{ - //@TODO use JUMPV in EVM1.5? - - visitExpression(*_switch.expression); - int expressionHeight = m_assembly.stackHeight(); - map<Case const*, AbstractAssembly::LabelID> caseBodies; - AbstractAssembly::LabelID end = m_assembly.newLabelId(); - for (Case const& c: _switch.cases) - { - if (c.value) - { - (*this)(*c.value); - m_assembly.setSourceLocation(c.location); - AbstractAssembly::LabelID bodyLabel = m_assembly.newLabelId(); - caseBodies[&c] = bodyLabel; - solAssert(m_assembly.stackHeight() == expressionHeight + 1, ""); - m_assembly.appendInstruction(solidity::dupInstruction(2)); - m_assembly.appendInstruction(solidity::Instruction::EQ); - m_assembly.appendJumpToIf(bodyLabel); - } - else - // default case - (*this)(c.body); - } - m_assembly.setSourceLocation(_switch.location); - m_assembly.appendJumpTo(end); - - size_t numCases = caseBodies.size(); - for (auto const& c: caseBodies) - { - m_assembly.setSourceLocation(c.first->location); - m_assembly.appendLabel(c.second); - (*this)(c.first->body); - // Avoid useless "jump to next" for the last case. - if (--numCases > 0) - { - m_assembly.setSourceLocation(c.first->location); - m_assembly.appendJumpTo(end); - } - } - - m_assembly.setSourceLocation(_switch.location); - m_assembly.appendLabel(end); - m_assembly.appendInstruction(solidity::Instruction::POP); - checkStackHeight(&_switch); -} - -void CodeTransform::operator()(FunctionDefinition const& _function) -{ - solAssert(m_scope, ""); - solAssert(m_scope->identifiers.count(_function.name), ""); - Scope::Function& function = boost::get<Scope::Function>(m_scope->identifiers.at(_function.name)); - - int const localStackAdjustment = m_evm15 ? 0 : 1; - int height = localStackAdjustment; - solAssert(m_info.scopes.at(&_function.body), ""); - Scope* varScope = m_info.scopes.at(m_info.virtualBlocks.at(&_function).get()).get(); - solAssert(varScope, ""); - for (auto const& v: _function.parameters | boost::adaptors::reversed) - { - auto& var = boost::get<Scope::Variable>(varScope->identifiers.at(v.name)); - m_context->variableStackHeights[&var] = height++; - } - - m_assembly.setSourceLocation(_function.location); - int stackHeightBefore = m_assembly.stackHeight(); - AbstractAssembly::LabelID afterFunction = m_assembly.newLabelId(); - - if (m_evm15) - { - m_assembly.appendJumpTo(afterFunction, -stackHeightBefore); - m_assembly.appendBeginsub(functionEntryID(_function.name, function), _function.parameters.size()); - } - else - { - m_assembly.appendJumpTo(afterFunction, -stackHeightBefore + height); - m_assembly.appendLabel(functionEntryID(_function.name, function)); - } - m_stackAdjustment += localStackAdjustment; - - for (auto const& v: _function.returnVariables) - { - auto& var = boost::get<Scope::Variable>(varScope->identifiers.at(v.name)); - m_context->variableStackHeights[&var] = height++; - // Preset stack slots for return variables to zero. - m_assembly.appendConstant(u256(0)); - } - - CodeTransform( - m_assembly, - m_info, - m_julia, - m_evm15, - m_identifierAccess, - m_useNamedLabelsForFunctions, - localStackAdjustment, - m_context - )(_function.body); - - { - // The stack layout here is: - // <return label>? <arguments...> <return values...> - // But we would like it to be: - // <return values...> <return label>? - // So we have to append some SWAP and POP instructions. - - // This vector holds the desired target positions of all stack slots and is - // modified parallel to the actual stack. - vector<int> stackLayout; - if (!m_evm15) - stackLayout.push_back(_function.returnVariables.size()); // Move return label to the top - stackLayout += vector<int>(_function.parameters.size(), -1); // discard all arguments - for (size_t i = 0; i < _function.returnVariables.size(); ++i) - stackLayout.push_back(i); // Move return values down, but keep order. - - solAssert(stackLayout.size() <= 17, "Stack too deep"); - while (!stackLayout.empty() && stackLayout.back() != int(stackLayout.size() - 1)) - if (stackLayout.back() < 0) - { - m_assembly.appendInstruction(solidity::Instruction::POP); - stackLayout.pop_back(); - } - else - { - m_assembly.appendInstruction(swapInstruction(stackLayout.size() - stackLayout.back() - 1)); - swap(stackLayout[stackLayout.back()], stackLayout.back()); - } - for (int i = 0; size_t(i) < stackLayout.size(); ++i) - solAssert(i == stackLayout[i], "Error reshuffling stack."); - } - - if (m_evm15) - m_assembly.appendReturnsub(_function.returnVariables.size(), stackHeightBefore); - else - m_assembly.appendJump(stackHeightBefore - _function.returnVariables.size()); - m_stackAdjustment -= localStackAdjustment; - m_assembly.appendLabel(afterFunction); - checkStackHeight(&_function); -} - -void CodeTransform::operator()(ForLoop const& _forLoop) -{ - Scope* originalScope = m_scope; - // We start with visiting the block, but not finalizing it. - m_scope = m_info.scopes.at(&_forLoop.pre).get(); - int stackStartHeight = m_assembly.stackHeight(); - - visitStatements(_forLoop.pre.statements); - - // TODO: When we implement break and continue, the labels and the stack heights at that point - // have to be stored in a stack. - AbstractAssembly::LabelID loopStart = m_assembly.newLabelId(); - AbstractAssembly::LabelID loopEnd = m_assembly.newLabelId(); - AbstractAssembly::LabelID postPart = m_assembly.newLabelId(); - - m_assembly.setSourceLocation(_forLoop.location); - m_assembly.appendLabel(loopStart); - - visitExpression(*_forLoop.condition); - m_assembly.setSourceLocation(_forLoop.location); - m_assembly.appendInstruction(solidity::Instruction::ISZERO); - m_assembly.appendJumpToIf(loopEnd); - - (*this)(_forLoop.body); - - m_assembly.setSourceLocation(_forLoop.location); - m_assembly.appendLabel(postPart); - - (*this)(_forLoop.post); - - m_assembly.setSourceLocation(_forLoop.location); - m_assembly.appendJumpTo(loopStart); - m_assembly.appendLabel(loopEnd); - - finalizeBlock(_forLoop.pre, stackStartHeight); - m_scope = originalScope; -} - -void CodeTransform::operator()(Block const& _block) -{ - Scope* originalScope = m_scope; - m_scope = m_info.scopes.at(&_block).get(); - - int blockStartStackHeight = m_assembly.stackHeight(); - visitStatements(_block.statements); - - finalizeBlock(_block, blockStartStackHeight); - m_scope = originalScope; -} - -AbstractAssembly::LabelID CodeTransform::labelFromIdentifier(Identifier const& _identifier) -{ - AbstractAssembly::LabelID label = AbstractAssembly::LabelID(-1); - if (!m_scope->lookup(_identifier.name, Scope::NonconstVisitor( - [=](Scope::Variable&) { solAssert(false, "Expected label"); }, - [&](Scope::Label& _label) - { - label = labelID(_label); - }, - [=](Scope::Function&) { solAssert(false, "Expected label"); } - ))) - { - solAssert(false, "Identifier not found."); - } - return label; -} - -AbstractAssembly::LabelID CodeTransform::labelID(Scope::Label const& _label) -{ - if (!m_context->labelIDs.count(&_label)) - m_context->labelIDs[&_label] = m_assembly.newLabelId(); - return m_context->labelIDs[&_label]; -} - -AbstractAssembly::LabelID CodeTransform::functionEntryID(string const& _name, Scope::Function const& _function) -{ - if (!m_context->functionEntryIDs.count(&_function)) - { - AbstractAssembly::LabelID id = - m_useNamedLabelsForFunctions ? - m_assembly.namedLabel(_name) : - m_assembly.newLabelId(); - m_context->functionEntryIDs[&_function] = id; - } - return m_context->functionEntryIDs[&_function]; -} - -void CodeTransform::visitExpression(Expression const& _expression) -{ - int height = m_assembly.stackHeight(); - boost::apply_visitor(*this, _expression); - expectDeposit(1, height); -} - -void CodeTransform::visitStatements(vector<Statement> const& _statements) -{ - for (auto const& statement: _statements) - boost::apply_visitor(*this, statement); -} - -void CodeTransform::finalizeBlock(Block const& _block, int blockStartStackHeight) -{ - m_assembly.setSourceLocation(_block.location); - - // pop variables - solAssert(m_info.scopes.at(&_block).get() == m_scope, ""); - for (size_t i = 0; i < m_scope->numberOfVariables(); ++i) - m_assembly.appendInstruction(solidity::Instruction::POP); - - int deposit = m_assembly.stackHeight() - blockStartStackHeight; - solAssert(deposit == 0, "Invalid stack height at end of block."); - checkStackHeight(&_block); -} - -void CodeTransform::generateMultiAssignment(vector<Identifier> const& _variableNames) -{ - solAssert(m_scope, ""); - for (auto const& variableName: _variableNames | boost::adaptors::reversed) - generateAssignment(variableName); -} - -void CodeTransform::generateAssignment(Identifier const& _variableName) -{ - solAssert(m_scope, ""); - auto var = m_scope->lookup(_variableName.name); - if (var) - { - Scope::Variable const& _var = boost::get<Scope::Variable>(*var); - if (int heightDiff = variableHeightDiff(_var, true)) - m_assembly.appendInstruction(solidity::swapInstruction(heightDiff - 1)); - m_assembly.appendInstruction(solidity::Instruction::POP); - } - else - { - solAssert( - m_identifierAccess.generateCode, - "Identifier not found and no external access available." - ); - m_identifierAccess.generateCode(_variableName, IdentifierContext::LValue, m_assembly); - } -} - -int CodeTransform::variableHeightDiff(solidity::assembly::Scope::Variable const& _var, bool _forSwap) const -{ - solAssert(m_context->variableStackHeights.count(&_var), ""); - int heightDiff = m_assembly.stackHeight() - m_context->variableStackHeights[&_var]; - if (heightDiff <= (_forSwap ? 1 : 0) || heightDiff > (_forSwap ? 17 : 16)) - { - solUnimplemented( - "Variable inaccessible, too deep inside stack (" + boost::lexical_cast<string>(heightDiff) + ")" - ); - return 0; - } - else - return heightDiff; -} - -void CodeTransform::expectDeposit(int _deposit, int _oldHeight) const -{ - solAssert(m_assembly.stackHeight() == _oldHeight + _deposit, "Invalid stack deposit."); -} - -void CodeTransform::checkStackHeight(void const* _astElement) const -{ - solAssert(m_info.stackHeightInfo.count(_astElement), "Stack height for AST element not found."); - solAssert( - m_info.stackHeightInfo.at(_astElement) == m_assembly.stackHeight() - m_stackAdjustment, - "Stack height mismatch between analysis and code generation phase: Analysis: " + - to_string(m_info.stackHeightInfo.at(_astElement)) + - " code gen: " + - to_string(m_assembly.stackHeight() - m_stackAdjustment) - ); -} diff --git a/libjulia/backends/evm/EVMCodeTransform.h b/libjulia/backends/evm/EVMCodeTransform.h deleted file mode 100644 index f8eec0b7..00000000 --- a/libjulia/backends/evm/EVMCodeTransform.h +++ /dev/null @@ -1,158 +0,0 @@ -/* - This file is part of solidity. - - solidity is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - solidity is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with solidity. If not, see <http://www.gnu.org/licenses/>. -*/ -/** - * Common code generator for translating Julia / inline assembly to EVM and EVM1.5. - */ - -#include <libjulia/backends/evm/EVMAssembly.h> - -#include <libjulia/ASTDataForward.h> - -#include <libsolidity/inlineasm/AsmScope.h> - -#include <boost/variant.hpp> -#include <boost/optional.hpp> - -namespace dev -{ -namespace solidity -{ -class ErrorReporter; -namespace assembly -{ -struct AsmAnalysisInfo; -} -} -namespace julia -{ -class EVMAssembly; - -class CodeTransform: public boost::static_visitor<> -{ -public: - /// Create the code transformer. - /// @param _identifierAccess used to resolve identifiers external to the inline assembly - CodeTransform( - julia::AbstractAssembly& _assembly, - solidity::assembly::AsmAnalysisInfo& _analysisInfo, - bool _julia = false, - bool _evm15 = false, - ExternalIdentifierAccess const& _identifierAccess = ExternalIdentifierAccess(), - bool _useNamedLabelsForFunctions = false - ): CodeTransform( - _assembly, - _analysisInfo, - _julia, - _evm15, - _identifierAccess, - _useNamedLabelsForFunctions, - _assembly.stackHeight(), - std::make_shared<Context>() - ) - { - } - -protected: - struct Context - { - using Scope = solidity::assembly::Scope; - std::map<Scope::Label const*, AbstractAssembly::LabelID> labelIDs; - std::map<Scope::Function const*, AbstractAssembly::LabelID> functionEntryIDs; - std::map<Scope::Variable const*, int> variableStackHeights; - }; - - CodeTransform( - julia::AbstractAssembly& _assembly, - solidity::assembly::AsmAnalysisInfo& _analysisInfo, - bool _julia, - bool _evm15, - ExternalIdentifierAccess const& _identifierAccess, - bool _useNamedLabelsForFunctions, - int _stackAdjustment, - std::shared_ptr<Context> _context - ): - m_assembly(_assembly), - m_info(_analysisInfo), - m_julia(_julia), - m_evm15(_evm15), - m_useNamedLabelsForFunctions(_useNamedLabelsForFunctions), - m_identifierAccess(_identifierAccess), - m_stackAdjustment(_stackAdjustment), - m_context(_context) - {} - -public: - void operator()(Instruction const& _instruction); - void operator()(Literal const& _literal); - void operator()(Identifier const& _identifier); - void operator()(FunctionalInstruction const& _instr); - void operator()(FunctionCall const&); - void operator()(ExpressionStatement const& _statement); - void operator()(Label const& _label); - void operator()(StackAssignment const& _assignment); - void operator()(Assignment const& _assignment); - void operator()(VariableDeclaration const& _varDecl); - void operator()(If const& _if); - void operator()(Switch const& _switch); - void operator()(FunctionDefinition const&); - void operator()(ForLoop const&); - void operator()(Block const& _block); - -private: - AbstractAssembly::LabelID labelFromIdentifier(Identifier const& _identifier); - /// @returns the label ID corresponding to the given label, allocating a new one if - /// necessary. - AbstractAssembly::LabelID labelID(solidity::assembly::Scope::Label const& _label); - AbstractAssembly::LabelID functionEntryID(std::string const& _name, solidity::assembly::Scope::Function const& _function); - /// Generates code for an expression that is supposed to return a single value. - void visitExpression(Expression const& _expression); - - void visitStatements(std::vector<Statement> const& _statements); - - /// Pops all variables declared in the block and checks that the stack height is equal - /// to @a _blackStartStackHeight. - void finalizeBlock(Block const& _block, int _blockStartStackHeight); - - void generateMultiAssignment(std::vector<Identifier> const& _variableNames); - void generateAssignment(Identifier const& _variableName); - - /// Determines the stack height difference to the given variables. Throws - /// if it is not yet in scope or the height difference is too large. Returns - /// the (positive) stack height difference otherwise. - int variableHeightDiff(solidity::assembly::Scope::Variable const& _var, bool _forSwap) const; - - void expectDeposit(int _deposit, int _oldHeight) const; - - void checkStackHeight(void const* _astElement) const; - - julia::AbstractAssembly& m_assembly; - solidity::assembly::AsmAnalysisInfo& m_info; - solidity::assembly::Scope* m_scope = nullptr; - bool m_julia = false; - bool m_evm15 = false; - bool m_useNamedLabelsForFunctions = false; - ExternalIdentifierAccess m_identifierAccess; - /// Adjustment between the stack height as determined during the analysis phase - /// and the stack height in the assembly. This is caused by an initial stack being present - /// for inline assembly and different stack heights depending on the EVM backend used - /// (EVM 1.0 or 1.5). - int m_stackAdjustment = 0; - std::shared_ptr<Context> m_context; -}; - -} -} diff --git a/libjulia/optimiser/ASTCopier.cpp b/libjulia/optimiser/ASTCopier.cpp deleted file mode 100644 index a8a1e30f..00000000 --- a/libjulia/optimiser/ASTCopier.cpp +++ /dev/null @@ -1,184 +0,0 @@ -/* - This file is part of solidity. - - solidity is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - solidity is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with solidity. If not, see <http://www.gnu.org/licenses/>. -*/ -/** - * Creates an independent copy of an AST, renaming identifiers to be unique. - */ - -#include <libjulia/optimiser/ASTCopier.h> - -#include <libjulia/Exceptions.h> - -#include <libsolidity/inlineasm/AsmData.h> - -#include <libdevcore/Common.h> - -using namespace std; -using namespace dev; -using namespace dev::julia; - -Statement ASTCopier::operator()(Instruction const&) -{ - assertThrow(false, OptimizerException, "Invalid operation."); - return {}; -} - -Statement ASTCopier::operator()(ExpressionStatement const& _statement) -{ - return ExpressionStatement{ _statement.location, translate(_statement.expression) }; -} - -Statement ASTCopier::operator()(VariableDeclaration const& _varDecl) -{ - return VariableDeclaration{ - _varDecl.location, - translateVector(_varDecl.variables), - translate(_varDecl.value) - }; -} - -Statement ASTCopier::operator()(Assignment const& _assignment) -{ - return Assignment{ - _assignment.location, - translateVector(_assignment.variableNames), - translate(_assignment.value) - }; -} - -Statement ASTCopier::operator()(StackAssignment const&) -{ - assertThrow(false, OptimizerException, "Invalid operation."); - return {}; -} - -Statement ASTCopier::operator()(Label const&) -{ - assertThrow(false, OptimizerException, "Invalid operation."); - return {}; -} - -Expression ASTCopier::operator()(FunctionCall const& _call) -{ - return FunctionCall{ - _call.location, - translate(_call.functionName), - translateVector(_call.arguments) - }; -} - -Expression ASTCopier::operator()(FunctionalInstruction const& _instruction) -{ - return FunctionalInstruction{ - _instruction.location, - _instruction.instruction, - translateVector(_instruction.arguments) - }; -} - -Expression ASTCopier::operator()(Identifier const& _identifier) -{ - return Identifier{_identifier.location, translateIdentifier(_identifier.name)}; -} - -Expression ASTCopier::operator()(Literal const& _literal) -{ - return translate(_literal); -} - -Statement ASTCopier::operator()(If const& _if) -{ - return If{_if.location, translate(_if.condition), translate(_if.body)}; -} - -Statement ASTCopier::operator()(Switch const& _switch) -{ - return Switch{_switch.location, translate(_switch.expression), translateVector(_switch.cases)}; -} - -Statement ASTCopier::operator()(FunctionDefinition const& _function) -{ - string translatedName = translateIdentifier(_function.name); - - enterFunction(_function); - ScopeGuard g([&]() { this->leaveFunction(_function); }); - - return FunctionDefinition{ - _function.location, - move(translatedName), - translateVector(_function.parameters), - translateVector(_function.returnVariables), - translate(_function.body) - }; -} - -Statement ASTCopier::operator()(ForLoop const& _forLoop) -{ - enterScope(_forLoop.pre); - ScopeGuard g([&]() { this->leaveScope(_forLoop.pre); }); - - return ForLoop{ - _forLoop.location, - translate(_forLoop.pre), - translate(_forLoop.condition), - translate(_forLoop.post), - translate(_forLoop.body) - }; -} - -Statement ASTCopier::operator ()(Block const& _block) -{ - return translate(_block); -} - -Expression ASTCopier::translate(Expression const& _expression) -{ - return _expression.apply_visitor(static_cast<ExpressionCopier&>(*this)); -} - -Statement ASTCopier::translate(Statement const& _statement) -{ - return _statement.apply_visitor(static_cast<StatementCopier&>(*this)); -} - -Block ASTCopier::translate(Block const& _block) -{ - enterScope(_block); - ScopeGuard g([&]() { this->leaveScope(_block); }); - - return Block{_block.location, translateVector(_block.statements)}; -} - -Case ASTCopier::translate(Case const& _case) -{ - return Case{_case.location, translate(_case.value), translate(_case.body)}; -} - -Identifier ASTCopier::translate(Identifier const& _identifier) -{ - return Identifier{_identifier.location, translateIdentifier(_identifier.name)}; -} - -Literal ASTCopier::translate(Literal const& _literal) -{ - return _literal; -} - -TypedName ASTCopier::translate(TypedName const& _typedName) -{ - return TypedName{_typedName.location, translateIdentifier(_typedName.name), _typedName.type}; -} - diff --git a/libjulia/optimiser/ASTCopier.h b/libjulia/optimiser/ASTCopier.h deleted file mode 100644 index 8681f2e0..00000000 --- a/libjulia/optimiser/ASTCopier.h +++ /dev/null @@ -1,124 +0,0 @@ -/* - This file is part of solidity. - - solidity is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - solidity is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with solidity. If not, see <http://www.gnu.org/licenses/>. -*/ -/** - * Creates an independent copy of an AST, renaming identifiers to be unique. - */ - -#pragma once - -#include <libjulia/ASTDataForward.h> - -#include <boost/variant.hpp> -#include <boost/optional.hpp> - -#include <vector> -#include <set> -#include <memory> - -namespace dev -{ -namespace julia -{ - -class ExpressionCopier: public boost::static_visitor<Expression> -{ -public: - virtual ~ExpressionCopier() = default; - virtual Expression operator()(Literal const& _literal) = 0; - virtual Expression operator()(Identifier const& _identifier) = 0; - virtual Expression operator()(FunctionalInstruction const& _instr) = 0; - virtual Expression operator()(FunctionCall const&) = 0; -}; - -class StatementCopier: public boost::static_visitor<Statement> -{ -public: - virtual ~StatementCopier() = default; - virtual Statement operator()(ExpressionStatement const& _statement) = 0; - virtual Statement operator()(Instruction const& _instruction) = 0; - virtual Statement operator()(Label const& _label) = 0; - virtual Statement operator()(StackAssignment const& _assignment) = 0; - virtual Statement operator()(Assignment const& _assignment) = 0; - virtual Statement operator()(VariableDeclaration const& _varDecl) = 0; - virtual Statement operator()(If const& _if) = 0; - virtual Statement operator()(Switch const& _switch) = 0; - virtual Statement operator()(FunctionDefinition const&) = 0; - virtual Statement operator()(ForLoop const&) = 0; - virtual Statement operator()(Block const& _block) = 0; -}; - -/** - * Creates a copy of a iulia AST potentially replacing identifier names. - * Base class to be extended. - */ -class ASTCopier: public ExpressionCopier, public StatementCopier -{ -public: - virtual ~ASTCopier() = default; - virtual Expression operator()(Literal const& _literal) override; - virtual Statement operator()(Instruction const& _instruction) override; - virtual Expression operator()(Identifier const& _identifier) override; - virtual Expression operator()(FunctionalInstruction const& _instr) override; - virtual Expression operator()(FunctionCall const&) override; - virtual Statement operator()(ExpressionStatement const& _statement) override; - virtual Statement operator()(Label const& _label) override; - virtual Statement operator()(StackAssignment const& _assignment) override; - virtual Statement operator()(Assignment const& _assignment) override; - virtual Statement operator()(VariableDeclaration const& _varDecl) override; - virtual Statement operator()(If const& _if) override; - virtual Statement operator()(Switch const& _switch) override; - virtual Statement operator()(FunctionDefinition const&) override; - virtual Statement operator()(ForLoop const&) override; - virtual Statement operator()(Block const& _block) override; - - virtual Expression translate(Expression const& _expression); - virtual Statement translate(Statement const& _statement); - -protected: - template <typename T> - std::vector<T> translateVector(std::vector<T> const& _values); - - template <typename T> - std::shared_ptr<T> translate(std::shared_ptr<T> const& _v) - { - return _v ? std::make_shared<T>(translate(*_v)) : nullptr; - } - Block translate(Block const& _block); - Case translate(Case const& _case); - Identifier translate(Identifier const& _identifier); - Literal translate(Literal const& _literal); - TypedName translate(TypedName const& _typedName); - - virtual void enterScope(Block const&) { } - virtual void leaveScope(Block const&) { } - virtual void enterFunction(FunctionDefinition const&) { } - virtual void leaveFunction(FunctionDefinition const&) { } - virtual std::string translateIdentifier(std::string const& _name) { return _name; } -}; - -template <typename T> -std::vector<T> ASTCopier::translateVector(std::vector<T> const& _values) -{ - std::vector<T> translated; - for (auto const& v: _values) - translated.emplace_back(translate(v)); - return translated; -} - - -} -} diff --git a/libjulia/optimiser/ASTWalker.cpp b/libjulia/optimiser/ASTWalker.cpp deleted file mode 100644 index dc94cc60..00000000 --- a/libjulia/optimiser/ASTWalker.cpp +++ /dev/null @@ -1,157 +0,0 @@ -/* - This file is part of solidity. - - solidity is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - solidity is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with solidity. If not, see <http://www.gnu.org/licenses/>. -*/ -/** - * Generic AST walker. - */ - -#include <libjulia/optimiser/ASTWalker.h> - -#include <libsolidity/inlineasm/AsmData.h> - -#include <boost/range/adaptor/reversed.hpp> - -using namespace std; -using namespace dev; -using namespace dev::julia; -using namespace dev::solidity; - - -void ASTWalker::operator()(FunctionalInstruction const& _instr) -{ - walkVector(_instr.arguments | boost::adaptors::reversed); -} - -void ASTWalker::operator()(FunctionCall const& _funCall) -{ - walkVector(_funCall.arguments | boost::adaptors::reversed); -} - -void ASTWalker::operator()(ExpressionStatement const& _statement) -{ - visit(_statement.expression); -} - -void ASTWalker::operator()(Assignment const& _assignment) -{ - for (auto const& name: _assignment.variableNames) - (*this)(name); - visit(*_assignment.value); -} - -void ASTWalker::operator()(VariableDeclaration const& _varDecl) -{ - if (_varDecl.value) - visit(*_varDecl.value); -} - -void ASTWalker::operator()(If const& _if) -{ - visit(*_if.condition); - (*this)(_if.body); -} - -void ASTWalker::operator()(Switch const& _switch) -{ - visit(*_switch.expression); - for (auto const& _case: _switch.cases) - { - if (_case.value) - (*this)(*_case.value); - (*this)(_case.body); - } -} - -void ASTWalker::operator()(FunctionDefinition const& _fun) -{ - (*this)(_fun.body); -} - -void ASTWalker::operator()(ForLoop const& _for) -{ - (*this)(_for.pre); - visit(*_for.condition); - (*this)(_for.body); - (*this)(_for.post); -} - -void ASTWalker::operator()(Block const& _block) -{ - walkVector(_block.statements); -} - -void ASTModifier::operator()(FunctionalInstruction& _instr) -{ - walkVector(_instr.arguments | boost::adaptors::reversed); -} - -void ASTModifier::operator()(FunctionCall& _funCall) -{ - walkVector(_funCall.arguments | boost::adaptors::reversed); -} - -void ASTModifier::operator()(ExpressionStatement& _statement) -{ - visit(_statement.expression); -} - -void ASTModifier::operator()(Assignment& _assignment) -{ - for (auto& name: _assignment.variableNames) - (*this)(name); - visit(*_assignment.value); -} - -void ASTModifier::operator()(VariableDeclaration& _varDecl) -{ - if (_varDecl.value) - visit(*_varDecl.value); -} - -void ASTModifier::operator()(If& _if) -{ - visit(*_if.condition); - (*this)(_if.body); -} - -void ASTModifier::operator()(Switch& _switch) -{ - visit(*_switch.expression); - for (auto& _case: _switch.cases) - { - if (_case.value) - (*this)(*_case.value); - (*this)(_case.body); - } -} - -void ASTModifier::operator()(FunctionDefinition& _fun) -{ - (*this)(_fun.body); -} - -void ASTModifier::operator()(ForLoop& _for) -{ - (*this)(_for.pre); - visit(*_for.condition); - (*this)(_for.post); - (*this)(_for.body); -} - -void ASTModifier::operator()(Block& _block) -{ - walkVector(_block.statements); -} diff --git a/libjulia/optimiser/ASTWalker.h b/libjulia/optimiser/ASTWalker.h deleted file mode 100644 index e1f0f5bd..00000000 --- a/libjulia/optimiser/ASTWalker.h +++ /dev/null @@ -1,122 +0,0 @@ -/* - This file is part of solidity. - - solidity is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - solidity is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with solidity. If not, see <http://www.gnu.org/licenses/>. -*/ -/** - * Generic AST walker. - */ - -#pragma once - -#include <libjulia/ASTDataForward.h> - -#include <libjulia/Exceptions.h> - -#include <boost/variant.hpp> -#include <boost/optional.hpp> - -#include <vector> -#include <set> -#include <map> - -namespace dev -{ -namespace julia -{ - -/** - * Generic AST walker. - */ -class ASTWalker: public boost::static_visitor<> -{ -public: - virtual ~ASTWalker() = default; - virtual void operator()(Literal const&) {} - virtual void operator()(Instruction const&) { assertThrow(false, OptimizerException, ""); } - virtual void operator()(Identifier const&) {} - virtual void operator()(FunctionalInstruction const& _instr); - virtual void operator()(FunctionCall const& _funCall); - virtual void operator()(ExpressionStatement const& _statement); - virtual void operator()(Label const&) { assertThrow(false, OptimizerException, ""); } - virtual void operator()(StackAssignment const&) { assertThrow(false, OptimizerException, ""); } - virtual void operator()(Assignment const& _assignment); - virtual void operator()(VariableDeclaration const& _varDecl); - virtual void operator()(If const& _if); - virtual void operator()(Switch const& _switch); - virtual void operator()(FunctionDefinition const&); - virtual void operator()(ForLoop const&); - virtual void operator()(Block const& _block); - - virtual void visit(Statement const& _st) - { - boost::apply_visitor(*this, _st); - } - virtual void visit(Expression const& _e) - { - boost::apply_visitor(*this, _e); - } - -protected: - template <class T> - void walkVector(T const& _statements) - { - for (auto const& statement: _statements) - visit(statement); - } -}; - -/** - * Generic AST modifier (i.e. non-const version of ASTWalker). - */ -class ASTModifier: public boost::static_visitor<> -{ -public: - virtual ~ASTModifier() = default; - virtual void operator()(Literal&) {} - virtual void operator()(Instruction&) { assertThrow(false, OptimizerException, ""); } - virtual void operator()(Identifier&) {} - virtual void operator()(FunctionalInstruction& _instr); - virtual void operator()(FunctionCall& _funCall); - virtual void operator()(ExpressionStatement& _statement); - virtual void operator()(Label&) { assertThrow(false, OptimizerException, ""); } - virtual void operator()(StackAssignment&) { assertThrow(false, OptimizerException, ""); } - virtual void operator()(Assignment& _assignment); - virtual void operator()(VariableDeclaration& _varDecl); - virtual void operator()(If& _if); - virtual void operator()(Switch& _switch); - virtual void operator()(FunctionDefinition&); - virtual void operator()(ForLoop&); - virtual void operator()(Block& _block); - - virtual void visit(Statement& _st) - { - boost::apply_visitor(*this, _st); - } - virtual void visit(Expression& _e) - { - boost::apply_visitor(*this, _e); - } - -protected: - template <class T> - void walkVector(T&& _statements) - { - for (auto& st: _statements) - visit(st); - } -}; - -} -} diff --git a/libjulia/optimiser/CommonSubexpressionEliminator.cpp b/libjulia/optimiser/CommonSubexpressionEliminator.cpp deleted file mode 100644 index 3122280b..00000000 --- a/libjulia/optimiser/CommonSubexpressionEliminator.cpp +++ /dev/null @@ -1,49 +0,0 @@ -/*( - This file is part of solidity. - - solidity is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - solidity is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with solidity. If not, see <http://www.gnu.org/licenses/>. -*/ -/** - * Optimisation stage that replaces expressions known to be the current value of a variable - * in scope by a reference to that variable. - */ - -#include <libjulia/optimiser/CommonSubexpressionEliminator.h> - -#include <libjulia/optimiser/Metrics.h> -#include <libjulia/optimiser/SyntacticalEquality.h> -#include <libjulia/Exceptions.h> - -#include <libsolidity/inlineasm/AsmData.h> - -using namespace std; -using namespace dev; -using namespace dev::julia; - -void CommonSubexpressionEliminator::visit(Expression& _e) -{ - // Single exception for substitution: We do not substitute one variable for another. - if (_e.type() != typeid(Identifier)) - // TODO this search rather inefficient. - for (auto const& var: m_value) - { - assertThrow(var.second, OptimizerException, ""); - if (SyntacticalEqualityChecker::equal(_e, *var.second)) - { - _e = Identifier{locationOf(_e), var.first}; - break; - } - } - DataFlowAnalyzer::visit(_e); -} diff --git a/libjulia/optimiser/CommonSubexpressionEliminator.h b/libjulia/optimiser/CommonSubexpressionEliminator.h deleted file mode 100644 index a8ca3abb..00000000 --- a/libjulia/optimiser/CommonSubexpressionEliminator.h +++ /dev/null @@ -1,45 +0,0 @@ -/* - This file is part of solidity. - - solidity is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - solidity is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with solidity. If not, see <http://www.gnu.org/licenses/>. -*/ -/** - * Optimisation stage that replaces expressions known to be the current value of a variable - * in scope by a reference to that variable. - */ - -#pragma once - -#include <libjulia/optimiser/DataFlowAnalyzer.h> - -namespace dev -{ -namespace julia -{ - -/** - * Optimisation stage that replaces expressions known to be the current value of a variable - * in scope by a reference to that variable. - * - * Prerequisite: Disambiguator - */ -class CommonSubexpressionEliminator: public DataFlowAnalyzer -{ -protected: - using ASTModifier::visit; - virtual void visit(Expression& _e) override; -}; - -} -} diff --git a/libjulia/optimiser/DataFlowAnalyzer.cpp b/libjulia/optimiser/DataFlowAnalyzer.cpp deleted file mode 100644 index 25f0ffb4..00000000 --- a/libjulia/optimiser/DataFlowAnalyzer.cpp +++ /dev/null @@ -1,193 +0,0 @@ -/*( - This file is part of solidity. - - solidity is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - solidity is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with solidity. If not, see <http://www.gnu.org/licenses/>. -*/ -/** - * Base class to perform data flaw analysis during AST walks. - * Tracks assignments and is used as base class for both Rematerialiser and - * Common Subexpression Eliminator. - */ - -#include <libjulia/optimiser/DataFlowAnalyzer.h> - -#include <libjulia/optimiser/NameCollector.h> -#include <libjulia/optimiser/Semantics.h> -#include <libjulia/Exceptions.h> - -#include <libsolidity/inlineasm/AsmData.h> - -#include <libdevcore/CommonData.h> - -#include <boost/range/adaptor/reversed.hpp> - -using namespace std; -using namespace dev; -using namespace dev::julia; - -void DataFlowAnalyzer::operator()(Assignment& _assignment) -{ - set<string> names; - for (auto const& var: _assignment.variableNames) - names.insert(var.name); - assertThrow(_assignment.value, OptimizerException, ""); - visit(*_assignment.value); - handleAssignment(names, _assignment.value.get()); -} - -void DataFlowAnalyzer::operator()(VariableDeclaration& _varDecl) -{ - set<string> names; - for (auto const& var: _varDecl.variables) - names.insert(var.name); - m_variableScopes.back().variables += names; - if (_varDecl.value) - visit(*_varDecl.value); - handleAssignment(names, _varDecl.value.get()); -} - -void DataFlowAnalyzer::operator()(If& _if) -{ - ASTModifier::operator()(_if); - - Assignments assignments; - assignments(_if.body); - clearValues(assignments.names()); -} - -void DataFlowAnalyzer::operator()(Switch& _switch) -{ - visit(*_switch.expression); - set<string> assignedVariables; - for (auto& _case: _switch.cases) - { - (*this)(_case.body); - Assignments assignments; - assignments(_case.body); - assignedVariables += assignments.names(); - // This is a little too destructive, we could retain the old values. - clearValues(assignments.names()); - } - clearValues(assignedVariables); -} - -void DataFlowAnalyzer::operator()(FunctionDefinition& _fun) -{ - m_variableScopes.emplace_back(true); - for (auto const& parameter: _fun.parameters) - m_variableScopes.back().variables.insert(parameter.name); - for (auto const& var: _fun.returnVariables) - m_variableScopes.back().variables.insert(var.name); - ASTModifier::operator()(_fun); - m_variableScopes.pop_back(); -} - -void DataFlowAnalyzer::operator()(ForLoop& _for) -{ - // Special scope handling of the pre block. - m_variableScopes.emplace_back(false); - for (auto& statement: _for.pre.statements) - visit(statement); - - Assignments assignments; - assignments(_for.body); - assignments(_for.post); - clearValues(assignments.names()); - - visit(*_for.condition); - (*this)(_for.body); - (*this)(_for.post); - - clearValues(assignments.names()); - - m_variableScopes.pop_back(); -} - -void DataFlowAnalyzer::operator()(Block& _block) -{ - size_t numScopes = m_variableScopes.size(); - m_variableScopes.emplace_back(false); - ASTModifier::operator()(_block); - m_variableScopes.pop_back(); - assertThrow(numScopes == m_variableScopes.size(), OptimizerException, ""); -} - -void DataFlowAnalyzer::handleAssignment(set<string> const& _variables, Expression* _value) -{ - clearValues(_variables); - - MovableChecker movableChecker; - if (_value) - movableChecker.visit(*_value); - if (_variables.size() == 1) - { - string const& name = *_variables.begin(); - // Expression has to be movable and cannot contain a reference - // to the variable that will be assigned to. - if (_value && movableChecker.movable() && !movableChecker.referencedVariables().count(name)) - m_value[name] = _value; - } - - auto const& referencedVariables = movableChecker.referencedVariables(); - for (auto const& name: _variables) - { - m_references[name] = referencedVariables; - for (auto const& ref: referencedVariables) - m_referencedBy[ref].insert(name); - } -} - -void DataFlowAnalyzer::clearValues(set<string> const& _variables) -{ - // All variables that reference variables to be cleared also have to be - // cleared, but not recursively, since only the value of the original - // variables changes. Example: - // let a := 1 - // let b := a - // let c := b - // let a := 2 - // add(b, c) - // In the last line, we can replace c by b, but not b by a. - // - // This cannot be easily tested since the substitutions will be done - // one by one on the fly, and the last line will just be add(1, 1) - - set<string> variables = _variables; - // Clear variables that reference variables to be cleared. - for (auto const& name: variables) - for (auto const& ref: m_referencedBy[name]) - variables.insert(ref); - - // Clear the value and update the reference relation. - for (auto const& name: variables) - m_value.erase(name); - for (auto const& name: variables) - { - for (auto const& ref: m_references[name]) - m_referencedBy[ref].erase(name); - m_references[name].clear(); - } -} - -bool DataFlowAnalyzer::inScope(string const& _variableName) const -{ - for (auto const& scope: m_variableScopes | boost::adaptors::reversed) - { - if (scope.variables.count(_variableName)) - return true; - if (scope.isFunction) - return false; - } - return false; -} diff --git a/libjulia/optimiser/DataFlowAnalyzer.h b/libjulia/optimiser/DataFlowAnalyzer.h deleted file mode 100644 index 4cb3d4cd..00000000 --- a/libjulia/optimiser/DataFlowAnalyzer.h +++ /dev/null @@ -1,84 +0,0 @@ -/* - This file is part of solidity. - - solidity is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - solidity is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with solidity. If not, see <http://www.gnu.org/licenses/>. -*/ -/** - * Base class to perform data flow analysis during AST walks. - * Tracks assignments and is used as base class for both Rematerialiser and - * Common Subexpression Eliminator. - */ - -#pragma once - -#include <libjulia/optimiser/ASTWalker.h> - -#include <string> -#include <map> -#include <set> - -namespace dev -{ -namespace julia -{ - -/** - * Base class to perform data flow analysis during AST walks. - * Tracks assignments and is used as base class for both Rematerialiser and - * Common Subexpression Eliminator. - * - * Prerequisite: Disambiguator - */ -class DataFlowAnalyzer: public ASTModifier -{ -public: - using ASTModifier::operator(); - virtual void operator()(Assignment& _assignment) override; - virtual void operator()(VariableDeclaration& _varDecl) override; - virtual void operator()(If& _if) override; - virtual void operator()(Switch& _switch) override; - virtual void operator()(FunctionDefinition&) override; - virtual void operator()(ForLoop&) override; - virtual void operator()(Block& _block) override; - -protected: - /// Registers the assignment. - void handleAssignment(std::set<std::string> const& _names, Expression* _value); - - /// Clears information about the valuse assigned to the given variables, - /// for example at points where control flow is merged. - void clearValues(std::set<std::string> const& _names); - - /// Returns true iff the variable is in scope. - bool inScope(std::string const& _variableName) const; - - /// Current values of variables, always movable. - std::map<std::string, Expression const*> m_value; - /// m_references[a].contains(b) <=> the current expression assigned to a references b - std::map<std::string, std::set<std::string>> m_references; - /// m_referencedBy[b].contains(a) <=> the current expression assigned to a references b - std::map<std::string, std::set<std::string>> m_referencedBy; - - struct Scope - { - explicit Scope(bool _isFunction): isFunction(_isFunction) {} - std::set<std::string> variables; - bool isFunction; - }; - /// List of scopes. - std::vector<Scope> m_variableScopes; -}; - -} -} diff --git a/libjulia/optimiser/Disambiguator.cpp b/libjulia/optimiser/Disambiguator.cpp deleted file mode 100644 index 687be9b9..00000000 --- a/libjulia/optimiser/Disambiguator.cpp +++ /dev/null @@ -1,75 +0,0 @@ -/* - This file is part of solidity. - - solidity is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - solidity is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with solidity. If not, see <http://www.gnu.org/licenses/>. -*/ -/** - * Optimiser component that makes all identifiers unique. - */ - -#include <libjulia/optimiser/Disambiguator.h> - -#include <libjulia/Exceptions.h> - -#include <libsolidity/inlineasm/AsmData.h> -#include <libsolidity/inlineasm/AsmScope.h> - -using namespace std; -using namespace dev; -using namespace dev::julia; -using namespace dev::solidity; - -using Scope = dev::solidity::assembly::Scope; - -string Disambiguator::translateIdentifier(string const& _originalName) -{ - assertThrow(!m_scopes.empty() && m_scopes.back(), OptimizerException, ""); - Scope::Identifier const* id = m_scopes.back()->lookup(_originalName); - assertThrow(id, OptimizerException, ""); - if (!m_translations.count(id)) - m_translations[id] = m_nameDispenser.newName(_originalName); - return m_translations.at(id); -} - -void Disambiguator::enterScope(Block const& _block) -{ - enterScopeInternal(*m_info.scopes.at(&_block)); -} - -void Disambiguator::leaveScope(Block const& _block) -{ - leaveScopeInternal(*m_info.scopes.at(&_block)); -} - -void Disambiguator::enterFunction(FunctionDefinition const& _function) -{ - enterScopeInternal(*m_info.scopes.at(m_info.virtualBlocks.at(&_function).get())); -} - -void Disambiguator::leaveFunction(FunctionDefinition const& _function) -{ - leaveScopeInternal(*m_info.scopes.at(m_info.virtualBlocks.at(&_function).get())); -} - -void Disambiguator::enterScopeInternal(Scope& _scope) -{ - m_scopes.push_back(&_scope); -} - -void Disambiguator::leaveScopeInternal(Scope& _scope) -{ - assertThrow(!m_scopes.empty(), OptimizerException, ""); - assertThrow(m_scopes.back() == &_scope, OptimizerException, ""); - m_scopes.pop_back(); -} diff --git a/libjulia/optimiser/Disambiguator.h b/libjulia/optimiser/Disambiguator.h deleted file mode 100644 index 6fc8a615..00000000 --- a/libjulia/optimiser/Disambiguator.h +++ /dev/null @@ -1,68 +0,0 @@ -/* - This file is part of solidity. - - solidity is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - solidity is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with solidity. If not, see <http://www.gnu.org/licenses/>. -*/ -/** - * Optimiser component that makes all identifiers unique. - */ - -#pragma once - -#include <libjulia/ASTDataForward.h> - -#include <libjulia/optimiser/ASTCopier.h> -#include <libjulia/optimiser/NameDispenser.h> - -#include <libsolidity/inlineasm/AsmAnalysisInfo.h> - -#include <boost/variant.hpp> -#include <boost/optional.hpp> - -#include <set> - -namespace dev -{ -namespace julia -{ - -/** - * Creates a copy of a iulia AST replacing all identifiers by unique names. - */ -class Disambiguator: public ASTCopier -{ -public: - Disambiguator(solidity::assembly::AsmAnalysisInfo const& _analysisInfo): - m_info(_analysisInfo) - {} - -protected: - virtual void enterScope(Block const& _block) override; - virtual void leaveScope(Block const& _block) override; - virtual void enterFunction(FunctionDefinition const& _function) override; - virtual void leaveFunction(FunctionDefinition const& _function) override; - virtual std::string translateIdentifier(std::string const& _name) override; - - void enterScopeInternal(solidity::assembly::Scope& _scope); - void leaveScopeInternal(solidity::assembly::Scope& _scope); - - solidity::assembly::AsmAnalysisInfo const& m_info; - - std::vector<solidity::assembly::Scope*> m_scopes; - std::map<void const*, std::string> m_translations; - NameDispenser m_nameDispenser; -}; - -} -} diff --git a/libjulia/optimiser/ExpressionInliner.cpp b/libjulia/optimiser/ExpressionInliner.cpp deleted file mode 100644 index 450769fd..00000000 --- a/libjulia/optimiser/ExpressionInliner.cpp +++ /dev/null @@ -1,74 +0,0 @@ -/* - This file is part of solidity. - - solidity is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - solidity is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with solidity. If not, see <http://www.gnu.org/licenses/>. -*/ -/** - * Optimiser component that performs function inlining inside expressions. - */ - -#include <libjulia/optimiser/ExpressionInliner.h> - -#include <libjulia/optimiser/InlinableExpressionFunctionFinder.h> -#include <libjulia/optimiser/Substitution.h> -#include <libjulia/optimiser/Semantics.h> - -#include <libsolidity/inlineasm/AsmData.h> - -#include <boost/algorithm/cxx11/all_of.hpp> - -using namespace std; -using namespace dev; -using namespace dev::julia; -using namespace dev::solidity; - -void ExpressionInliner::run() -{ - InlinableExpressionFunctionFinder funFinder; - funFinder(m_block); - m_inlinableFunctions = funFinder.inlinableFunctions(); - - (*this)(m_block); -} - - -void ExpressionInliner::operator()(FunctionDefinition& _fun) -{ - ASTModifier::operator()(_fun); -} - -void ExpressionInliner::visit(Expression& _expression) -{ - ASTModifier::visit(_expression); - if (_expression.type() == typeid(FunctionCall)) - { - FunctionCall& funCall = boost::get<FunctionCall>(_expression); - - bool movable = boost::algorithm::all_of( - funCall.arguments, - [=](Expression const& _arg) { return MovableChecker(_arg).movable(); } - ); - if (m_inlinableFunctions.count(funCall.functionName.name) && movable) - { - FunctionDefinition const& fun = *m_inlinableFunctions.at(funCall.functionName.name); - map<string, Expression const*> substitutions; - for (size_t i = 0; i < fun.parameters.size(); ++i) - substitutions[fun.parameters[i].name] = &funCall.arguments[i]; - _expression = Substitution(substitutions).translate(*boost::get<Assignment>(fun.body.statements.front()).value); - - // TODO Add metric! This metric should perform well on a pair of functions who - // call each other recursively. - } - } -} diff --git a/libjulia/optimiser/ExpressionInliner.h b/libjulia/optimiser/ExpressionInliner.h deleted file mode 100644 index 3d24ef5d..00000000 --- a/libjulia/optimiser/ExpressionInliner.h +++ /dev/null @@ -1,72 +0,0 @@ -/* - This file is part of solidity. - - solidity is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - solidity is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with solidity. If not, see <http://www.gnu.org/licenses/>. -*/ -/** - * Optimiser component that performs function inlining. - */ -#pragma once - -#include <libjulia/optimiser/ASTWalker.h> - -#include <libjulia/ASTDataForward.h> - -#include <boost/variant.hpp> -#include <boost/optional.hpp> - -#include <set> - -namespace dev -{ -namespace julia -{ - -/** - * Optimiser component that modifies an AST in place, inlining functions that can be - * inlined inside functional expressions, i.e. functions that - * - return a single value - * - have a body like r := <functional expression> - * - neither reference themselves nor r in the right hand side - * - * Furthermore, the arguments of the function call cannot have any side-effects. - * - * This component can only be used on sources with unique names. - */ -class ExpressionInliner: public ASTModifier -{ -public: - ExpressionInliner(Block& _block): - m_block(_block) - {} - - void run(); - - using ASTModifier::operator(); - virtual void operator()(FunctionDefinition& _fun) override; - - virtual void visit(Expression& _expression) override; - -private: - std::map<std::string, FunctionDefinition const*> m_inlinableFunctions; - std::map<std::string, std::string> m_varReplacements; - /// Set of functions we are currently visiting inside. - std::set<std::string> m_currentFunctions; - - Block& m_block; -}; - - -} -} diff --git a/libjulia/optimiser/ExpressionSimplifier.cpp b/libjulia/optimiser/ExpressionSimplifier.cpp deleted file mode 100644 index 8bd6b1c7..00000000 --- a/libjulia/optimiser/ExpressionSimplifier.cpp +++ /dev/null @@ -1,48 +0,0 @@ -/* - This file is part of solidity. - - solidity is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - solidity is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with solidity. If not, see <http://www.gnu.org/licenses/>. -*/ -/** - * Optimiser component that uses the simplification rules to simplify expressions. - */ - -#include <libjulia/optimiser/ExpressionSimplifier.h> - -#include <libjulia/optimiser/SimplificationRules.h> -#include <libjulia/optimiser/Semantics.h> - -#include <libsolidity/inlineasm/AsmData.h> - -#include <libdevcore/CommonData.h> - -using namespace std; -using namespace dev; -using namespace dev::julia; -using namespace dev::solidity; - - -void ExpressionSimplifier::visit(Expression& _expression) -{ - ASTModifier::visit(_expression); - while (auto match = SimplificationRules::findFirstMatch(_expression)) - { - // Do not apply the rule if it removes non-constant parts of the expression. - // TODO: The check could actually be less strict than "movable". - // We only require "Does not cause side-effects". - if (match->removesNonConstants && !MovableChecker(_expression).movable()) - return; - _expression = match->action().toExpression(locationOf(_expression)); - } -} diff --git a/libjulia/optimiser/ExpressionSimplifier.h b/libjulia/optimiser/ExpressionSimplifier.h deleted file mode 100644 index 8a9e0e20..00000000 --- a/libjulia/optimiser/ExpressionSimplifier.h +++ /dev/null @@ -1,45 +0,0 @@ -/* - This file is part of solidity. - - solidity is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - solidity is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with solidity. If not, see <http://www.gnu.org/licenses/>. -*/ -/** - * Optimiser component that uses the simplification rules to simplify expressions. - */ - -#pragma once - -#include <libjulia/ASTDataForward.h> - -#include <libjulia/optimiser/ASTWalker.h> - -namespace dev -{ -namespace julia -{ - -/** - * Applies simplification rules to all expressions. - */ -class ExpressionSimplifier: public ASTModifier -{ -public: - using ASTModifier::operator(); - virtual void visit(Expression& _expression); - -private: -}; - -} -} diff --git a/libjulia/optimiser/FullInliner.cpp b/libjulia/optimiser/FullInliner.cpp deleted file mode 100644 index e8776e23..00000000 --- a/libjulia/optimiser/FullInliner.cpp +++ /dev/null @@ -1,259 +0,0 @@ -/* - This file is part of solidity. - - solidity is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - solidity is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with solidity. If not, see <http://www.gnu.org/licenses/>. -*/ -/** - * Optimiser component that performs function inlining for arbitrary functions. - */ - -#include <libjulia/optimiser/FullInliner.h> - -#include <libjulia/optimiser/ASTCopier.h> -#include <libjulia/optimiser/ASTWalker.h> -#include <libjulia/optimiser/NameCollector.h> -#include <libjulia/optimiser/Semantics.h> -#include <libjulia/Exceptions.h> - -#include <libsolidity/inlineasm/AsmData.h> - -#include <libdevcore/CommonData.h> - -#include <boost/range/adaptor/reversed.hpp> - -using namespace std; -using namespace dev; -using namespace dev::julia; -using namespace dev::solidity; - -FullInliner::FullInliner(Block& _ast): - m_ast(_ast) -{ - assertThrow(m_ast.statements.size() >= 1, OptimizerException, ""); - assertThrow(m_ast.statements.front().type() == typeid(Block), OptimizerException, ""); - m_nameDispenser.m_usedNames = NameCollector(m_ast).names(); - - for (size_t i = 1; i < m_ast.statements.size(); ++i) - { - assertThrow(m_ast.statements.at(i).type() == typeid(FunctionDefinition), OptimizerException, ""); - FunctionDefinition& fun = boost::get<FunctionDefinition>(m_ast.statements.at(i)); - m_functions[fun.name] = &fun; - m_functionsToVisit.insert(&fun); - } -} - -void FullInliner::run() -{ - assertThrow(m_ast.statements[0].type() == typeid(Block), OptimizerException, ""); - InlineModifier(*this, m_nameDispenser, "").visit(m_ast.statements[0]); - while (!m_functionsToVisit.empty()) - handleFunction(**m_functionsToVisit.begin()); -} - -void FullInliner::handleFunction(FunctionDefinition& _fun) -{ - if (!m_functionsToVisit.count(&_fun)) - return; - m_functionsToVisit.erase(&_fun); - (InlineModifier(*this, m_nameDispenser, _fun.name))(_fun.body); -} - -void InlineModifier::operator()(FunctionalInstruction& _instruction) -{ - visitArguments(_instruction.arguments); -} - -void InlineModifier::operator()(FunctionCall&) -{ - assertThrow(false, OptimizerException, "Should be handled in visit() instead."); -} - -void InlineModifier::operator()(ForLoop& _loop) -{ - (*this)(_loop.pre); - // Do not visit the condition because we cannot inline there. - (*this)(_loop.post); - (*this)(_loop.body); -} - -void InlineModifier::operator()(Block& _block) -{ - // This is only used if needed to minimize the number of move operations. - vector<Statement> modifiedStatements; - for (size_t i = 0; i < _block.statements.size(); ++i) - { - visit(_block.statements.at(i)); - if (!m_statementsToPrefix.empty()) - { - if (modifiedStatements.empty()) - std::move( - _block.statements.begin(), - _block.statements.begin() + i, - back_inserter(modifiedStatements) - ); - modifiedStatements += std::move(m_statementsToPrefix); - m_statementsToPrefix.clear(); - } - if (!modifiedStatements.empty()) - modifiedStatements.emplace_back(std::move(_block.statements[i])); - } - if (!modifiedStatements.empty()) - _block.statements = std::move(modifiedStatements); -} - -void InlineModifier::visit(Expression& _expression) -{ - if (_expression.type() != typeid(FunctionCall)) - return ASTModifier::visit(_expression); - - FunctionCall& funCall = boost::get<FunctionCall>(_expression); - FunctionDefinition& fun = m_driver.function(funCall.functionName.name); - - m_driver.handleFunction(fun); - - // TODO: Insert good heuristic here. Perhaps implement that inside the driver. - bool doInline = funCall.functionName.name != m_currentFunction; - - if (fun.returnVariables.size() > 1) - doInline = false; - - { - vector<string> argNames; - vector<string> argTypes; - for (auto const& arg: fun.parameters) - { - argNames.push_back(fun.name + "_" + arg.name); - argTypes.push_back(arg.type); - } - visitArguments(funCall.arguments, argNames, argTypes, doInline); - } - - if (!doInline) - return; - - map<string, string> variableReplacements; - for (size_t i = 0; i < funCall.arguments.size(); ++i) - variableReplacements[fun.parameters[i].name] = boost::get<Identifier>(funCall.arguments[i]).name; - if (fun.returnVariables.empty()) - _expression = noop(funCall.location); - else - { - string returnVariable = fun.returnVariables[0].name; - variableReplacements[returnVariable] = newName(fun.name + "_" + returnVariable); - - m_statementsToPrefix.emplace_back(VariableDeclaration{ - funCall.location, - {{funCall.location, variableReplacements[returnVariable], fun.returnVariables[0].type}}, - {} - }); - _expression = Identifier{funCall.location, variableReplacements[returnVariable]}; - } - m_statementsToPrefix.emplace_back(BodyCopier(m_nameDispenser, fun.name + "_", variableReplacements)(fun.body)); -} - -void InlineModifier::visit(Statement& _statement) -{ - ASTModifier::visit(_statement); - // Replace pop(0) expression statemets (and others) by empty blocks. - if (_statement.type() == typeid(ExpressionStatement)) - { - ExpressionStatement& expSt = boost::get<ExpressionStatement>(_statement); - if (expSt.expression.type() == typeid(FunctionalInstruction)) - { - FunctionalInstruction& funInstr = boost::get<FunctionalInstruction>(expSt.expression); - if (funInstr.instruction == solidity::Instruction::POP) - if (MovableChecker(funInstr.arguments.at(0)).movable()) - _statement = Block{expSt.location, {}}; - } - } -} - -void InlineModifier::visitArguments( - vector<Expression>& _arguments, - vector<string> const& _nameHints, - vector<string> const& _types, - bool _moveToFront -) -{ - // If one of the elements moves parts to the front, all other elements right of it - // also have to be moved to the front to keep the order of evaluation. - vector<Statement> prefix; - for (size_t i = 0; i < _arguments.size(); ++i) - { - auto& arg = _arguments[i]; - // TODO optimize vector operations, check that it actually moves - auto internalPrefix = visitRecursively(arg); - if (!internalPrefix.empty()) - { - _moveToFront = true; - // We go through the arguments left to right, so we have to invert - // the prefixes. - prefix = std::move(internalPrefix) + std::move(prefix); - } - else if (_moveToFront) - { - auto location = locationOf(arg); - string var = newName(i < _nameHints.size() ? _nameHints[i] : ""); - prefix.emplace(prefix.begin(), VariableDeclaration{ - location, - {{TypedName{location, var, i < _types.size() ? _types[i] : ""}}}, - make_shared<Expression>(std::move(arg)) - }); - arg = Identifier{location, var}; - } - } - m_statementsToPrefix += std::move(prefix); -} - -vector<Statement> InlineModifier::visitRecursively(Expression& _expression) -{ - vector<Statement> saved; - saved.swap(m_statementsToPrefix); - visit(_expression); - saved.swap(m_statementsToPrefix); - return saved; -} - -string InlineModifier::newName(string const& _prefix) -{ - return m_nameDispenser.newName(_prefix); -} - -Expression InlineModifier::noop(SourceLocation const& _location) -{ - return FunctionalInstruction{_location, solidity::Instruction::POP, { - Literal{_location, assembly::LiteralKind::Number, "0", ""} - }}; -} - -Statement BodyCopier::operator()(VariableDeclaration const& _varDecl) -{ - for (auto const& var: _varDecl.variables) - m_variableReplacements[var.name] = m_nameDispenser.newName(m_varNamePrefix + var.name); - return ASTCopier::operator()(_varDecl); -} - -Statement BodyCopier::operator()(FunctionDefinition const& _funDef) -{ - assertThrow(false, OptimizerException, "Function hoisting has to be done before function inlining."); - return _funDef; -} - -string BodyCopier::translateIdentifier(string const& _name) -{ - if (m_variableReplacements.count(_name)) - return m_variableReplacements.at(_name); - else - return _name; -} diff --git a/libjulia/optimiser/FullInliner.h b/libjulia/optimiser/FullInliner.h deleted file mode 100644 index ff9e6854..00000000 --- a/libjulia/optimiser/FullInliner.h +++ /dev/null @@ -1,179 +0,0 @@ -/* - This file is part of solidity. - - solidity is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - solidity is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with solidity. If not, see <http://www.gnu.org/licenses/>. -*/ -/** - * Optimiser component that performs function inlining for arbitrary functions. - */ -#pragma once - -#include <libjulia/ASTDataForward.h> - -#include <libjulia/optimiser/ASTCopier.h> -#include <libjulia/optimiser/ASTWalker.h> -#include <libjulia/optimiser/NameDispenser.h> -#include <libjulia/Exceptions.h> - -#include <libevmasm/SourceLocation.h> - -#include <boost/variant.hpp> -#include <boost/optional.hpp> - -#include <set> - -namespace dev -{ -namespace julia -{ - -class NameCollector; - - -/** - * Optimiser component that modifies an AST in place, inlining arbitrary functions. - * - * Code of the form - * - * function f(a, b) -> c { ... } - * h(g(x(...), f(arg1(...), arg2(...)), y(...)), z(...)) - * - * is transformed into - * - * function f(a, b) -> c { ... } - * - * let z1 := z(...) let y1 := y(...) let a2 := arg2(...) let a1 := arg1(...) - * let c1 := 0 - * { code of f, with replacements: a -> a1, b -> a2, c -> c1, d -> d1 } - * h(g(x(...), c1, y1), z1) - * - * No temporary variable is created for expressions that are "movable" - * (i.e. they are "pure", have no side-effects and also do not depend on other code - * that might have side-effects). - * - * This component can only be used on sources with unique names and with hoisted functions, - * i.e. the root node has to be a block that itself contains a single block followed by all - * function definitions. - */ -class FullInliner: public ASTModifier -{ -public: - explicit FullInliner(Block& _ast); - - void run(); - - /// Perform inlining operations inside the given function. - void handleFunction(FunctionDefinition& _function); - - FunctionDefinition& function(std::string _name) { return *m_functions.at(_name); } - -private: - /// The AST to be modified. The root block itself will not be modified, because - /// we store pointers to functions. - Block& m_ast; - std::map<std::string, FunctionDefinition*> m_functions; - std::set<FunctionDefinition*> m_functionsToVisit; - NameDispenser m_nameDispenser; -}; - -/** - * Class that walks the AST of a block that does not contain function definitions and perform - * the actual code modifications. - */ -class InlineModifier: public ASTModifier -{ -public: - InlineModifier(FullInliner& _driver, NameDispenser& _nameDispenser, std::string _functionName): - m_currentFunction(std::move(_functionName)), - m_driver(_driver), - m_nameDispenser(_nameDispenser) - { } - ~InlineModifier() - { - assertThrow(m_statementsToPrefix.empty(), OptimizerException, ""); - } - - virtual void operator()(FunctionalInstruction&) override; - virtual void operator()(FunctionCall&) override; - virtual void operator()(ForLoop&) override; - virtual void operator()(Block& _block) override; - - using ASTModifier::visit; - virtual void visit(Expression& _expression) override; - virtual void visit(Statement& _st) override; - -private: - - /// Visits a list of expressions (usually an argument list to a function call) and tries - /// to inline them. If one of them is inlined, all right of it have to be moved to the front - /// (to keep the order of evaluation). If @a _moveToFront is true, all elements are moved - /// to the front. @a _nameHints and @_types are used for the newly created variables, but - /// both can be empty. - void visitArguments( - std::vector<Expression>& _arguments, - std::vector<std::string> const& _nameHints = {}, - std::vector<std::string> const& _types = {}, - bool _moveToFront = false - ); - - /// Visits an expression, but saves and restores the current statements to prefix and returns - /// the statements that should be prefixed for @a _expression. - std::vector<Statement> visitRecursively(Expression& _expression); - - std::string newName(std::string const& _prefix); - - /// @returns an expression returning nothing. - Expression noop(SourceLocation const& _location); - - /// List of statements that should go in front of the currently visited AST element, - /// at the statement level. - std::vector<Statement> m_statementsToPrefix; - std::string m_currentFunction; - FullInliner& m_driver; - NameDispenser& m_nameDispenser; -}; - -/** - * Creates a copy of a block that is supposed to be the body of a function. - * Applies replacements to referenced variables and creates new names for - * variable declarations. - */ -class BodyCopier: public ASTCopier -{ -public: - BodyCopier( - NameDispenser& _nameDispenser, - std::string const& _varNamePrefix, - std::map<std::string, std::string> const& _variableReplacements - ): - m_nameDispenser(_nameDispenser), - m_varNamePrefix(_varNamePrefix), - m_variableReplacements(_variableReplacements) - {} - - using ASTCopier::operator (); - - virtual Statement operator()(VariableDeclaration const& _varDecl) override; - virtual Statement operator()(FunctionDefinition const& _funDef) override; - - virtual std::string translateIdentifier(std::string const& _name) override; - - NameDispenser& m_nameDispenser; - std::string const& m_varNamePrefix; - std::map<std::string, std::string> m_variableReplacements; -}; - - -} -} diff --git a/libjulia/optimiser/FunctionGrouper.cpp b/libjulia/optimiser/FunctionGrouper.cpp deleted file mode 100644 index f1e99e6b..00000000 --- a/libjulia/optimiser/FunctionGrouper.cpp +++ /dev/null @@ -1,47 +0,0 @@ -/* - This file is part of solidity. - - solidity is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - solidity is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with solidity. If not, see <http://www.gnu.org/licenses/>. -*/ -/** - * Optimiser component that changes the code of a block so that all non-function definition - * statements are moved to a block of their own followed by all function definitions. - */ - -#include <libjulia/optimiser/FunctionGrouper.h> - -#include <libsolidity/inlineasm/AsmData.h> - -#include <boost/range/algorithm_ext/erase.hpp> - -using namespace std; -using namespace dev; -using namespace dev::julia; -using namespace dev::solidity; - - -void FunctionGrouper::operator()(Block& _block) -{ - vector<Statement> reordered; - reordered.emplace_back(Block{_block.location, {}}); - - for (auto&& statement: _block.statements) - { - if (statement.type() == typeid(FunctionDefinition)) - reordered.emplace_back(std::move(statement)); - else - boost::get<Block>(reordered.front()).statements.emplace_back(std::move(statement)); - } - _block.statements = std::move(reordered); -} diff --git a/libjulia/optimiser/FunctionGrouper.h b/libjulia/optimiser/FunctionGrouper.h deleted file mode 100644 index 64a71318..00000000 --- a/libjulia/optimiser/FunctionGrouper.h +++ /dev/null @@ -1,46 +0,0 @@ -/* - This file is part of solidity. - - solidity is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - solidity is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with solidity. If not, see <http://www.gnu.org/licenses/>. -*/ -/** - * Optimiser component that changes the code of a black so that all non-function definition - * instructions are moved to a block of their own followed by all function definitions. - */ - -#pragma once - -#include <libjulia/ASTDataForward.h> - -namespace dev -{ -namespace julia -{ - -/** - * Moves all instructions in a block into a new block at the start of the block, followed by - * all function definitions. - * - * After this step, a block is of the form - * { { I...} F... } - * Where I are (non-function-definition) instructions and F are function definitions. - */ -class FunctionGrouper -{ -public: - void operator()(Block& _block); -}; - -} -} diff --git a/libjulia/optimiser/FunctionHoister.cpp b/libjulia/optimiser/FunctionHoister.cpp deleted file mode 100644 index 98fc714c..00000000 --- a/libjulia/optimiser/FunctionHoister.cpp +++ /dev/null @@ -1,51 +0,0 @@ -/* - This file is part of solidity. - - solidity is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - solidity is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with solidity. If not, see <http://www.gnu.org/licenses/>. -*/ -/** - * Optimiser component that changes the code so that it consists of a block starting with - * a single block followed only by function definitions and with no functions defined - * anywhere else. - */ - -#include <libjulia/optimiser/FunctionHoister.h> -#include <libjulia/optimiser/Utilities.h> - -#include <libsolidity/inlineasm/AsmData.h> - -#include <libdevcore/CommonData.h> - -using namespace std; -using namespace dev; -using namespace dev::julia; -using namespace dev::solidity; - -void FunctionHoister::operator()(Block& _block) -{ - bool topLevel = m_isTopLevel; - m_isTopLevel = false; - for (auto&& statement: _block.statements) - { - boost::apply_visitor(*this, statement); - if (statement.type() == typeid(FunctionDefinition)) - { - m_functions.emplace_back(std::move(statement)); - statement = Block{_block.location, {}}; - } - } - removeEmptyBlocks(_block); - if (topLevel) - _block.statements += std::move(m_functions); -} diff --git a/libjulia/optimiser/FunctionHoister.h b/libjulia/optimiser/FunctionHoister.h deleted file mode 100644 index f9f8bce0..00000000 --- a/libjulia/optimiser/FunctionHoister.h +++ /dev/null @@ -1,52 +0,0 @@ -/* - This file is part of solidity. - - solidity is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - solidity is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with solidity. If not, see <http://www.gnu.org/licenses/>. -*/ -/** - * Optimiser component that changes the code so that all function definitions are at the top - * level block. - */ - -#pragma once - -#include <libjulia/ASTDataForward.h> - -#include <libjulia/optimiser/ASTWalker.h> - -namespace dev -{ -namespace julia -{ - -/** - * Moves all functions to the top-level scope. - * Applying this transformation to source code that has ambiguous identifiers might - * lead to invalid code. - * - * Prerequisites: Disambiguator - */ -class FunctionHoister: public ASTModifier -{ -public: - using ASTModifier::operator(); - virtual void operator()(Block& _block); - -private: - bool m_isTopLevel = true; - std::vector<Statement> m_functions; -}; - -} -} diff --git a/libjulia/optimiser/InlinableExpressionFunctionFinder.cpp b/libjulia/optimiser/InlinableExpressionFunctionFinder.cpp deleted file mode 100644 index e237063d..00000000 --- a/libjulia/optimiser/InlinableExpressionFunctionFinder.cpp +++ /dev/null @@ -1,70 +0,0 @@ -/* - This file is part of solidity. - - solidity is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - solidity is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with solidity. If not, see <http://www.gnu.org/licenses/>. -*/ -/** - * Optimiser component that identifies functions to be inlined. - */ - -#include <libjulia/optimiser/InlinableExpressionFunctionFinder.h> - -#include <libjulia/optimiser/Utilities.h> - -#include <libsolidity/inlineasm/AsmData.h> - -using namespace std; -using namespace dev; -using namespace dev::julia; - -void InlinableExpressionFunctionFinder::operator()(Identifier const& _identifier) -{ - checkAllowed(_identifier.name); - ASTWalker::operator()(_identifier); -} - -void InlinableExpressionFunctionFinder::operator()(FunctionCall const& _funCall) -{ - checkAllowed(_funCall.functionName.name); - ASTWalker::operator()(_funCall); -} - -void InlinableExpressionFunctionFinder::operator()(FunctionDefinition const& _function) -{ - if (_function.returnVariables.size() == 1 && _function.body.statements.size() == 1) - { - string const& retVariable = _function.returnVariables.front().name; - Statement const& bodyStatement = _function.body.statements.front(); - if (bodyStatement.type() == typeid(Assignment)) - { - Assignment const& assignment = boost::get<Assignment>(bodyStatement); - if (assignment.variableNames.size() == 1 && assignment.variableNames.front().name == retVariable) - { - // FIXME: use code size metric here - - // We cannot overwrite previous settings, because this function definition - // would not be valid here if we were searching inside a functionally inlinable - // function body. - assertThrow(m_disallowedIdentifiers.empty() && !m_foundDisallowedIdentifier, OptimizerException, ""); - m_disallowedIdentifiers = set<string>{retVariable, _function.name}; - boost::apply_visitor(*this, *assignment.value); - if (!m_foundDisallowedIdentifier) - m_inlinableFunctions[_function.name] = &_function; - m_disallowedIdentifiers.clear(); - m_foundDisallowedIdentifier = false; - } - } - } - ASTWalker::operator()(_function.body); -} diff --git a/libjulia/optimiser/InlinableExpressionFunctionFinder.h b/libjulia/optimiser/InlinableExpressionFunctionFinder.h deleted file mode 100644 index d11160d7..00000000 --- a/libjulia/optimiser/InlinableExpressionFunctionFinder.h +++ /dev/null @@ -1,69 +0,0 @@ -/* - This file is part of solidity. - - solidity is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - solidity is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with solidity. If not, see <http://www.gnu.org/licenses/>. -*/ -/** - * Optimiser component that identifies functions to be inlined. - */ - -#pragma once - -#include <libjulia/ASTDataForward.h> -#include <libjulia/optimiser/ASTWalker.h> - -#include <set> - -namespace dev -{ -namespace julia -{ - -/** - * Optimiser component that finds functions that can be - * inlined inside functional expressions, i.e. functions that - * - have a single return parameter r - * - have a body like r := <functional expression> - * - neither reference themselves nor r in the right hand side - * - * This component can only be used on sources with unique names. - */ -class InlinableExpressionFunctionFinder: public ASTWalker -{ -public: - - std::map<std::string, FunctionDefinition const*> const& inlinableFunctions() const - { - return m_inlinableFunctions; - } - - using ASTWalker::operator(); - virtual void operator()(Identifier const& _identifier) override; - virtual void operator()(FunctionCall const& _funCall) override; - virtual void operator()(FunctionDefinition const& _function) override; - -private: - void checkAllowed(std::string const& _name) - { - if (m_disallowedIdentifiers.count(_name)) - m_foundDisallowedIdentifier = true; - } - - bool m_foundDisallowedIdentifier = false; - std::set<std::string> m_disallowedIdentifiers; - std::map<std::string, FunctionDefinition const*> m_inlinableFunctions; -}; - -} -} diff --git a/libjulia/optimiser/MainFunction.cpp b/libjulia/optimiser/MainFunction.cpp deleted file mode 100644 index bcd2f178..00000000 --- a/libjulia/optimiser/MainFunction.cpp +++ /dev/null @@ -1,54 +0,0 @@ -/* - This file is part of solidity. - - solidity is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - solidity is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with solidity. If not, see <http://www.gnu.org/licenses/>. -*/ -/** - * Changes the topmost block to be a function with a specific name ("main") which has no - * inputs nor outputs. - */ - -#include <libjulia/optimiser/MainFunction.h> - -#include <libjulia/optimiser/NameCollector.h> -#include <libjulia/Exceptions.h> - -#include <libsolidity/inlineasm/AsmData.h> - -#include <libdevcore/CommonData.h> - -using namespace std; -using namespace dev; -using namespace dev::julia; -using namespace dev::solidity; - -void MainFunction::operator()(Block& _block) -{ - assertThrow(_block.statements.size() >= 1, OptimizerException, ""); - assertThrow(_block.statements[0].type() == typeid(Block), OptimizerException, ""); - for (size_t i = 1; i < _block.statements.size(); ++i) - assertThrow(_block.statements.at(i).type() == typeid(FunctionDefinition), OptimizerException, ""); - /// @todo this should handle scopes properly and instead of an assertion it should rename the conflicting function - assertThrow(NameCollector(_block).names().count("main") == 0, OptimizerException, ""); - - Block& block = boost::get<Block>(_block.statements[0]); - FunctionDefinition main{ - block.location, - "main", - {}, - {}, - std::move(block) - }; - _block.statements[0] = std::move(main); -} diff --git a/libjulia/optimiser/MainFunction.h b/libjulia/optimiser/MainFunction.h deleted file mode 100644 index 7201d89a..00000000 --- a/libjulia/optimiser/MainFunction.h +++ /dev/null @@ -1,41 +0,0 @@ -/* - This file is part of solidity. - - solidity is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - solidity is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with solidity. If not, see <http://www.gnu.org/licenses/>. -*/ -/** - * Changes the topmost block to be a function with a specific name ("main") which has no - * inputs nor outputs. - */ - -#pragma once - -#include <libjulia/ASTDataForward.h> - -namespace dev -{ -namespace julia -{ - -/** - * Prerequisites: Function Grouper - */ -class MainFunction -{ -public: - void operator()(Block& _block); -}; - -} -} diff --git a/libjulia/optimiser/Metrics.cpp b/libjulia/optimiser/Metrics.cpp deleted file mode 100644 index eaa1494f..00000000 --- a/libjulia/optimiser/Metrics.cpp +++ /dev/null @@ -1,52 +0,0 @@ -/*( - This file is part of solidity. - - solidity is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - solidity is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with solidity. If not, see <http://www.gnu.org/licenses/>. -*/ -/** -* Module providing metrics for the optimizer. -*/ - -#include <libjulia/optimiser/Metrics.h> - -#include <libsolidity/inlineasm/AsmData.h> - -using namespace dev; -using namespace dev::julia; - -size_t CodeSize::codeSize(Statement const& _statement) -{ - CodeSize cs; - cs.visit(_statement); - return cs.m_size; -} - -size_t CodeSize::codeSize(Expression const& _expression) -{ - CodeSize cs; - cs.visit(_expression); - return cs.m_size; -} - -void CodeSize::visit(Statement const& _statement) -{ - ++m_size; - ASTWalker::visit(_statement); -} - -void CodeSize::visit(Expression const& _expression) -{ - ++m_size; - ASTWalker::visit(_expression); -} diff --git a/libjulia/optimiser/Metrics.h b/libjulia/optimiser/Metrics.h deleted file mode 100644 index ff058fc3..00000000 --- a/libjulia/optimiser/Metrics.h +++ /dev/null @@ -1,49 +0,0 @@ -/* - This file is part of solidity. - - solidity is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - solidity is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with solidity. If not, see <http://www.gnu.org/licenses/>. -*/ -/** - * Module providing metrics for the optimizer. - */ - -#pragma once - -#include <libjulia/optimiser/ASTWalker.h> - -namespace dev -{ -namespace julia -{ - -class CodeSize: public ASTWalker -{ -public: - /// Returns a metric for the code size of an AST element. - /// More specifically, it returns the number of AST nodes. - static size_t codeSize(Statement const& _statement); - /// Returns a metric for the code size of an AST element. - /// More specifically, it returns the number of AST nodes. - static size_t codeSize(Expression const& _expression); - -private: - virtual void visit(Statement const& _statement) override; - virtual void visit(Expression const& _expression) override; - -private: - size_t m_size = 0; -}; - -} -} diff --git a/libjulia/optimiser/NameCollector.cpp b/libjulia/optimiser/NameCollector.cpp deleted file mode 100644 index c0d0b707..00000000 --- a/libjulia/optimiser/NameCollector.cpp +++ /dev/null @@ -1,74 +0,0 @@ -/* - This file is part of solidity. - - solidity is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - solidity is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with solidity. If not, see <http://www.gnu.org/licenses/>. -*/ -/** - * Specific AST walker that collects all defined names. - */ - -#include <libjulia/optimiser/NameCollector.h> - -#include <libsolidity/inlineasm/AsmData.h> - -using namespace std; -using namespace dev; -using namespace dev::julia; - -void NameCollector::operator()(VariableDeclaration const& _varDecl) -{ - for (auto const& var: _varDecl.variables) - m_names.insert(var.name); -} - -void NameCollector::operator ()(FunctionDefinition const& _funDef) -{ - m_names.insert(_funDef.name); - for (auto const arg: _funDef.parameters) - m_names.insert(arg.name); - for (auto const ret: _funDef.returnVariables) - m_names.insert(ret.name); - ASTWalker::operator ()(_funDef); -} - -void ReferencesCounter::operator()(Identifier const& _identifier) -{ - ++m_references[_identifier.name]; -} - -void ReferencesCounter::operator()(FunctionCall const& _funCall) -{ - ++m_references[_funCall.functionName.name]; - ASTWalker::operator()(_funCall); -} - -map<string, size_t> ReferencesCounter::countReferences(Block const& _block) -{ - ReferencesCounter counter; - counter(_block); - return counter.references(); -} - -map<string, size_t> ReferencesCounter::countReferences(Expression const& _expression) -{ - ReferencesCounter counter; - counter.visit(_expression); - return counter.references(); -} - -void Assignments::operator()(Assignment const& _assignment) -{ - for (auto const& var: _assignment.variableNames) - m_names.insert(var.name); -} diff --git a/libjulia/optimiser/NameCollector.h b/libjulia/optimiser/NameCollector.h deleted file mode 100644 index 29856172..00000000 --- a/libjulia/optimiser/NameCollector.h +++ /dev/null @@ -1,87 +0,0 @@ -/* - This file is part of solidity. - - solidity is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - solidity is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with solidity. If not, see <http://www.gnu.org/licenses/>. -*/ -/** - * Specific AST walkers that collect facts about identifiers and definitions. - */ - -#pragma once - -#include <libjulia/optimiser/ASTWalker.h> - -#include <string> -#include <map> -#include <set> - -namespace dev -{ -namespace julia -{ - -/** - * Specific AST walker that collects all defined names. - */ -class NameCollector: public ASTWalker -{ -public: - explicit NameCollector(Block const& _block) - { - (*this)(_block); - } - - using ASTWalker::operator (); - virtual void operator()(VariableDeclaration const& _varDecl) override; - virtual void operator()(FunctionDefinition const& _funDef) override; - - std::set<std::string> names() const { return m_names; } -private: - std::set<std::string> m_names; -}; - -/** - * Specific AST walker that counts all references to all declarations. - */ -class ReferencesCounter: public ASTWalker -{ -public: - using ASTWalker::operator (); - virtual void operator()(Identifier const& _identifier); - virtual void operator()(FunctionCall const& _funCall); - - static std::map<std::string, size_t> countReferences(Block const& _block); - static std::map<std::string, size_t> countReferences(Expression const& _expression); - - std::map<std::string, size_t> const& references() const { return m_references; } -private: - std::map<std::string, size_t> m_references; -}; - -/** - * Specific AST walker that finds all variables that are assigned to. - */ -class Assignments: public ASTWalker -{ -public: - using ASTWalker::operator (); - virtual void operator()(Assignment const& _assignment) override; - - std::set<std::string> const& names() const { return m_names; } -private: - std::set<std::string> m_names; -}; - -} -} diff --git a/libjulia/optimiser/NameDispenser.cpp b/libjulia/optimiser/NameDispenser.cpp deleted file mode 100644 index e4f0e4f6..00000000 --- a/libjulia/optimiser/NameDispenser.cpp +++ /dev/null @@ -1,38 +0,0 @@ -/* - This file is part of solidity. - - solidity is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - solidity is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with solidity. If not, see <http://www.gnu.org/licenses/>. -*/ -/** - * Optimiser component that can create new unique names. - */ - -#include <libjulia/optimiser/NameDispenser.h> - -using namespace std; -using namespace dev; -using namespace dev::julia; - -string NameDispenser::newName(string const& _prefix) -{ - string name = _prefix; - size_t suffix = 0; - while (name.empty() || m_usedNames.count(name)) - { - suffix++; - name = _prefix + "_" + std::to_string(suffix); - } - m_usedNames.insert(name); - return name; -} diff --git a/libjulia/optimiser/NameDispenser.h b/libjulia/optimiser/NameDispenser.h deleted file mode 100644 index 91c43d54..00000000 --- a/libjulia/optimiser/NameDispenser.h +++ /dev/null @@ -1,37 +0,0 @@ -/* - This file is part of solidity. - - solidity is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - solidity is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with solidity. If not, see <http://www.gnu.org/licenses/>. -*/ -/** - * Optimiser component that can create new unique names. - */ -#pragma once - -#include <set> -#include <string> - -namespace dev -{ -namespace julia -{ - -struct NameDispenser -{ - std::string newName(std::string const& _prefix); - std::set<std::string> m_usedNames; -}; - -} -} diff --git a/libjulia/optimiser/README.md b/libjulia/optimiser/README.md deleted file mode 100644 index e8aa777a..00000000 --- a/libjulia/optimiser/README.md +++ /dev/null @@ -1,98 +0,0 @@ -## IULIA Optimiser - -The iulia optimiser consists of several stages and components that all transform -the AST in a semantically equivalent way. The goal is to end up either with code -that is shorter or at least only marginally longer but will allow further -optimisation steps. - -The optimiser currently follows a purely greedy strategy and does not do any -backtracking. - -## Disambiguator - -The disambiguator takes an AST and returns a fresh copy where all identifiers have -names unique to the input AST. This is a prerequisite for all other optimiser stages. -One of the benefits is that identifier lookup does not need to take scopes into account -and we can basically ignore the result of the analysis phase. - -All subsequent stages have the property that all names stay unique. This means if -a new identifier needs to be introduced, a new unique name is generated. - -## Function Hoister - -The function hoister moves all function definitions to the topmost block. This is -a semantically equivalent transformation as long as it is performed after the -disambiguation stage. The reason is that moving a definition upwards cannot decrease -its visibility and it is impossible to reference variables defined in a different function. - -The benefit of this stage is that function definitions can be lookup up more easily. - -## Function Grouper - -The function grouper has to be applied after the disambiguator and the function hoister. -Its effect is that all topmost elements that are not function definitions are moved -into a single block which is the first satement of the root block. - -After this step, a program has the following normal form: - - { I F... } - -Where I is a block that does not contain any function definitions (not even recursively) -and F is a list of function definitions such that no function contains a function definition. - -## Functional Inliner - -The functional inliner depends on the disambiguator, the function hoister and function grouper. -It performs function inlining such that the result of the inlining is an expression. This can -only be done if the body of the function to be inlined has the form ``{ r := E }`` where ``r`` -is the single return value of the function, ``E`` is an expression and all arguments in the -function call are so-called movable expressions. A movable expression is either a literal, a -variable or a function call (or EVM opcode) which does not have side-effects and also does not -depend on any side-effects. - -As an example, neither ``mload`` nor ``mstore`` would be allowed. - -## Full Function Inliner - -## Rematerialisation - -The rematerialisation stage tries to replace variable references by the expression that -was last assigned to the variable. This is of course only beneficial if this expression -is comparatively cheap to evaluate. Furthermore, it is only semantically equivalent if -the value of the expression did not change between the point of assignment and the -point of use. The main benefit of this stage is that it can save stack slots if it -leads to a variable being eliminated completely (see below), but it can also -save a DUP opcode on the EVM if the expression is very cheap. - -The algorithm only allows movable expressions (see above for a definition) in this case. -Expressions that contain other variables are also disallowed if one of those variables -have been assigned to in the meantime. This is also not applied to variables where -assignment and use span across loops and conditionals. - -## Unused Definition Pruner - -If a variable or function is not referenced, it is removed from the code. -If there are two assignments to a variable where the first one is a movable expression -and the variable is not used between the two assignments (and the second is not inside -a loop or conditional, the first one is not inside), the first assignment is removed. - - -## Function Unifier - -## Expression Simplifier - -This step can only be applied for the EVM-flavoured dialect of iulia. It applies -simple rules like ``x + 0 == x`` to simplify expressions. - -## Ineffective Statement Remover - -This step removes statements that have no side-effects. - -## WebAssembly specific - -### Main Function - -Changes the topmost block to be a function with a specific name ("main") which has no -inputs nor outputs. - -Depends on the Function Grouper. diff --git a/libjulia/optimiser/Rematerialiser.cpp b/libjulia/optimiser/Rematerialiser.cpp deleted file mode 100644 index 392099fb..00000000 --- a/libjulia/optimiser/Rematerialiser.cpp +++ /dev/null @@ -1,55 +0,0 @@ -/*( - This file is part of solidity. - - solidity is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - solidity is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with solidity. If not, see <http://www.gnu.org/licenses/>. -*/ -/** - * Optimisation stage that replaces variables by their most recently assigned expressions. - */ - -#include <libjulia/optimiser/Rematerialiser.h> - -#include <libjulia/optimiser/Metrics.h> -#include <libjulia/optimiser/ASTCopier.h> -#include <libjulia/Exceptions.h> - -#include <libsolidity/inlineasm/AsmData.h> - -using namespace std; -using namespace dev; -using namespace dev::julia; - -void Rematerialiser::visit(Expression& _e) -{ - if (_e.type() == typeid(Identifier)) - { - Identifier& identifier = boost::get<Identifier>(_e); - if (m_value.count(identifier.name)) - { - string name = identifier.name; - bool expressionValid = true; - for (auto const& ref: m_references[name]) - if (!inScope(ref)) - { - expressionValid = false; - break; - } - assertThrow(m_value.at(name), OptimizerException, ""); - auto const& value = *m_value.at(name); - if (expressionValid && CodeSize::codeSize(value) <= 7) - _e = (ASTCopier{}).translate(value); - } - } - DataFlowAnalyzer::visit(_e); -} diff --git a/libjulia/optimiser/Rematerialiser.h b/libjulia/optimiser/Rematerialiser.h deleted file mode 100644 index 60dbfada..00000000 --- a/libjulia/optimiser/Rematerialiser.h +++ /dev/null @@ -1,48 +0,0 @@ -/* - This file is part of solidity. - - solidity is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - solidity is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with solidity. If not, see <http://www.gnu.org/licenses/>. -*/ -/** - * Optimisation stage that replaces variables by their most recently assigned expressions. - */ - -#pragma once - -#include <libjulia/optimiser/DataFlowAnalyzer.h> - -#include <string> -#include <map> -#include <set> - -namespace dev -{ -namespace julia -{ - -/** - * Optimisation stage that replaces variables by their most recently assigned expressions. - * - * Prerequisite: Disambiguator - */ -class Rematerialiser: public DataFlowAnalyzer -{ -protected: - using ASTModifier::visit; - virtual void visit(Expression& _e) override; - -}; - -} -} diff --git a/libjulia/optimiser/Semantics.cpp b/libjulia/optimiser/Semantics.cpp deleted file mode 100644 index f28925a4..00000000 --- a/libjulia/optimiser/Semantics.cpp +++ /dev/null @@ -1,62 +0,0 @@ -/*( - This file is part of solidity. - - solidity is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - solidity is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with solidity. If not, see <http://www.gnu.org/licenses/>. -*/ -/** - * Specific AST walkers that collect semantical facts. - */ - -#include <libjulia/optimiser/Semantics.h> - -#include <libjulia/Exceptions.h> - -#include <libsolidity/inlineasm/AsmData.h> - -#include <libevmasm/SemanticInformation.h> - -#include <libdevcore/CommonData.h> - -using namespace std; -using namespace dev; -using namespace dev::julia; - -MovableChecker::MovableChecker(Expression const& _expression) -{ - visit(_expression); -} - -void MovableChecker::operator()(Identifier const& _identifier) -{ - ASTWalker::operator()(_identifier); - m_variableReferences.insert(_identifier.name); -} - -void MovableChecker::operator()(FunctionalInstruction const& _instr) -{ - if (!eth::SemanticInformation::movable(_instr.instruction)) - m_movable = false; - else - ASTWalker::operator()(_instr); -} - -void MovableChecker::operator()(FunctionCall const&) -{ - m_movable = false; -} - -void MovableChecker::visit(Statement const&) -{ - assertThrow(false, OptimizerException, "Movability for statement requested."); -} diff --git a/libjulia/optimiser/Semantics.h b/libjulia/optimiser/Semantics.h deleted file mode 100644 index 6df5f01a..00000000 --- a/libjulia/optimiser/Semantics.h +++ /dev/null @@ -1,62 +0,0 @@ -/* - This file is part of solidity. - - solidity is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - solidity is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with solidity. If not, see <http://www.gnu.org/licenses/>. -*/ -/** - * Specific AST walkers that collect semantical facts. - */ - -#pragma once - -#include <libjulia/optimiser/ASTWalker.h> - -#include <string> -#include <map> -#include <set> - -namespace dev -{ -namespace julia -{ - -/** - * Specific AST walker that determines whether an expression is movable. - */ -class MovableChecker: public ASTWalker -{ -public: - MovableChecker() = default; - explicit MovableChecker(Expression const& _expression); - - virtual void operator()(Identifier const& _identifier) override; - virtual void operator()(FunctionalInstruction const& _functionalInstruction) override; - virtual void operator()(FunctionCall const& _functionCall) override; - - /// Disallow visiting anything apart from Expressions (this throws). - virtual void visit(Statement const&) override; - using ASTWalker::visit; - - bool movable() const { return m_movable; } - std::set<std::string> const& referencedVariables() const { return m_variableReferences; } - -private: - /// Which variables the current expression references. - std::set<std::string> m_variableReferences; - /// Is the current expression movable or not. - bool m_movable = true; -}; - -} -} diff --git a/libjulia/optimiser/SimplificationRules.cpp b/libjulia/optimiser/SimplificationRules.cpp deleted file mode 100644 index 070d5484..00000000 --- a/libjulia/optimiser/SimplificationRules.cpp +++ /dev/null @@ -1,182 +0,0 @@ -/* - This file is part of solidity. - - solidity is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - solidity is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with solidity. If not, see <http://www.gnu.org/licenses/>. -*/ -/** - * Module for applying replacement rules against Expressions. - */ - -#include <libjulia/optimiser/SimplificationRules.h> - -#include <libjulia/optimiser/Utilities.h> -#include <libjulia/optimiser/ASTCopier.h> -#include <libjulia/optimiser/Semantics.h> -#include <libjulia/optimiser/SyntacticalEquality.h> - -#include <libsolidity/inlineasm/AsmData.h> - -#include <libevmasm/RuleList.h> - -using namespace std; -using namespace dev; -using namespace dev::julia; - - -SimplificationRule<Pattern> const* SimplificationRules::findFirstMatch(Expression const& _expr) -{ - if (_expr.type() != typeid(FunctionalInstruction)) - return nullptr; - - static SimplificationRules rules; - - FunctionalInstruction const& instruction = boost::get<FunctionalInstruction>(_expr); - for (auto const& rule: rules.m_rules[byte(instruction.instruction)]) - { - rules.resetMatchGroups(); - if (rule.pattern.matches(_expr)) - return &rule; - } - return nullptr; -} - -void SimplificationRules::addRules(vector<SimplificationRule<Pattern>> const& _rules) -{ - for (auto const& r: _rules) - addRule(r); -} - -void SimplificationRules::addRule(SimplificationRule<Pattern> const& _rule) -{ - m_rules[byte(_rule.pattern.instruction())].push_back(_rule); -} - -SimplificationRules::SimplificationRules() -{ - // Multiple occurences of one of these inside one rule must match the same equivalence class. - // Constants. - Pattern A(PatternKind::Constant); - Pattern B(PatternKind::Constant); - Pattern C(PatternKind::Constant); - // Anything. - Pattern X; - Pattern Y; - A.setMatchGroup(1, m_matchGroups); - B.setMatchGroup(2, m_matchGroups); - C.setMatchGroup(3, m_matchGroups); - X.setMatchGroup(4, m_matchGroups); - Y.setMatchGroup(5, m_matchGroups); - - addRules(simplificationRuleList(A, B, C, X, Y)); -} - -Pattern::Pattern(solidity::Instruction _instruction, vector<Pattern> const& _arguments): - m_kind(PatternKind::Operation), - m_instruction(_instruction), - m_arguments(_arguments) -{ -} - -void Pattern::setMatchGroup(unsigned _group, map<unsigned, Expression const*>& _matchGroups) -{ - m_matchGroup = _group; - m_matchGroups = &_matchGroups; -} - -bool Pattern::matches(Expression const& _expr) const -{ - if (m_kind == PatternKind::Constant) - { - if (_expr.type() != typeid(Literal)) - return false; - Literal const& literal = boost::get<Literal>(_expr); - if (literal.kind != assembly::LiteralKind::Number) - return false; - if (m_data && *m_data != u256(literal.value)) - return false; - assertThrow(m_arguments.empty(), OptimizerException, ""); - } - else if (m_kind == PatternKind::Operation) - { - if (_expr.type() != typeid(FunctionalInstruction)) - return false; - FunctionalInstruction const& instr = boost::get<FunctionalInstruction>(_expr); - if (m_instruction != instr.instruction) - return false; - assertThrow(m_arguments.size() == instr.arguments.size(), OptimizerException, ""); - for (size_t i = 0; i < m_arguments.size(); ++i) - if (!m_arguments[i].matches(instr.arguments.at(i))) - return false; - } - else - { - assertThrow(m_arguments.empty(), OptimizerException, ""); - } - // We support matching multiple expressions that require the same value - // based on identical ASTs, which have to be movable. - if (m_matchGroup) - { - if (m_matchGroups->count(m_matchGroup)) - { - Expression const* firstMatch = (*m_matchGroups)[m_matchGroup]; - assertThrow(firstMatch, OptimizerException, "Match set but to null."); - return - SyntacticalEqualityChecker::equal(*firstMatch, _expr) && - MovableChecker(_expr).movable(); - } - else - (*m_matchGroups)[m_matchGroup] = &_expr; - } - return true; -} - -solidity::Instruction Pattern::instruction() const -{ - assertThrow(m_kind == PatternKind::Operation, OptimizerException, ""); - return m_instruction; -} - -Expression Pattern::toExpression(SourceLocation const& _location) const -{ - if (matchGroup()) - return ASTCopier().translate(matchGroupValue()); - if (m_kind == PatternKind::Constant) - { - assertThrow(m_data, OptimizerException, "No match group and no constant value given."); - return Literal{_location, assembly::LiteralKind::Number, formatNumber(*m_data), ""}; - } - else if (m_kind == PatternKind::Operation) - { - vector<Expression> arguments; - for (auto const& arg: m_arguments) - arguments.emplace_back(arg.toExpression(_location)); - return FunctionalInstruction{_location, m_instruction, std::move(arguments)}; - } - assertThrow(false, OptimizerException, "Pattern of kind 'any', but no match group."); -} - -u256 Pattern::d() const -{ - Literal const& literal = boost::get<Literal>(matchGroupValue()); - assertThrow(literal.kind == assembly::LiteralKind::Number, OptimizerException, ""); - return u256(literal.value); -} - -Expression const& Pattern::matchGroupValue() const -{ - assertThrow(m_matchGroup > 0, OptimizerException, ""); - assertThrow(!!m_matchGroups, OptimizerException, ""); - assertThrow((*m_matchGroups)[m_matchGroup], OptimizerException, ""); - return *(*m_matchGroups)[m_matchGroup]; -} diff --git a/libjulia/optimiser/SimplificationRules.h b/libjulia/optimiser/SimplificationRules.h deleted file mode 100644 index 68b640b1..00000000 --- a/libjulia/optimiser/SimplificationRules.h +++ /dev/null @@ -1,117 +0,0 @@ -/* - This file is part of solidity. - - solidity is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - solidity is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with solidity. If not, see <http://www.gnu.org/licenses/>. -*/ -/** - * Module for applying replacement rules against Expressions. - */ - -#pragma once - -#include <libevmasm/ExpressionClasses.h> -#include <libevmasm/SimplificationRule.h> - -#include <libjulia/ASTDataForward.h> - -#include <libsolidity/inlineasm/AsmData.h> - -#include <boost/noncopyable.hpp> - -#include <functional> -#include <vector> - -namespace dev -{ -namespace julia -{ - -class Pattern; - -/** - * Container for all simplification rules. - */ -class SimplificationRules: public boost::noncopyable -{ -public: - SimplificationRules(); - - /// @returns a pointer to the first matching pattern and sets the match - /// groups accordingly. - static SimplificationRule<Pattern> const* findFirstMatch(Expression const& _expr); - -private: - void addRules(std::vector<SimplificationRule<Pattern>> const& _rules); - void addRule(SimplificationRule<Pattern> const& _rule); - - void resetMatchGroups() { m_matchGroups.clear(); } - - std::map<unsigned, Expression const*> m_matchGroups; - std::vector<SimplificationRule<Pattern>> m_rules[256]; -}; - -enum class PatternKind -{ - Operation, - Constant, - Any -}; - -/** - * Pattern to match against an expression. - * Also stores matched expressions to retrieve them later, for constructing new expressions using - * ExpressionTemplate. - */ -class Pattern -{ -public: - /// Matches any expression. - Pattern(PatternKind _kind = PatternKind::Any): m_kind(_kind) {} - // Matches a specific constant value. - Pattern(unsigned _value): Pattern(u256(_value)) {} - // Matches a specific constant value. - Pattern(u256 const& _value): m_kind(PatternKind::Constant), m_data(std::make_shared<u256>(_value)) {} - // Matches a given instruction with given arguments - Pattern(solidity::Instruction _instruction, std::vector<Pattern> const& _arguments = {}); - /// Sets this pattern to be part of the match group with the identifier @a _group. - /// Inside one rule, all patterns in the same match group have to match expressions from the - /// same expression equivalence class. - void setMatchGroup(unsigned _group, std::map<unsigned, Expression const*>& _matchGroups); - unsigned matchGroup() const { return m_matchGroup; } - bool matches(Expression const& _expr) const; - - std::vector<Pattern> arguments() const { return m_arguments; } - - /// @returns the data of the matched expression if this pattern is part of a match group. - u256 d() const; - - solidity::Instruction instruction() const; - - /// Turns this pattern into an actual expression. Should only be called - /// for patterns resulting from an action, i.e. with match groups assigned. - Expression toExpression(SourceLocation const& _location) const; - -private: - Expression const& matchGroupValue() const; - - PatternKind m_kind = PatternKind::Any; - solidity::Instruction m_instruction; ///< Only valid if m_kind is Operation - std::shared_ptr<u256> m_data; ///< Only valid if m_kind is Constant - std::vector<Pattern> m_arguments; - unsigned m_matchGroup = 0; - std::map<unsigned, Expression const*>* m_matchGroups = nullptr; -}; - -} -} diff --git a/libjulia/optimiser/Substitution.cpp b/libjulia/optimiser/Substitution.cpp deleted file mode 100644 index 668b6cb6..00000000 --- a/libjulia/optimiser/Substitution.cpp +++ /dev/null @@ -1,39 +0,0 @@ -/* - This file is part of solidity. - - solidity is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - solidity is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with solidity. If not, see <http://www.gnu.org/licenses/>. -*/ -/** - * Specific AST copier that replaces certain identifiers with expressions. - */ - -#include <libjulia/optimiser/Substitution.h> - -#include <libsolidity/inlineasm/AsmData.h> - -using namespace std; -using namespace dev; -using namespace dev::julia; - -Expression Substitution::translate(Expression const& _expression) -{ - if (_expression.type() == typeid(Identifier)) - { - string const& name = boost::get<Identifier>(_expression).name; - if (m_substitutions.count(name)) - // No recursive substitution - return ASTCopier().translate(*m_substitutions.at(name)); - } - return ASTCopier::translate(_expression); -} diff --git a/libjulia/optimiser/Substitution.h b/libjulia/optimiser/Substitution.h deleted file mode 100644 index 313a08d7..00000000 --- a/libjulia/optimiser/Substitution.h +++ /dev/null @@ -1,50 +0,0 @@ -/* - This file is part of solidity. - - solidity is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - solidity is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with solidity. If not, see <http://www.gnu.org/licenses/>. -*/ -/** - * Specific AST copier that replaces certain identifiers with expressions. - */ - -#pragma once - -#include <libjulia/optimiser/ASTCopier.h> - -#include <string> -#include <map> -#include <set> - -namespace dev -{ -namespace julia -{ - -/** - * Specific AST copier that replaces certain identifiers with expressions. - */ -class Substitution: public ASTCopier -{ -public: - Substitution(std::map<std::string, Expression const*> const& _substitutions): - m_substitutions(_substitutions) - {} - virtual Expression translate(Expression const& _expression) override; - -private: - std::map<std::string, Expression const*> const& m_substitutions; -}; - -} -} diff --git a/libjulia/optimiser/SyntacticalEquality.cpp b/libjulia/optimiser/SyntacticalEquality.cpp deleted file mode 100644 index c497336d..00000000 --- a/libjulia/optimiser/SyntacticalEquality.cpp +++ /dev/null @@ -1,76 +0,0 @@ -/*( - This file is part of solidity. - - solidity is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - solidity is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with solidity. If not, see <http://www.gnu.org/licenses/>. -*/ -/** - * Component that can compare ASTs for equality on a syntactic basis. - */ - -#include <libjulia/optimiser/SyntacticalEquality.h> - -#include <libjulia/Exceptions.h> - -#include <libsolidity/inlineasm/AsmData.h> - -#include <libdevcore/CommonData.h> - -using namespace std; -using namespace dev; -using namespace dev::julia; - -bool SyntacticalEqualityChecker::equal(Expression const& _e1, Expression const& _e2) -{ - if (_e1.type() != _e2.type()) - return false; - - // TODO This should be replaced by some kind of AST walker as soon as it gets - // more complex. - if (_e1.type() == typeid(FunctionalInstruction)) - { - auto const& e1 = boost::get<FunctionalInstruction>(_e1); - auto const& e2 = boost::get<FunctionalInstruction>(_e2); - return - e1.instruction == e2.instruction && - equalVector(e1.arguments, e2.arguments); - } - else if (_e1.type() == typeid(FunctionCall)) - { - auto const& e1 = boost::get<FunctionCall>(_e1); - auto const& e2 = boost::get<FunctionCall>(_e2); - return - equal(e1.functionName, e2.functionName) && - equalVector(e1.arguments, e2.arguments); - } - else if (_e1.type() == typeid(Identifier)) - return boost::get<Identifier>(_e1).name == boost::get<Identifier>(_e2).name; - else if (_e1.type() == typeid(Literal)) - { - auto const& e1 = boost::get<Literal>(_e1); - auto const& e2 = boost::get<Literal>(_e2); - return e1.kind == e2.kind && e1.value == e2.value && e1.type == e2.type; - } - else - { - assertThrow(false, OptimizerException, "Invalid expression"); - } - return false; -} - -bool SyntacticalEqualityChecker::equalVector(vector<Expression> const& _e1, vector<Expression> const& _e2) -{ - return _e1.size() == _e2.size() && - std::equal(begin(_e1), end(_e1), begin(_e2), SyntacticalEqualityChecker::equal); - -} diff --git a/libjulia/optimiser/SyntacticalEquality.h b/libjulia/optimiser/SyntacticalEquality.h deleted file mode 100644 index b7c09330..00000000 --- a/libjulia/optimiser/SyntacticalEquality.h +++ /dev/null @@ -1,50 +0,0 @@ -/* - This file is part of solidity. - - solidity is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - solidity is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with solidity. If not, see <http://www.gnu.org/licenses/>. -*/ -/** - * Component that can compare ASTs for equality on a syntactic basis. - */ - -#pragma once - -#include <libjulia/ASTDataForward.h> - -#include <vector> - -namespace dev -{ -namespace julia -{ - -/** - * Component that can compare ASTs for equality on a syntactic basis. - * Ignores source locations but requires exact matches otherwise. - * - * TODO: Only implemented for Expressions for now. - * A future version might also recognize renamed variables and thus could be used to - * remove duplicate functions. - */ -class SyntacticalEqualityChecker -{ -public: - static bool equal(Expression const& _e1, Expression const& _e2); - -protected: - static bool equalVector(std::vector<Expression> const& _e1, std::vector<Expression> const& _e2); -}; - -} -} diff --git a/libjulia/optimiser/UnusedPruner.cpp b/libjulia/optimiser/UnusedPruner.cpp deleted file mode 100644 index 54e8fd6e..00000000 --- a/libjulia/optimiser/UnusedPruner.cpp +++ /dev/null @@ -1,117 +0,0 @@ -/*( - This file is part of solidity. - - solidity is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - solidity is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with solidity. If not, see <http://www.gnu.org/licenses/>. -*/ -/** - * Optimisation stage that removes unused variables and functions. - */ - -#include <libjulia/optimiser/UnusedPruner.h> - -#include <libjulia/optimiser/NameCollector.h> -#include <libjulia/optimiser/Semantics.h> -#include <libjulia/optimiser/Utilities.h> -#include <libjulia/Exceptions.h> - -#include <libsolidity/inlineasm/AsmData.h> - -#include <boost/algorithm/cxx11/none_of.hpp> - -using namespace std; -using namespace dev; -using namespace dev::julia; - -UnusedPruner::UnusedPruner(Block& _ast) -{ - ReferencesCounter counter; - counter(_ast); - - m_references = counter.references(); -} - -void UnusedPruner::operator()(Block& _block) -{ - for (auto&& statement: _block.statements) - if (statement.type() == typeid(FunctionDefinition)) - { - FunctionDefinition& funDef = boost::get<FunctionDefinition>(statement); - if (!used(funDef.name)) - { - subtractReferences(ReferencesCounter::countReferences(funDef.body)); - statement = Block{std::move(funDef.location), {}}; - } - } - else if (statement.type() == typeid(VariableDeclaration)) - { - VariableDeclaration& varDecl = boost::get<VariableDeclaration>(statement); - // Multi-variable declarations are special. We can only remove it - // if all vairables are unused and the right-hand-side is either - // movable or it return a single value. In the latter case, we - // replace `let a := f()` by `pop(f())` (in pure IULIA, this will be - // `drop(f())`). - if (boost::algorithm::none_of( - varDecl.variables, - [=](TypedName const& _typedName) { return used(_typedName.name); } - )) - { - if (!varDecl.value) - statement = Block{std::move(varDecl.location), {}}; - else if (MovableChecker(*varDecl.value).movable()) - { - subtractReferences(ReferencesCounter::countReferences(*varDecl.value)); - statement = Block{std::move(varDecl.location), {}}; - } - else if (varDecl.variables.size() == 1) - // In pure IULIA, this should be replaced by a function call to `drop` - // instead of `pop`. - statement = ExpressionStatement{varDecl.location, FunctionalInstruction{ - varDecl.location, - solidity::Instruction::POP, - {*std::move(varDecl.value)} - }}; - } - } - - removeEmptyBlocks(_block); - - ASTModifier::operator()(_block); -} - -void UnusedPruner::runUntilStabilised(Block& _ast) -{ - while (true) - { - UnusedPruner pruner(_ast); - pruner(_ast); - if (!pruner.shouldRunAgain()) - return; - } -} - -bool UnusedPruner::used(string const& _name) const -{ - return m_references.count(_name) && m_references.at(_name) > 0; -} - -void UnusedPruner::subtractReferences(map<string, size_t> const& _subtrahend) -{ - for (auto const& ref: _subtrahend) - { - assertThrow(m_references.count(ref.first), OptimizerException, ""); - assertThrow(m_references.at(ref.first) >= ref.second, OptimizerException, ""); - m_references[ref.first] -= ref.second; - m_shouldRunAgain = true; - } -} diff --git a/libjulia/optimiser/UnusedPruner.h b/libjulia/optimiser/UnusedPruner.h deleted file mode 100644 index 73e8de7c..00000000 --- a/libjulia/optimiser/UnusedPruner.h +++ /dev/null @@ -1,67 +0,0 @@ -/* - This file is part of solidity. - - solidity is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - solidity is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with solidity. If not, see <http://www.gnu.org/licenses/>. -*/ -/** - * Optimisation stage that removes unused variables and functions. - */ - -#pragma once - -#include <libjulia/optimiser/ASTWalker.h> - -#include <string> -#include <map> -#include <set> - -namespace dev -{ -namespace julia -{ - -/** - * Optimisation stage that removes unused variables and functions. - * - * TODO: Also remove intermediate variable assignments from movable expressions - * which are not referenced until after the next assignment to the same variable. - * - * Note that this does not remove circular references. - * - * Prerequisite: Disambiguator - */ -class UnusedPruner: public ASTModifier -{ -public: - explicit UnusedPruner(Block& _ast); - - using ASTModifier::operator(); - virtual void operator()(Block& _block) override; - - // @returns true iff the code changed in the previous run. - bool shouldRunAgain() const { return m_shouldRunAgain; } - - // Run the pruner until the code does not change anymore. - static void runUntilStabilised(Block& _ast); - -private: - bool used(std::string const& _name) const; - void subtractReferences(std::map<std::string, size_t> const& _subtrahend); - - bool m_shouldRunAgain = false; - std::map<std::string, size_t> m_references; -}; - -} -} diff --git a/libjulia/optimiser/Utilities.cpp b/libjulia/optimiser/Utilities.cpp deleted file mode 100644 index ff108b89..00000000 --- a/libjulia/optimiser/Utilities.cpp +++ /dev/null @@ -1,39 +0,0 @@ -/*( - This file is part of solidity. - - solidity is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - solidity is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with solidity. If not, see <http://www.gnu.org/licenses/>. -*/ -/** - * Some useful snippets for the optimiser. - */ - -#include <libjulia/optimiser/Utilities.h> - -#include <libsolidity/inlineasm/AsmData.h> - -#include <libdevcore/CommonData.h> - -#include <boost/range/algorithm_ext/erase.hpp> - -using namespace std; -using namespace dev; -using namespace dev::julia; - -void dev::julia::removeEmptyBlocks(Block& _block) -{ - auto isEmptyBlock = [](Statement const& _st) -> bool { - return _st.type() == typeid(Block) && boost::get<Block>(_st).statements.empty(); - }; - boost::range::remove_erase_if(_block.statements, isEmptyBlock); -} diff --git a/libjulia/optimiser/Utilities.h b/libjulia/optimiser/Utilities.h deleted file mode 100644 index 88ba3f47..00000000 --- a/libjulia/optimiser/Utilities.h +++ /dev/null @@ -1,34 +0,0 @@ -/* - This file is part of solidity. - - solidity is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - solidity is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with solidity. If not, see <http://www.gnu.org/licenses/>. -*/ -/** - * Small useful snippets for the optimiser. - */ - -#pragma once - -#include <libjulia/ASTDataForward.h> - -namespace dev -{ -namespace julia -{ - -/// Removes statements that are just empty blocks (non-recursive). -void removeEmptyBlocks(Block& _block); - -} -} diff --git a/liblll/CodeFragment.cpp b/liblll/CodeFragment.cpp index 5c68194b..f37cb8b9 100644 --- a/liblll/CodeFragment.cpp +++ b/liblll/CodeFragment.cpp @@ -22,22 +22,33 @@ #include "CodeFragment.h" #include <boost/algorithm/string.hpp> -#pragma warning(push) + +#if defined(__GNUC__) #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wunused-parameter" +#endif // defined(__GNUC__) + #include <boost/spirit/include/support_utree.hpp> -#pragma warning(pop) + +#if defined(__GNUC__) #pragma GCC diagnostic pop +#endif // defined(__GNUC__) + #include <libdevcore/CommonIO.h> #include <libevmasm/Instruction.h> #include "CompilerState.h" #include "Parser.h" + using namespace std; using namespace dev; -using namespace dev::eth; +using namespace dev::lll; void CodeFragment::finalise(CompilerState const& _cs) { + // NOTE: add this as a safeguard in case the user didn't issue an + // explicit stop at the end of the sequence + m_asm.append(Instruction::STOP); + if (_cs.usedAlloc && _cs.vars.size() && !m_finalised) { m_finalised = true; @@ -225,7 +236,12 @@ void CodeFragment::constructOperation(sp::utree const& _t, CompilerState& _s) int c = 0; for (auto const& i: _t) if (c++) - m_asm.append(CodeFragment(i, _s, m_readFile, true).m_asm); + { + auto fragment = CodeFragment(i, _s, m_readFile, true).m_asm; + if ((m_asm.deposit() + fragment.deposit()) < 0) + error<IncorrectParameterCount>("The assembly instruction resulted in stack underflow"); + m_asm.append(fragment); + } } else if (us == "INCLUDE") { @@ -341,7 +357,7 @@ void CodeFragment::constructOperation(sp::utree const& _t, CompilerState& _s) else if (i.which() == sp::utree_type::string_type) { auto sr = i.get<sp::basic_string<boost::iterator_range<char const*>, sp::utree_type::string_type>>(); - data.insert(data.end(), (byte const *)sr.begin(), (byte const*)sr.end()); + data.insert(data.end(), (uint8_t const *)sr.begin(), (uint8_t const*)sr.end()); } else if (i.which() == sp::utree_type::any_type) { @@ -552,7 +568,7 @@ void CodeFragment::constructOperation(sp::utree const& _t, CompilerState& _s) int targetDeposit = hasDefault ? code[code.size() - 1].m_asm.deposit() : 0; // The conditions - AssemblyItems jumpTags; + eth::AssemblyItems jumpTags; for (unsigned i = 0; i < code.size() - 1; i += 2) { requireDeposit(i, 1); @@ -619,7 +635,7 @@ void CodeFragment::constructOperation(sp::utree const& _t, CompilerState& _s) requireMaxSize(3); requireDeposit(1, 1); - auto subPush = m_asm.appendSubroutine(make_shared<Assembly>(code[0].assembly(ns))); + auto subPush = m_asm.appendSubroutine(make_shared<eth::Assembly>(code[0].assembly(ns))); m_asm.append(Instruction::DUP1); if (code.size() == 3) { diff --git a/liblll/CodeFragment.h b/liblll/CodeFragment.h index e5cac34e..e6e4d3b6 100644 --- a/liblll/CodeFragment.h +++ b/liblll/CodeFragment.h @@ -31,7 +31,7 @@ namespace sp = boost::spirit; namespace dev { -namespace eth +namespace lll { struct CompilerState; @@ -47,7 +47,7 @@ public: static CodeFragment compile(std::string const& _src, CompilerState& _s, ReadCallback const& _readFile); /// Consolidates data and compiles code. - Assembly& assembly(CompilerState const& _cs) { finalise(_cs); return m_asm; } + eth::Assembly& assembly(CompilerState const& _cs) { finalise(_cs); return m_asm; } private: void finalise(CompilerState const& _cs); @@ -61,7 +61,7 @@ private: void constructOperation(sp::utree const& _t, CompilerState& _s); bool m_finalised = false; - Assembly m_asm; + eth::Assembly m_asm; ReadCallback m_readFile; }; diff --git a/liblll/Compiler.cpp b/liblll/Compiler.cpp index f2c1b0be..f944adbd 100644 --- a/liblll/Compiler.cpp +++ b/liblll/Compiler.cpp @@ -26,9 +26,9 @@ using namespace std; using namespace dev; -using namespace dev::eth; +using namespace dev::lll; -bytes dev::eth::compileLLL(string const& _src, dev::solidity::EVMVersion _evmVersion, bool _opt, std::vector<std::string>* _errors, dev::eth::ReadCallback const& _readFile) +bytes dev::lll::compileLLL(string const& _src, dev::solidity::EVMVersion _evmVersion, bool _opt, std::vector<std::string>* _errors, ReadCallback const& _readFile) { try { @@ -66,7 +66,7 @@ bytes dev::eth::compileLLL(string const& _src, dev::solidity::EVMVersion _evmVer return bytes(); } -std::string dev::eth::compileLLLToAsm(std::string const& _src, EVMVersion _evmVersion, bool _opt, std::vector<std::string>* _errors, ReadCallback const& _readFile) +std::string dev::lll::compileLLLToAsm(std::string const& _src, EVMVersion _evmVersion, bool _opt, std::vector<std::string>* _errors, ReadCallback const& _readFile) { try { @@ -90,7 +90,8 @@ std::string dev::eth::compileLLLToAsm(std::string const& _src, EVMVersion _evmVe } catch (std::exception const& _e) { - if (_errors) { + if (_errors) + { _errors->push_back("Parse exception."); _errors->push_back(boost::diagnostic_information(_e)); } @@ -103,7 +104,7 @@ std::string dev::eth::compileLLLToAsm(std::string const& _src, EVMVersion _evmVe return string(); } -string dev::eth::parseLLL(string const& _src) +string dev::lll::parseLLL(string const& _src) { sp::utree o; diff --git a/liblll/Compiler.h b/liblll/Compiler.h index 06440c17..1ff7d5f8 100644 --- a/liblll/Compiler.h +++ b/liblll/Compiler.h @@ -30,7 +30,7 @@ namespace dev { -namespace eth +namespace lll { using ReadCallback = std::function<std::string(std::string const&)>; diff --git a/liblll/CompilerState.cpp b/liblll/CompilerState.cpp index c0e344b2..019582d4 100644 --- a/liblll/CompilerState.cpp +++ b/liblll/CompilerState.cpp @@ -24,7 +24,7 @@ using namespace std; using namespace dev; -using namespace dev::eth; +using namespace dev::lll; CompilerState::CompilerState() { diff --git a/liblll/CompilerState.h b/liblll/CompilerState.h index 96a0246d..0fa50288 100644 --- a/liblll/CompilerState.h +++ b/liblll/CompilerState.h @@ -26,7 +26,7 @@ namespace dev { -namespace eth +namespace lll { struct Macro diff --git a/liblll/Exceptions.h b/liblll/Exceptions.h index e8ca99db..7bd951e3 100644 --- a/liblll/Exceptions.h +++ b/liblll/Exceptions.h @@ -25,7 +25,7 @@ namespace dev { -namespace eth +namespace lll { /// Compile a Low-level Lisp-like Language program into EVM-code. diff --git a/liblll/Parser.cpp b/liblll/Parser.cpp index a3962df4..854aeecc 100644 --- a/liblll/Parser.cpp +++ b/liblll/Parser.cpp @@ -33,12 +33,12 @@ using namespace std; using namespace dev; -using namespace dev::eth; +using namespace dev::lll; namespace qi = boost::spirit::qi; namespace px = boost::phoenix; namespace sp = boost::spirit; -void dev::eth::killBigints(sp::utree const& _this) +void dev::lll::killBigints(sp::utree const& _this) { switch (_this.which()) { @@ -48,7 +48,7 @@ void dev::eth::killBigints(sp::utree const& _this) } } -void dev::eth::debugOutAST(ostream& _out, sp::utree const& _this) +void dev::lll::debugOutAST(ostream& _out, sp::utree const& _this) { switch (_this.which()) { @@ -74,7 +74,8 @@ void dev::eth::debugOutAST(ostream& _out, sp::utree const& _this) } } -namespace dev { namespace eth { +namespace dev { +namespace lll { namespace parseTreeLLL_ { template<unsigned N> @@ -88,11 +89,11 @@ struct tagNode }}} -void dev::eth::parseTreeLLL(string const& _s, sp::utree& o_out) +void dev::lll::parseTreeLLL(string const& _s, sp::utree& o_out) { using qi::standard::space; using qi::standard::space_type; - using dev::eth::parseTreeLLL_::tagNode; + using dev::lll::parseTreeLLL_::tagNode; using symbol_type = sp::basic_string<std::string, sp::utree_type::symbol_type>; using it = string::const_iterator; @@ -142,12 +143,13 @@ void dev::eth::parseTreeLLL(string const& _s, sp::utree& o_out) catch (qi::expectation_failure<it> const& e) { std::string fragment(e.first, e.last); - std::string loc = std::to_string(std::distance(s.cbegin(), e.first) - 1); + std::string loc = to_string(std::distance(s.cbegin(), e.first) - 1); std::string reason("Lexer failure at " + loc + ": '" + fragment + "'"); BOOST_THROW_EXCEPTION(ParserException() << errinfo_comment(reason)); } for (auto i = ret; i != s.cend(); ++i) - if (!isspace(*i)) { + if (!isspace(*i)) + { BOOST_THROW_EXCEPTION(ParserException() << errinfo_comment("Non-whitespace left in parser")); } } diff --git a/liblll/Parser.h b/liblll/Parser.h index 694d0cc6..cfbec418 100644 --- a/liblll/Parser.h +++ b/liblll/Parser.h @@ -31,7 +31,7 @@ namespace sp = boost::spirit; namespace dev { -namespace eth +namespace lll { void killBigints(sp::utree const& _this); diff --git a/libsolc/CMakeLists.txt b/libsolc/CMakeLists.txt index e67583dd..ed14e9fe 100644 --- a/libsolc/CMakeLists.txt +++ b/libsolc/CMakeLists.txt @@ -1,5 +1,5 @@ if (EMSCRIPTEN) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -s EXPORTED_FUNCTIONS='[\"_compileJSON\",\"_license\",\"_version\",\"_compileJSONMulti\",\"_compileJSONCallback\",\"_compileStandard\"]' -s RESERVED_FUNCTION_POINTERS=20") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -s EXPORTED_FUNCTIONS='[\"_solidity_license\",\"_solidity_version\",\"_solidity_compile\"]' -s RESERVED_FUNCTION_POINTERS=20") add_executable(soljson libsolc.cpp) target_link_libraries(soljson PRIVATE solidity) else() diff --git a/libsolc/libsolc.cpp b/libsolc/libsolc.cpp index 6c587e23..766e1c0c 100644 --- a/libsolc/libsolc.cpp +++ b/libsolc/libsolc.cpp @@ -17,7 +17,7 @@ /** * @author Christian <c@ethdev.com> * @date 2014 - * JSON interface for the solidity compiler to be used from Javascript. + * Public compiler API. */ #include <libsolc/libsolc.h> @@ -72,193 +72,7 @@ ReadCallback::Callback wrapReadCallback(CStyleReadFileCallback _readCallback = n return readCallback; } -/// Translates a gas value as a string to a JSON number or null -Json::Value gasToJson(Json::Value const& _value) -{ - if (_value.isObject()) - { - Json::Value ret = Json::objectValue; - for (auto const& sig: _value.getMemberNames()) - ret[sig] = gasToJson(_value[sig]); - return ret; - } - - if (_value == "infinite") - return Json::Value(Json::nullValue); - - u256 value(_value.asString()); - if (value > std::numeric_limits<Json::LargestUInt>::max()) - return Json::Value(Json::nullValue); - else - return Json::Value(Json::LargestUInt(value)); -} - -Json::Value translateGasEstimates(Json::Value const& estimates) -{ - Json::Value output(Json::objectValue); - - if (estimates["creation"].isObject()) - { - Json::Value creation(Json::arrayValue); - creation[0] = gasToJson(estimates["creation"]["executionCost"]); - creation[1] = gasToJson(estimates["creation"]["codeDepositCost"]); - output["creation"] = creation; - } - else - output["creation"] = Json::objectValue; - output["external"] = gasToJson(estimates.get("external", Json::objectValue)); - output["internal"] = gasToJson(estimates.get("internal", Json::objectValue)); - - return output; -} - -string compile(StringMap const& _sources, bool _optimize, CStyleReadFileCallback _readCallback) -{ - /// create new JSON input format - Json::Value input = Json::objectValue; - input["language"] = "Solidity"; - input["sources"] = Json::objectValue; - for (auto const& source: _sources) - { - input["sources"][source.first] = Json::objectValue; - input["sources"][source.first]["content"] = source.second; - } - input["settings"] = Json::objectValue; - input["settings"]["optimizer"] = Json::objectValue; - input["settings"]["optimizer"]["enabled"] = _optimize; - input["settings"]["optimizer"]["runs"] = 200; - - // Enable all SourceUnit-level outputs. - input["settings"]["outputSelection"]["*"][""][0] = "*"; - // Enable all Contract-level outputs. - input["settings"]["outputSelection"]["*"]["*"][0] = "*"; - - StandardCompiler compiler(wrapReadCallback(_readCallback)); - Json::Value ret = compiler.compile(input); - - /// transform JSON to match the old format - // { - // "errors": [ "Error 1", "Error 2" ], - // "sourceList": [ "sourcename1", "sourcename2" ], - // "sources": { - // "sourcename1": { - // "AST": {} - // } - // }, - // "contracts": { - // "Contract1": { - // "interface": "[...abi...]", - // "bytecode": "ff0011...", - // "runtimeBytecode": "ff0011", - // "opcodes": "PUSH 1 POP STOP", - // "metadata": "{...metadata...}", - // "functionHashes": { - // "test(uint256)": "11ff2233" - // }, - // "gasEstimates": { - // "creation": [ 224, 42000 ], - // "external": { - // "11ff2233": null, - // "3322ff11": 1234 - // }, - // "internal": { - // } - // }, - // "srcmap" = "0:1:2", - // "srcmapRuntime" = "0:1:2", - // "assembly" = {} - // } - // } - // } - Json::Value output = Json::objectValue; - - if (ret.isMember("errors")) - { - output["errors"] = Json::arrayValue; - for (auto const& error: ret["errors"]) - output["errors"].append( - !error["formattedMessage"].empty() ? error["formattedMessage"] : error["message"] - ); - } - - output["sourceList"] = Json::arrayValue; - for (auto const& source: _sources) - output["sourceList"].append(source.first); - - if (ret.isMember("sources")) - { - output["sources"] = Json::objectValue; - for (auto const& sourceName: ret["sources"].getMemberNames()) - { - output["sources"][sourceName] = Json::objectValue; - output["sources"][sourceName]["AST"] = ret["sources"][sourceName]["legacyAST"]; - } - } - - if (ret.isMember("contracts")) - { - output["contracts"] = Json::objectValue; - for (auto const& sourceName: ret["contracts"].getMemberNames()) - for (auto const& contractName: ret["contracts"][sourceName].getMemberNames()) - { - Json::Value contractInput = ret["contracts"][sourceName][contractName]; - Json::Value contractOutput = Json::objectValue; - contractOutput["interface"] = jsonCompactPrint(contractInput["abi"]); - contractOutput["metadata"] = contractInput["metadata"]; - contractOutput["functionHashes"] = contractInput["evm"]["methodIdentifiers"]; - contractOutput["gasEstimates"] = translateGasEstimates(contractInput["evm"]["gasEstimates"]); - contractOutput["assembly"] = contractInput["evm"]["legacyAssembly"]; - contractOutput["bytecode"] = contractInput["evm"]["bytecode"]["object"]; - contractOutput["opcodes"] = contractInput["evm"]["bytecode"]["opcodes"]; - contractOutput["srcmap"] = contractInput["evm"]["bytecode"]["sourceMap"]; - contractOutput["runtimeBytecode"] = contractInput["evm"]["deployedBytecode"]["object"]; - contractOutput["srcmapRuntime"] = contractInput["evm"]["deployedBytecode"]["sourceMap"]; - output["contracts"][sourceName + ":" + contractName] = contractOutput; - } - } - - try - { - return jsonCompactPrint(output); - } - catch (...) - { - return "{\"errors\":[\"Unknown error while generating JSON.\"]}"; - } -} - -string compileMulti(string const& _input, bool _optimize, CStyleReadFileCallback _readCallback = nullptr) -{ - string errors; - Json::Value input; - if (!jsonParseStrict(_input, input, &errors)) - { - Json::Value jsonErrors(Json::arrayValue); - jsonErrors.append("Error parsing input JSON: " + errors); - Json::Value output(Json::objectValue); - output["errors"] = jsonErrors; - return jsonCompactPrint(output); - } - else - { - StringMap sources; - Json::Value jsonSources = input["sources"]; - if (jsonSources.isObject()) - for (auto const& sourceName: jsonSources.getMemberNames()) - sources[sourceName] = jsonSources[sourceName].asString(); - return compile(sources, _optimize, _readCallback); - } -} - -string compileSingle(string const& _input, bool _optimize) -{ - StringMap sources; - sources[""] = _input; - return compile(sources, _optimize, nullptr); -} - - -string compileStandardInternal(string const& _input, CStyleReadFileCallback _readCallback = nullptr) +string compile(string const& _input, CStyleReadFileCallback _readCallback = nullptr) { StandardCompiler compiler(wrapReadCallback(_readCallback)); return compiler.compile(_input); @@ -270,33 +84,18 @@ static string s_outputBuffer; extern "C" { -extern char const* license() +extern char const* solidity_license() noexcept { static string fullLicenseText = otherLicenses + licenseText; return fullLicenseText.c_str(); } -extern char const* version() +extern char const* solidity_version() noexcept { return VersionString.c_str(); } -extern char const* compileJSON(char const* _input, bool _optimize) -{ - s_outputBuffer = compileSingle(_input, _optimize); - return s_outputBuffer.c_str(); -} -extern char const* compileJSONMulti(char const* _input, bool _optimize) -{ - s_outputBuffer = compileMulti(_input, _optimize); - return s_outputBuffer.c_str(); -} -extern char const* compileJSONCallback(char const* _input, bool _optimize, CStyleReadFileCallback _readCallback) -{ - s_outputBuffer = compileMulti(_input, _optimize, _readCallback); - return s_outputBuffer.c_str(); -} -extern char const* compileStandard(char const* _input, CStyleReadFileCallback _readCallback) +extern char const* solidity_compile(char const* _input, CStyleReadFileCallback _readCallback) noexcept { - s_outputBuffer = compileStandardInternal(_input, _readCallback); + s_outputBuffer = compile(_input, _readCallback); return s_outputBuffer.c_str(); } } diff --git a/libsolc/libsolc.h b/libsolc/libsolc.h index c392ce93..4b0ec639 100644 --- a/libsolc/libsolc.h +++ b/libsolc/libsolc.h @@ -17,12 +17,18 @@ /** * @author Christian <c@ethdev.com> * @date 2014 - * JSON interface for the solidity compiler to be used from Javascript. + * Public compiler API. */ #include <stdbool.h> #ifdef __cplusplus +#define SOLC_NOEXCEPT noexcept +#else +#define SOLC_NOEXCEPT +#endif + +#ifdef __cplusplus extern "C" { #endif @@ -30,12 +36,9 @@ extern "C" { /// heap-allocated and are free'd by the caller. typedef void (*CStyleReadFileCallback)(char const* _path, char** o_contents, char** o_error); -char const* license(); -char const* version(); -char const* compileJSON(char const* _input, bool _optimize); -char const* compileJSONMulti(char const* _input, bool _optimize); -char const* compileJSONCallback(char const* _input, bool _optimize, CStyleReadFileCallback _readCallback); -char const* compileStandard(char const* _input, CStyleReadFileCallback _readCallback); +char const* solidity_license() SOLC_NOEXCEPT; +char const* solidity_version() SOLC_NOEXCEPT; +char const* solidity_compile(char const* _input, CStyleReadFileCallback _readCallback) SOLC_NOEXCEPT; #ifdef __cplusplus } diff --git a/libsolidity/CMakeLists.txt b/libsolidity/CMakeLists.txt index 0bdec4b4..136d39b1 100644 --- a/libsolidity/CMakeLists.txt +++ b/libsolidity/CMakeLists.txt @@ -1,30 +1,28 @@ -# Until we have a clear separation, libjulia has to be included here -file(GLOB_RECURSE sources "*.cpp" "../libjulia/*.cpp") -file(GLOB_RECURSE headers "*.h" "../libjulia/*.h") +# Until we have a clear separation, libyul has to be included here +file(GLOB_RECURSE sources "*.cpp" "../libyul/*.cpp") +file(GLOB_RECURSE headers "*.h" "../libyul/*.h") find_package(Z3 QUIET) if (${Z3_FOUND}) include_directories(${Z3_INCLUDE_DIR}) add_definitions(-DHAVE_Z3) message("Z3 SMT solver found. This enables optional SMT checking with Z3.") - list(REMOVE_ITEM sources "${CMAKE_CURRENT_SOURCE_DIR}/formal/CVC4Interface.cpp") else() list(REMOVE_ITEM sources "${CMAKE_CURRENT_SOURCE_DIR}/formal/Z3Interface.cpp") - find_package(GMP QUIET) - find_package(CVC4 QUIET) - if (${CVC4_FOUND}) - if (${GMP_FOUND}) - include_directories(${CVC4_INCLUDE_DIR}) - add_definitions(-DHAVE_CVC4) - message("CVC4 SMT solver and GMP found. This enables optional SMT checking with CVC4.") - else() - message("CVC4 SMT solver found but its dependency GMP was NOT found. Optional SMT checking with CVC4 will not be available. Please install GMP if it is desired.") - list(REMOVE_ITEM sources "${CMAKE_CURRENT_SOURCE_DIR}/formal/CVC4Interface.cpp") - endif() - else() - message("No SMT solver found (Z3 or CVC4). Optional SMT checking will not be available. Please install Z3 or CVC4 if it is desired.") - list(REMOVE_ITEM sources "${CMAKE_CURRENT_SOURCE_DIR}/formal/CVC4Interface.cpp") - endif() +endif() + +find_package(CVC4 QUIET) +if (${CVC4_FOUND}) + include_directories(${CVC4_INCLUDE_DIR}) + add_definitions(-DHAVE_CVC4) + message("CVC4 SMT solver found. This enables optional SMT checking with CVC4.") +else() + list(REMOVE_ITEM sources "${CMAKE_CURRENT_SOURCE_DIR}/formal/CVC4Interface.cpp") +endif() + +if (NOT (${Z3_FOUND} OR ${CVC4_FOUND})) + message("No SMT solver found (or it has been forcefully disabled). Optional SMT checking will not be available.\ + \nPlease install Z3 or CVC4 or remove the option disabling them (USE_Z3, USE_CVC4).") endif() add_library(solidity ${sources} ${headers}) @@ -34,7 +32,6 @@ if (${Z3_FOUND}) target_link_libraries(solidity PUBLIC ${Z3_LIBRARY}) endif() -if (${CVC4_FOUND} AND ${GMP_FOUND}) - target_link_libraries(solidity PUBLIC ${CVC4_LIBRARY}) - target_link_libraries(solidity PUBLIC ${GMP_LIBRARY}) +if (${CVC4_FOUND}) + target_link_libraries(solidity PUBLIC ${CVC4_LIBRARIES}) endif() diff --git a/libsolidity/analysis/ConstantEvaluator.cpp b/libsolidity/analysis/ConstantEvaluator.cpp index 8659bbfd..f9b00927 100644 --- a/libsolidity/analysis/ConstantEvaluator.cpp +++ b/libsolidity/analysis/ConstantEvaluator.cpp @@ -46,7 +46,7 @@ void ConstantEvaluator::endVisit(BinaryOperation const& _operation) m_errorReporter.fatalTypeError( _operation.location(), "Operator " + - string(Token::toString(_operation.getOperator())) + + string(TokenTraits::toString(_operation.getOperator())) + " not compatible with types " + left->toString() + " and " + @@ -54,7 +54,7 @@ void ConstantEvaluator::endVisit(BinaryOperation const& _operation) ); setType( _operation, - Token::isCompareOp(_operation.getOperator()) ? + TokenTraits::isCompareOp(_operation.getOperator()) ? make_shared<BoolType>() : commonType ); diff --git a/libsolidity/analysis/ControlFlowAnalyzer.cpp b/libsolidity/analysis/ControlFlowAnalyzer.cpp index 6edf7986..8a608552 100644 --- a/libsolidity/analysis/ControlFlowAnalyzer.cpp +++ b/libsolidity/analysis/ControlFlowAnalyzer.cpp @@ -28,8 +28,11 @@ bool ControlFlowAnalyzer::analyze(ASTNode const& _astRoot) bool ControlFlowAnalyzer::visit(FunctionDefinition const& _function) { - auto const& functionFlow = m_cfg.functionFlow(_function); - checkUnassignedStorageReturnValues(_function, functionFlow.entry, functionFlow.exit); + if (_function.isImplemented()) + { + auto const& functionFlow = m_cfg.functionFlow(_function); + checkUnassignedStorageReturnValues(_function, functionFlow.entry, functionFlow.exit); + } return false; } @@ -75,7 +78,10 @@ void ControlFlowAnalyzer::checkUnassignedStorageReturnValues( { auto& unassignedAtFunctionEntry = unassigned[_functionEntry]; for (auto const& returnParameter: _function.returnParameterList()->parameters()) - if (returnParameter->type()->dataStoredIn(DataLocation::Storage)) + if ( + returnParameter->type()->dataStoredIn(DataLocation::Storage) || + returnParameter->type()->category() == Type::Category::Mapping + ) unassignedAtFunctionEntry.insert(returnParameter.get()); } @@ -144,12 +150,12 @@ void ControlFlowAnalyzer::checkUnassignedStorageReturnValues( ssl.append("Problematic end of function:", _function.location()); } - m_errorReporter.warning( + m_errorReporter.typeError( returnVal->location(), - "This variable is of storage pointer type and might be returned without assignment. " - "This can cause storage corruption. Assign the variable (potentially from itself) " - "to remove this warning.", - ssl + ssl, + "This variable is of storage pointer type and might be returned without assignment and " + "could be used uninitialized. Assign the variable (potentially from itself) " + "to fix this error." ); } } diff --git a/libsolidity/analysis/ControlFlowBuilder.cpp b/libsolidity/analysis/ControlFlowBuilder.cpp index 35d7687c..5bd39da3 100644 --- a/libsolidity/analysis/ControlFlowBuilder.cpp +++ b/libsolidity/analysis/ControlFlowBuilder.cpp @@ -159,15 +159,14 @@ bool ControlFlowBuilder::visit(WhileStatement const& _whileStatement) { auto afterWhile = newLabel(); auto whileBody = createLabelHere(); + auto condition = newLabel(); { - // Note that "continue" in this case currently indeed jumps to whileBody - // and not to the condition. This is inconsistent with JavaScript and C and - // therefore a bug. This will be fixed in the future (planned for 0.5.0) - // and the Control Flow Graph will have to be adjusted accordingly. - BreakContinueScope scope(*this, afterWhile, whileBody); + BreakContinueScope scope(*this, afterWhile, condition); appendControlFlow(_whileStatement.body()); } + + placeAndConnectLabel(condition); appendControlFlow(_whileStatement.condition()); connect(m_currentNode, whileBody); diff --git a/libsolidity/analysis/DeclarationContainer.cpp b/libsolidity/analysis/DeclarationContainer.cpp index 786272e4..cf12a49d 100644 --- a/libsolidity/analysis/DeclarationContainer.cpp +++ b/libsolidity/analysis/DeclarationContainer.cpp @@ -49,16 +49,10 @@ Declaration const* DeclarationContainer::conflictingDeclaration( dynamic_cast<MagicVariableDeclaration const*>(&_declaration) ) { - // check that all other declarations with the same name are functions or a public state variable or events. - // And then check that the signatures are different. + // check that all other declarations are of the same kind (in which + // case the type checker will ensure that the signatures are different) for (Declaration const* declaration: declarations) { - if (auto variableDeclaration = dynamic_cast<VariableDeclaration const*>(declaration)) - { - if (variableDeclaration->isStateVariable() && !variableDeclaration->isConstant() && variableDeclaration->isPublic()) - continue; - return declaration; - } if ( dynamic_cast<FunctionDefinition const*>(&_declaration) && !dynamic_cast<FunctionDefinition const*>(declaration) @@ -96,6 +90,11 @@ void DeclarationContainer::activateVariable(ASTString const& _name) m_invisibleDeclarations.erase(_name); } +bool DeclarationContainer::isInvisible(ASTString const& _name) const +{ + return m_invisibleDeclarations.count(_name); +} + bool DeclarationContainer::registerDeclaration( Declaration const& _declaration, ASTString const* _name, @@ -138,20 +137,23 @@ vector<Declaration const*> DeclarationContainer::resolveName(ASTString const& _n vector<ASTString> DeclarationContainer::similarNames(ASTString const& _name) const { - static size_t const MAXIMUM_EDIT_DISTANCE = 2; - vector<ASTString> similar; + // because the function below has quadratic runtime - it will not magically improve once a better algorithm is discovered ;) + // since 80 is the suggested line length limit, we use 80^2 as length threshold + static size_t const MAXIMUM_LENGTH_THRESHOLD = 80 * 80; + vector<ASTString> similar; + size_t maximumEditDistance = _name.size() > 3 ? 2 : _name.size() / 2; for (auto const& declaration: m_declarations) { string const& declarationName = declaration.first; - if (stringWithinDistance(_name, declarationName, MAXIMUM_EDIT_DISTANCE)) + if (stringWithinDistance(_name, declarationName, maximumEditDistance, MAXIMUM_LENGTH_THRESHOLD)) similar.push_back(declarationName); } for (auto const& declaration: m_invisibleDeclarations) { string const& declarationName = declaration.first; - if (stringWithinDistance(_name, declarationName, MAXIMUM_EDIT_DISTANCE)) + if (stringWithinDistance(_name, declarationName, maximumEditDistance, MAXIMUM_LENGTH_THRESHOLD)) similar.push_back(declarationName); } diff --git a/libsolidity/analysis/DeclarationContainer.h b/libsolidity/analysis/DeclarationContainer.h index e4b3320a..9d7a17a3 100644 --- a/libsolidity/analysis/DeclarationContainer.h +++ b/libsolidity/analysis/DeclarationContainer.h @@ -58,10 +58,13 @@ public: /// @returns whether declaration is valid, and if not also returns previous declaration. Declaration const* conflictingDeclaration(Declaration const& _declaration, ASTString const* _name = nullptr) const; - /// Activates a previously inactive (invisible) variable. To be used in C99 scpoing for + /// Activates a previously inactive (invisible) variable. To be used in C99 scoping for /// VariableDeclarationStatements. void activateVariable(ASTString const& _name); + /// @returns true if declaration is currently invisible. + bool isInvisible(ASTString const& _name) const; + /// @returns existing declaration names similar to @a _name. /// Searches this and all parent containers. std::vector<ASTString> similarNames(ASTString const& _name) const; diff --git a/libsolidity/analysis/DocStringAnalyser.cpp b/libsolidity/analysis/DocStringAnalyser.cpp index b3fb5258..c1b97def 100644 --- a/libsolidity/analysis/DocStringAnalyser.cpp +++ b/libsolidity/analysis/DocStringAnalyser.cpp @@ -48,7 +48,10 @@ bool DocStringAnalyser::visit(ContractDefinition const& _contract) bool DocStringAnalyser::visit(FunctionDefinition const& _function) { - handleCallable(_function, _function, _function.annotation()); + if (_function.isConstructor()) + handleConstructor(_function, _function, _function.annotation()); + else + handleCallable(_function, _function, _function.annotation()); return true; } @@ -66,15 +69,11 @@ bool DocStringAnalyser::visit(EventDefinition const& _event) return true; } -void DocStringAnalyser::handleCallable( +void DocStringAnalyser::checkParameters( CallableDeclaration const& _callable, - Documented const& _node, DocumentedAnnotation& _annotation ) { - static const set<string> validTags = set<string>{"author", "dev", "notice", "return", "param"}; - parseDocStrings(_node, _annotation, validTags, "functions"); - set<string> validParams; for (auto const& p: _callable.parameters()) validParams.insert(p->name()); @@ -89,6 +88,29 @@ void DocStringAnalyser::handleCallable( i->second.paramName + "\" not found in the parameter list of the function." ); + +} + +void DocStringAnalyser::handleConstructor( + CallableDeclaration const& _callable, + Documented const& _node, + DocumentedAnnotation& _annotation +) +{ + static const set<string> validTags = set<string>{"author", "dev", "notice", "param"}; + parseDocStrings(_node, _annotation, validTags, "constructor"); + checkParameters(_callable, _annotation); +} + +void DocStringAnalyser::handleCallable( + CallableDeclaration const& _callable, + Documented const& _node, + DocumentedAnnotation& _annotation +) +{ + static const set<string> validTags = set<string>{"author", "dev", "notice", "return", "param"}; + parseDocStrings(_node, _annotation, validTags, "functions"); + checkParameters(_callable, _annotation); } void DocStringAnalyser::parseDocStrings( diff --git a/libsolidity/analysis/DocStringAnalyser.h b/libsolidity/analysis/DocStringAnalyser.h index 158b4060..5d339428 100644 --- a/libsolidity/analysis/DocStringAnalyser.h +++ b/libsolidity/analysis/DocStringAnalyser.h @@ -48,6 +48,17 @@ private: virtual bool visit(ModifierDefinition const& _modifier) override; virtual bool visit(EventDefinition const& _event) override; + void checkParameters( + CallableDeclaration const& _callable, + DocumentedAnnotation& _annotation + ); + + void handleConstructor( + CallableDeclaration const& _callable, + Documented const& _node, + DocumentedAnnotation& _annotation + ); + void handleCallable( CallableDeclaration const& _callable, Documented const& _node, diff --git a/libsolidity/analysis/GlobalContext.cpp b/libsolidity/analysis/GlobalContext.cpp index 756bb540..cba2655c 100644 --- a/libsolidity/analysis/GlobalContext.cpp +++ b/libsolidity/analysis/GlobalContext.cpp @@ -42,7 +42,7 @@ m_magicVariables(vector<shared_ptr<MagicVariableDeclaration const>>{ make_shared<MagicVariableDeclaration>("blockhash", make_shared<FunctionType>(strings{"uint256"}, strings{"bytes32"}, FunctionType::Kind::BlockHash, false, StateMutability::View)), make_shared<MagicVariableDeclaration>("ecrecover", make_shared<FunctionType>(strings{"bytes32", "uint8", "bytes32", "bytes32"}, strings{"address"}, FunctionType::Kind::ECRecover, false, StateMutability::Pure)), make_shared<MagicVariableDeclaration>("gasleft", make_shared<FunctionType>(strings(), strings{"uint256"}, FunctionType::Kind::GasLeft, false, StateMutability::View)), - make_shared<MagicVariableDeclaration>("keccak256", make_shared<FunctionType>(strings(), strings{"bytes32"}, FunctionType::Kind::SHA3, true, StateMutability::Pure)), + make_shared<MagicVariableDeclaration>("keccak256", make_shared<FunctionType>(strings{"bytes memory"}, strings{"bytes32"}, FunctionType::Kind::KECCAK256, false, StateMutability::Pure)), make_shared<MagicVariableDeclaration>("log0", make_shared<FunctionType>(strings{"bytes32"}, strings{}, FunctionType::Kind::Log0)), make_shared<MagicVariableDeclaration>("log1", make_shared<FunctionType>(strings{"bytes32", "bytes32"}, strings{}, FunctionType::Kind::Log1)), make_shared<MagicVariableDeclaration>("log2", make_shared<FunctionType>(strings{"bytes32", "bytes32", "bytes32"}, strings{}, FunctionType::Kind::Log2)), @@ -55,11 +55,11 @@ m_magicVariables(vector<shared_ptr<MagicVariableDeclaration const>>{ make_shared<MagicVariableDeclaration>("require", make_shared<FunctionType>(strings{"bool", "string memory"}, strings{}, FunctionType::Kind::Require, false, StateMutability::Pure)), make_shared<MagicVariableDeclaration>("revert", make_shared<FunctionType>(strings(), strings(), FunctionType::Kind::Revert, false, StateMutability::Pure)), make_shared<MagicVariableDeclaration>("revert", make_shared<FunctionType>(strings{"string memory"}, strings(), FunctionType::Kind::Revert, false, StateMutability::Pure)), - make_shared<MagicVariableDeclaration>("ripemd160", make_shared<FunctionType>(strings(), strings{"bytes20"}, FunctionType::Kind::RIPEMD160, true, StateMutability::Pure)), - make_shared<MagicVariableDeclaration>("selfdestruct", make_shared<FunctionType>(strings{"address"}, strings{}, FunctionType::Kind::Selfdestruct)), - make_shared<MagicVariableDeclaration>("sha256", make_shared<FunctionType>(strings(), strings{"bytes32"}, FunctionType::Kind::SHA256, true, StateMutability::Pure)), - make_shared<MagicVariableDeclaration>("sha3", make_shared<FunctionType>(strings(), strings{"bytes32"}, FunctionType::Kind::SHA3, true, StateMutability::Pure)), - make_shared<MagicVariableDeclaration>("suicide", make_shared<FunctionType>(strings{"address"}, strings{}, FunctionType::Kind::Selfdestruct)), + make_shared<MagicVariableDeclaration>("ripemd160", make_shared<FunctionType>(strings{"bytes memory"}, strings{"bytes20"}, FunctionType::Kind::RIPEMD160, false, StateMutability::Pure)), + make_shared<MagicVariableDeclaration>("selfdestruct", make_shared<FunctionType>(strings{"address payable"}, strings{}, FunctionType::Kind::Selfdestruct)), + make_shared<MagicVariableDeclaration>("sha256", make_shared<FunctionType>(strings{"bytes memory"}, strings{"bytes32"}, FunctionType::Kind::SHA256, false, StateMutability::Pure)), + make_shared<MagicVariableDeclaration>("sha3", make_shared<FunctionType>(strings{"bytes memory"}, strings{"bytes32"}, FunctionType::Kind::KECCAK256, false, StateMutability::Pure)), + make_shared<MagicVariableDeclaration>("suicide", make_shared<FunctionType>(strings{"address payable"}, strings{}, FunctionType::Kind::Selfdestruct)), make_shared<MagicVariableDeclaration>("tx", make_shared<MagicType>(MagicType::Kind::Transaction)) }) { diff --git a/libsolidity/analysis/NameAndTypeResolver.cpp b/libsolidity/analysis/NameAndTypeResolver.cpp index 0a356f04..b452a49a 100644 --- a/libsolidity/analysis/NameAndTypeResolver.cpp +++ b/libsolidity/analysis/NameAndTypeResolver.cpp @@ -54,11 +54,10 @@ NameAndTypeResolver::NameAndTypeResolver( bool NameAndTypeResolver::registerDeclarations(SourceUnit& _sourceUnit, ASTNode const* _currentScope) { - bool useC99Scoping = _sourceUnit.annotation().experimentalFeatures.count(ExperimentalFeature::V050); // The helper registers all declarations in m_scopes as a side-effect of its construction. try { - DeclarationRegistrationHelper registrar(m_scopes, _sourceUnit, useC99Scoping, m_errorReporter, _currentScope); + DeclarationRegistrationHelper registrar(m_scopes, _sourceUnit, m_errorReporter, _currentScope); } catch (FatalError const&) { @@ -157,7 +156,13 @@ bool NameAndTypeResolver::updateDeclaration(Declaration const& _declaration) void NameAndTypeResolver::activateVariable(string const& _name) { solAssert(m_currentScope, ""); - m_currentScope->activateVariable(_name); + // Scoped local variables are invisible before activation. + // When a local variable is activated, its name is removed + // from a scope's invisible variables. + // This is used to avoid activation of variables of same name + // in the same scope (an error is returned). + if (m_currentScope->isInvisible(_name)) + m_currentScope->activateVariable(_name); } vector<Declaration const*> NameAndTypeResolver::resolveName(ASTString const& _name, ASTNode const* _scope) const @@ -226,7 +231,7 @@ vector<Declaration const*> NameAndTypeResolver::cleanedDeclarations( shared_ptr<FunctionType const> newFunctionType { d->functionType(false) }; if (!newFunctionType) newFunctionType = d->functionType(true); - return newFunctionType && functionType->hasEqualArgumentTypes(*newFunctionType); + return newFunctionType && functionType->hasEqualParameterTypes(*newFunctionType); } )) uniqueFunctions.push_back(declaration); @@ -290,10 +295,7 @@ bool NameAndTypeResolver::resolveNamesAndTypesInternal(ASTNode& _node, bool _res { setScope(contract); if (!resolveNamesAndTypes(*node, false)) - { success = false; - break; - } } if (!success) @@ -449,11 +451,9 @@ string NameAndTypeResolver::similarNameSuggestions(ASTString const& _name) const DeclarationRegistrationHelper::DeclarationRegistrationHelper( map<ASTNode const*, shared_ptr<DeclarationContainer>>& _scopes, ASTNode& _astRoot, - bool _useC99Scoping, ErrorReporter& _errorReporter, ASTNode const* _currentScope ): - m_useC99Scoping(_useC99Scoping), m_scopes(_scopes), m_currentScope(_currentScope), m_errorReporter(_errorReporter) @@ -626,32 +626,39 @@ void DeclarationRegistrationHelper::endVisit(ModifierDefinition&) closeCurrentScope(); } +bool DeclarationRegistrationHelper::visit(FunctionTypeName& _funTypeName) +{ + enterNewSubScope(_funTypeName); + return true; +} + +void DeclarationRegistrationHelper::endVisit(FunctionTypeName&) +{ + closeCurrentScope(); +} + bool DeclarationRegistrationHelper::visit(Block& _block) { _block.setScope(m_currentScope); - if (m_useC99Scoping) - enterNewSubScope(_block); + enterNewSubScope(_block); return true; } void DeclarationRegistrationHelper::endVisit(Block&) { - if (m_useC99Scoping) - closeCurrentScope(); + closeCurrentScope(); } bool DeclarationRegistrationHelper::visit(ForStatement& _for) { _for.setScope(m_currentScope); - if (m_useC99Scoping) - enterNewSubScope(_for); + enterNewSubScope(_for); return true; } void DeclarationRegistrationHelper::endVisit(ForStatement&) { - if (m_useC99Scoping) - closeCurrentScope(); + closeCurrentScope(); } void DeclarationRegistrationHelper::endVisit(VariableDeclarationStatement& _variableDeclarationStatement) @@ -711,14 +718,9 @@ void DeclarationRegistrationHelper::registerDeclaration(Declaration& _declaratio dynamic_cast<EventDefinition const*>(m_currentScope) ) warnAboutShadowing = false; - // Do not warn about the constructor shadowing the contract. - if (auto fun = dynamic_cast<FunctionDefinition const*>(&_declaration)) - if (fun->isConstructor()) - warnAboutShadowing = false; - // Register declaration as inactive if we are in block scope and C99 mode. + // Register declaration as inactive if we are in block scope. bool inactive = - m_useC99Scoping && (dynamic_cast<Block const*>(m_currentScope) || dynamic_cast<ForStatement const*>(m_currentScope)); registerDeclaration(*m_scopes[m_currentScope], _declaration, nullptr, nullptr, warnAboutShadowing, inactive, m_errorReporter); diff --git a/libsolidity/analysis/NameAndTypeResolver.h b/libsolidity/analysis/NameAndTypeResolver.h index 3d10fbd8..a72c21e3 100644 --- a/libsolidity/analysis/NameAndTypeResolver.h +++ b/libsolidity/analysis/NameAndTypeResolver.h @@ -69,7 +69,7 @@ public: /// that create their own scope. /// @returns false in case of error. bool updateDeclaration(Declaration const& _declaration); - /// Activates a previously inactive (invisible) variable. To be used in C99 scpoing for + /// Activates a previously inactive (invisible) variable. To be used in C99 scoping for /// VariableDeclarationStatements. void activateVariable(std::string const& _name); @@ -142,7 +142,6 @@ public: DeclarationRegistrationHelper( std::map<ASTNode const*, std::shared_ptr<DeclarationContainer>>& _scopes, ASTNode& _astRoot, - bool _useC99Scoping, ErrorReporter& _errorReporter, ASTNode const* _currentScope = nullptr ); @@ -172,6 +171,8 @@ private: void endVisit(FunctionDefinition& _function) override; bool visit(ModifierDefinition& _modifier) override; void endVisit(ModifierDefinition& _modifier) override; + bool visit(FunctionTypeName& _funTypeName) override; + void endVisit(FunctionTypeName& _funTypeName) override; bool visit(Block& _block) override; void endVisit(Block& _block) override; bool visit(ForStatement& _forLoop) override; @@ -190,7 +191,6 @@ private: /// @returns the canonical name of the current scope. std::string currentCanonicalName() const; - bool m_useC99Scoping = false; std::map<ASTNode const*, std::shared_ptr<DeclarationContainer>>& m_scopes; ASTNode const* m_currentScope = nullptr; VariableScope* m_currentFunction = nullptr; diff --git a/libsolidity/analysis/PostTypeChecker.cpp b/libsolidity/analysis/PostTypeChecker.cpp index 19d0b708..240d7973 100644 --- a/libsolidity/analysis/PostTypeChecker.cpp +++ b/libsolidity/analysis/PostTypeChecker.cpp @@ -91,8 +91,11 @@ bool PostTypeChecker::visit(Identifier const& _identifier) VariableDeclaration const* PostTypeChecker::findCycle(VariableDeclaration const& _startingFrom) { - auto visitor = [&](VariableDeclaration const& _variable, CycleDetector<VariableDeclaration>& _cycleDetector) + auto visitor = [&](VariableDeclaration const& _variable, CycleDetector<VariableDeclaration>& _cycleDetector, size_t _depth) { + if (_depth >= 256) + m_errorReporter.fatalDeclarationError(_variable.location(), "Variable definition exhausting cyclic dependency validator."); + // Iterating through the dependencies needs to be deterministic and thus cannot // depend on the memory layout. // Because of that, we sort by AST node id. diff --git a/libsolidity/analysis/ReferencesResolver.cpp b/libsolidity/analysis/ReferencesResolver.cpp index f91eaf6e..2adc8e77 100644 --- a/libsolidity/analysis/ReferencesResolver.cpp +++ b/libsolidity/analysis/ReferencesResolver.cpp @@ -30,7 +30,10 @@ #include <libsolidity/inlineasm/AsmData.h> #include <libsolidity/interface/ErrorReporter.h> +#include <libdevcore/StringUtils.h> + #include <boost/algorithm/string.hpp> +#include <boost/range/adaptor/transformed.hpp> using namespace std; using namespace dev; @@ -47,10 +50,7 @@ bool ReferencesResolver::visit(Block const& _block) { if (!m_resolveInsideCode) return false; - m_experimental050Mode = _block.sourceUnit().annotation().experimentalFeatures.count(ExperimentalFeature::V050); - // C99-scoped variables - if (m_experimental050Mode) - m_resolver.setScope(&_block); + m_resolver.setScope(&_block); return true; } @@ -59,19 +59,14 @@ void ReferencesResolver::endVisit(Block const& _block) if (!m_resolveInsideCode) return; - // C99-scoped variables - if (m_experimental050Mode) - m_resolver.setScope(_block.scope()); + m_resolver.setScope(_block.scope()); } bool ReferencesResolver::visit(ForStatement const& _for) { if (!m_resolveInsideCode) return false; - m_experimental050Mode = _for.sourceUnit().annotation().experimentalFeatures.count(ExperimentalFeature::V050); - // C99-scoped variables - if (m_experimental050Mode) - m_resolver.setScope(&_for); + m_resolver.setScope(&_for); return true; } @@ -79,18 +74,16 @@ void ReferencesResolver::endVisit(ForStatement const& _for) { if (!m_resolveInsideCode) return; - if (m_experimental050Mode) - m_resolver.setScope(_for.scope()); + m_resolver.setScope(_for.scope()); } void ReferencesResolver::endVisit(VariableDeclarationStatement const& _varDeclStatement) { if (!m_resolveInsideCode) return; - if (m_experimental050Mode) - for (auto const& var: _varDeclStatement.declarations()) - if (var) - m_resolver.activateVariable(var->name()); + for (auto const& var: _varDeclStatement.declarations()) + if (var) + m_resolver.activateVariable(var->name()); } bool ReferencesResolver::visit(Identifier const& _identifier) @@ -99,9 +92,14 @@ bool ReferencesResolver::visit(Identifier const& _identifier) if (declarations.empty()) { string suggestions = m_resolver.similarNameSuggestions(_identifier.name()); - string errorMessage = - "Undeclared identifier." + - (suggestions.empty()? "": " Did you mean " + std::move(suggestions) + "?"); + string errorMessage = "Undeclared identifier."; + if (!suggestions.empty()) + { + if ("\"" + _identifier.name() + "\"" == suggestions) + errorMessage += " " + std::move(suggestions) + " is not (or not yet) visible at this point."; + else + errorMessage += " Did you mean " + std::move(suggestions) + "?"; + } declarationError(_identifier.location(), errorMessage); } else if (declarations.size() == 1) @@ -114,7 +112,28 @@ bool ReferencesResolver::visit(Identifier const& _identifier) bool ReferencesResolver::visit(ElementaryTypeName const& _typeName) { - _typeName.annotation().type = Type::fromElementaryTypeName(_typeName.typeName()); + if (!_typeName.annotation().type) + { + _typeName.annotation().type = Type::fromElementaryTypeName(_typeName.typeName()); + if (_typeName.stateMutability().is_initialized()) + { + // for non-address types this was already caught by the parser + solAssert(_typeName.annotation().type->category() == Type::Category::Address, ""); + switch(*_typeName.stateMutability()) + { + case StateMutability::Payable: + case StateMutability::NonPayable: + _typeName.annotation().type = make_shared<AddressType>(*_typeName.stateMutability()); + break; + default: + m_errorReporter.typeError( + _typeName.location(), + "Address types can only be payable or non-payable." + ); + break; + } + } + } return true; } @@ -147,7 +166,7 @@ void ReferencesResolver::endVisit(UserDefinedTypeName const& _typeName) Declaration const* declaration = m_resolver.pathFromCurrentScope(_typeName.namePath()); if (!declaration) { - declarationError(_typeName.location(), "Identifier not found or not unique."); + fatalDeclarationError(_typeName.location(), "Identifier not found or not unique."); return; } @@ -160,7 +179,10 @@ void ReferencesResolver::endVisit(UserDefinedTypeName const& _typeName) else if (ContractDefinition const* contract = dynamic_cast<ContractDefinition const*>(declaration)) _typeName.annotation().type = make_shared<ContractType>(*contract); else + { + _typeName.annotation().type = make_shared<TupleType>(); typeError(_typeName.location(), "Name has to refer to a struct, enum or contract."); + } } void ReferencesResolver::endVisit(FunctionTypeName const& _typeName) @@ -171,13 +193,13 @@ void ReferencesResolver::endVisit(FunctionTypeName const& _typeName) case VariableDeclaration::Visibility::External: break; default: - typeError(_typeName.location(), "Invalid visibility, can only be \"external\" or \"internal\"."); + fatalTypeError(_typeName.location(), "Invalid visibility, can only be \"external\" or \"internal\"."); return; } if (_typeName.isPayable() && _typeName.visibility() != VariableDeclaration::Visibility::External) { - typeError(_typeName.location(), "Only external function types can be payable."); + fatalTypeError(_typeName.location(), "Only external function types can be payable."); return; } @@ -187,7 +209,7 @@ void ReferencesResolver::endVisit(FunctionTypeName const& _typeName) solAssert(t->annotation().type, "Type not set for parameter."); if (!t->annotation().type->canBeUsedExternally(false)) { - typeError(t->location(), "Internal type cannot be used for external function type."); + fatalTypeError(t->location(), "Internal type cannot be used for external function type."); return; } } @@ -224,6 +246,8 @@ void ReferencesResolver::endVisit(ArrayTypeName const& _typeName) RationalNumberType const* lengthType = dynamic_cast<RationalNumberType const*>(lengthTypeGeneric.get()); if (!lengthType || !lengthType->mobileType()) fatalTypeError(length->location(), "Invalid array length, expected integer literal or constant expression."); + else if (lengthType->isZero()) + fatalTypeError(length->location(), "Array with zero length specified."); else if (lengthType->isFractional()) fatalTypeError(length->location(), "Array with fractional length specified."); else if (lengthType->isNegative()) @@ -245,26 +269,34 @@ bool ReferencesResolver::visit(InlineAssembly const& _inlineAssembly) // external references. ErrorList errors; ErrorReporter errorsIgnored(errors); - julia::ExternalIdentifierAccess::Resolver resolver = - [&](assembly::Identifier const& _identifier, julia::IdentifierContext, bool _crossesFunctionBoundary) { - auto declarations = m_resolver.nameFromCurrentScope(_identifier.name); - bool isSlot = boost::algorithm::ends_with(_identifier.name, "_slot"); - bool isOffset = boost::algorithm::ends_with(_identifier.name, "_offset"); + yul::ExternalIdentifierAccess::Resolver resolver = + [&](assembly::Identifier const& _identifier, yul::IdentifierContext, bool _crossesFunctionBoundary) { + auto declarations = m_resolver.nameFromCurrentScope(_identifier.name.str()); + bool isSlot = boost::algorithm::ends_with(_identifier.name.str(), "_slot"); + bool isOffset = boost::algorithm::ends_with(_identifier.name.str(), "_offset"); if (isSlot || isOffset) { // special mode to access storage variables if (!declarations.empty()) // the special identifier exists itself, we should not allow that. return size_t(-1); - string realName = _identifier.name.substr(0, _identifier.name.size() - ( + string realName = _identifier.name.str().substr(0, _identifier.name.str().size() - ( isSlot ? string("_slot").size() : string("_offset").size() )); + if (realName.empty()) + { + declarationError(_identifier.location, "In variable names _slot and _offset can only be used as a suffix."); + return size_t(-1); + } declarations = m_resolver.nameFromCurrentScope(realName); } if (declarations.size() != 1) + { + declarationError(_identifier.location, "Multiple matching identifiers. Resolving overloaded identifiers is not supported."); return size_t(-1); + } if (auto var = dynamic_cast<VariableDeclaration const*>(declarations.front())) if (var->isLocalVariable() && _crossesFunctionBoundary) { @@ -280,7 +312,7 @@ bool ReferencesResolver::visit(InlineAssembly const& _inlineAssembly) // Will be re-generated later with correct information // We use the latest EVM version because we will re-run it anyway. assembly::AsmAnalysisInfo analysisInfo; - boost::optional<Error::Type> errorTypeForLoose = m_experimental050Mode ? Error::Type::SyntaxError : Error::Type::Warning; + boost::optional<Error::Type> errorTypeForLoose = Error::Type::SyntaxError; assembly::AsmAnalyzer(analysisInfo, errorsIgnored, EVMVersion(), errorTypeForLoose, assembly::AsmFlavour::Loose, resolver).analyze(_inlineAssembly.operations()); return false; } @@ -297,112 +329,106 @@ void ReferencesResolver::endVisit(VariableDeclaration const& _variable) if (_variable.annotation().type) return; - TypePointer type; - if (_variable.typeName()) + if (_variable.isConstant() && !_variable.isStateVariable()) + m_errorReporter.declarationError(_variable.location(), "The \"constant\" keyword can only be used for state variables."); + + if (!_variable.typeName()) + { + // This can still happen in very unusual cases where a developer uses constructs, such as + // `var a;`, however, such code will have generated errors already. + // However, we cannot blindingly solAssert() for that here, as the TypeChecker (which is + // invoking ReferencesResolver) is generating it, so the error is most likely(!) generated + // after this step. + return; + } + using Location = VariableDeclaration::Location; + Location varLoc = _variable.referenceLocation(); + DataLocation typeLoc = DataLocation::Memory; + + set<Location> allowedDataLocations = _variable.allowedDataLocations(); + if (!allowedDataLocations.count(varLoc)) { - type = _variable.typeName()->annotation().type; - using Location = VariableDeclaration::Location; - Location varLoc = _variable.referenceLocation(); - DataLocation typeLoc = DataLocation::Memory; - // References are forced to calldata for external function parameters (not return) - // and memory for parameters (also return) of publicly visible functions. - // They default to memory for function parameters and storage for local variables. - // As an exception, "storage" is allowed for library functions. - if (auto ref = dynamic_cast<ReferenceType const*>(type.get())) + auto locationToString = [](VariableDeclaration::Location _location) -> string { - bool isPointer = true; - if (_variable.isExternalCallableParameter()) - { - auto const& contract = dynamic_cast<ContractDefinition const&>( - *dynamic_cast<Declaration const&>(*_variable.scope()).scope() - ); - if (contract.isLibrary()) - { - if (varLoc == Location::Memory) - fatalTypeError(_variable.location(), - "Location has to be calldata or storage for external " - "library functions (remove the \"memory\" keyword)." - ); - } - else - { - // force location of external function parameters (not return) to calldata - if (varLoc != Location::Default) - fatalTypeError(_variable.location(), - "Location has to be calldata for external functions " - "(remove the \"memory\" or \"storage\" keyword)." - ); - } - if (varLoc == Location::Default) - typeLoc = DataLocation::CallData; - else - typeLoc = varLoc == Location::Memory ? DataLocation::Memory : DataLocation::Storage; - } - else if (_variable.isCallableParameter() && dynamic_cast<Declaration const&>(*_variable.scope()).isPublic()) + switch (_location) { - auto const& contract = dynamic_cast<ContractDefinition const&>( - *dynamic_cast<Declaration const&>(*_variable.scope()).scope() - ); - // force locations of public or external function (return) parameters to memory - if (varLoc == Location::Storage && !contract.isLibrary()) - fatalTypeError(_variable.location(), - "Location has to be memory for publicly visible functions " - "(remove the \"storage\" keyword)." - ); - if (varLoc == Location::Default || !contract.isLibrary()) - typeLoc = DataLocation::Memory; - else - typeLoc = varLoc == Location::Memory ? DataLocation::Memory : DataLocation::Storage; + case Location::Memory: return "\"memory\""; + case Location::Storage: return "\"storage\""; + case Location::CallData: return "\"calldata\""; + case Location::Unspecified: return "none"; } + return {}; + }; + + string errorString; + if (!_variable.hasReferenceOrMappingType()) + errorString = "Data location can only be specified for array, struct or mapping types"; + else + { + errorString = "Data location must be " + + joinHumanReadable( + allowedDataLocations | boost::adaptors::transformed(locationToString), + ", ", + " or " + ); + if (_variable.isCallableParameter()) + errorString += + " for " + + string(_variable.isReturnParameter() ? "return " : "") + + "parameter in" + + string(_variable.isExternalCallableParameter() ? " external" : "") + + " function"; else - { - if (_variable.isConstant()) - { - if (varLoc != Location::Default && varLoc != Location::Memory) - fatalTypeError( - _variable.location(), - "Storage location has to be \"memory\" (or unspecified) for constants." - ); - typeLoc = DataLocation::Memory; - } - else if (varLoc == Location::Default) - { - if (_variable.isCallableParameter()) - typeLoc = DataLocation::Memory; - else - { - typeLoc = DataLocation::Storage; - if (_variable.isLocalVariable()) - { - if (_variable.sourceUnit().annotation().experimentalFeatures.count(ExperimentalFeature::V050)) - typeError( - _variable.location(), - "Storage location must be specified as either \"memory\" or \"storage\"." - ); - else - m_errorReporter.warning( - _variable.location(), - "Variable is declared as a storage pointer. " - "Use an explicit \"storage\" keyword to silence this warning." - ); - } - } - } - else - typeLoc = varLoc == Location::Memory ? DataLocation::Memory : DataLocation::Storage; - isPointer = !_variable.isStateVariable(); - } + errorString += " for variable"; + } + errorString += ", but " + locationToString(varLoc) + " was given."; + typeError(_variable.location(), errorString); - type = ref->copyForLocation(typeLoc, isPointer); + solAssert(!allowedDataLocations.empty(), ""); + varLoc = *allowedDataLocations.begin(); + } + + // Find correct data location. + if (_variable.isEventParameter()) + { + solAssert(varLoc == Location::Unspecified, ""); + typeLoc = DataLocation::Memory; + } + else if (_variable.isStateVariable()) + { + solAssert(varLoc == Location::Unspecified, ""); + typeLoc = _variable.isConstant() ? DataLocation::Memory : DataLocation::Storage; + } + else if ( + dynamic_cast<StructDefinition const*>(_variable.scope()) || + dynamic_cast<EnumDefinition const*>(_variable.scope()) + ) + // The actual location will later be changed depending on how the type is used. + typeLoc = DataLocation::Storage; + else + switch (varLoc) + { + case Location::Memory: + typeLoc = DataLocation::Memory; + break; + case Location::Storage: + typeLoc = DataLocation::Storage; + break; + case Location::CallData: + typeLoc = DataLocation::CallData; + break; + case Location::Unspecified: + solAssert(!_variable.hasReferenceOrMappingType(), "Data location not properly set."); } - else if (varLoc != Location::Default && !ref) - typeError(_variable.location(), "Storage location can only be given for array or struct types."); - _variable.annotation().type = type; + TypePointer type = _variable.typeName()->annotation().type; + if (auto ref = dynamic_cast<ReferenceType const*>(type.get())) + { + bool isPointer = !_variable.isStateVariable(); + type = ref->copyForLocation(typeLoc, isPointer); } - else if (!_variable.canHaveAutoType()) - typeError(_variable.location(), "Explicit type needed."); - // otherwise we have a "var"-declaration whose type is resolved by the first assignment + + _variable.annotation().type = type; } void ReferencesResolver::typeError(SourceLocation const& _location, string const& _description) diff --git a/libsolidity/analysis/ReferencesResolver.h b/libsolidity/analysis/ReferencesResolver.h index 4e8f54b5..24ec4643 100644 --- a/libsolidity/analysis/ReferencesResolver.h +++ b/libsolidity/analysis/ReferencesResolver.h @@ -94,7 +94,6 @@ private: std::vector<ParameterList const*> m_returnParameters; bool const m_resolveInsideCode; bool m_errorOccurred = false; - bool m_experimental050Mode = false; }; } diff --git a/libsolidity/analysis/SemVerHandler.cpp b/libsolidity/analysis/SemVerHandler.cpp index 42186396..64fa17b3 100644 --- a/libsolidity/analysis/SemVerHandler.cpp +++ b/libsolidity/analysis/SemVerHandler.cpp @@ -106,18 +106,22 @@ bool SemVerMatchExpression::MatchComponent::matches(SemVerVersion const& _versio } if (cmp == 0 && !_version.prerelease.empty() && didCompare) cmp = -1; - if (prefix == Token::Assign) + + switch (prefix) + { + case Token::Assign: return cmp == 0; - else if (prefix == Token::LessThan) + case Token::LessThan: return cmp < 0; - else if (prefix == Token::LessThanOrEqual) + case Token::LessThanOrEqual: return cmp <= 0; - else if (prefix == Token::GreaterThan) + case Token::GreaterThan: return cmp > 0; - else if (prefix == Token::GreaterThanOrEqual) + case Token::GreaterThanOrEqual: return cmp >= 0; - else + default: solAssert(false, "Invalid SemVer expression"); + } return false; } } @@ -195,22 +199,23 @@ void SemVerMatchExpressionParser::parseMatchExpression() SemVerMatchExpression::MatchComponent SemVerMatchExpressionParser::parseMatchComponent() { SemVerMatchExpression::MatchComponent component; - Token::Value token = currentToken(); - if ( - token == Token::BitXor || - token == Token::BitNot || - token == Token::LessThan || - token == Token::LessThanOrEqual|| - token == Token::GreaterThan || - token == Token::GreaterThanOrEqual || - token == Token::Assign - ) + Token token = currentToken(); + + switch (token) { + case Token::BitXor: + case Token::BitNot: + case Token::LessThan: + case Token::LessThanOrEqual: + case Token::GreaterThan: + case Token::GreaterThanOrEqual: + case Token::Assign: component.prefix = token; nextToken(); - } - else + break; + default: component.prefix = Token::Assign; + } component.levelsPresent = 0; while (component.levelsPresent < 3) @@ -275,7 +280,7 @@ char SemVerMatchExpressionParser::nextChar() return currentChar(); } -Token::Value SemVerMatchExpressionParser::currentToken() const +Token SemVerMatchExpressionParser::currentToken() const { if (m_pos < m_tokens.size()) return m_tokens[m_pos]; diff --git a/libsolidity/analysis/SemVerHandler.h b/libsolidity/analysis/SemVerHandler.h index 76b70c5b..03a557c5 100644 --- a/libsolidity/analysis/SemVerHandler.h +++ b/libsolidity/analysis/SemVerHandler.h @@ -61,7 +61,7 @@ struct SemVerMatchExpression struct MatchComponent { /// Prefix from < > <= >= ~ ^ - Token::Value prefix = Token::Illegal; + Token prefix = Token::Illegal; /// Version, where unsigned(-1) in major, minor or patch denotes '*', 'x' or 'X' SemVerVersion version; /// Whether we have 1, 1.2 or 1.2.4 @@ -81,7 +81,7 @@ struct SemVerMatchExpression class SemVerMatchExpressionParser { public: - SemVerMatchExpressionParser(std::vector<Token::Value> const& _tokens, std::vector<std::string> const& _literals): + SemVerMatchExpressionParser(std::vector<Token> const& _tokens, std::vector<std::string> const& _literals): m_tokens(_tokens), m_literals(_literals) {} SemVerMatchExpression parse(); @@ -95,10 +95,10 @@ private: char currentChar() const; char nextChar(); - Token::Value currentToken() const; + Token currentToken() const; void nextToken(); - std::vector<Token::Value> m_tokens; + std::vector<Token> m_tokens; std::vector<std::string> m_literals; unsigned m_pos = 0; diff --git a/libsolidity/analysis/StaticAnalyzer.cpp b/libsolidity/analysis/StaticAnalyzer.cpp index 00a581d0..487a5cca 100644 --- a/libsolidity/analysis/StaticAnalyzer.cpp +++ b/libsolidity/analysis/StaticAnalyzer.cpp @@ -51,22 +51,11 @@ void StaticAnalyzer::endVisit(ContractDefinition const&) bool StaticAnalyzer::visit(FunctionDefinition const& _function) { - const bool isInterface = m_currentContract->contractKind() == ContractDefinition::ContractKind::Interface; - - if (_function.noVisibilitySpecified()) - m_errorReporter.warning( - _function.location(), - "No visibility specified. Defaulting to \"" + - Declaration::visibilityToString(_function.visibility()) + - "\". " + - (isInterface ? "In interfaces it defaults to external." : "") - ); if (_function.isImplemented()) m_currentFunction = &_function; else solAssert(!m_currentFunction, ""); solAssert(m_localVarUseCount.empty(), ""); - m_nonPayablePublic = _function.isPublic() && !_function.isPayable(); m_constructor = _function.isConstructor(); return true; } @@ -74,7 +63,6 @@ bool StaticAnalyzer::visit(FunctionDefinition const& _function) void StaticAnalyzer::endVisit(FunctionDefinition const&) { m_currentFunction = nullptr; - m_nonPayablePublic = false; m_constructor = false; for (auto const& var: m_localVarUseCount) if (var.second == 0) @@ -150,61 +138,27 @@ bool StaticAnalyzer::visit(ExpressionStatement const& _statement) bool StaticAnalyzer::visit(MemberAccess const& _memberAccess) { - bool const v050 = m_currentContract->sourceUnit().annotation().experimentalFeatures.count(ExperimentalFeature::V050); - if (MagicType const* type = dynamic_cast<MagicType const*>(_memberAccess.expression().annotation().type.get())) { if (type->kind() == MagicType::Kind::Message && _memberAccess.memberName() == "gas") - { - if (v050) - m_errorReporter.typeError( - _memberAccess.location(), - "\"msg.gas\" has been deprecated in favor of \"gasleft()\"" - ); - else - m_errorReporter.warning( - _memberAccess.location(), - "\"msg.gas\" has been deprecated in favor of \"gasleft()\"" - ); - } - if (type->kind() == MagicType::Kind::Block && _memberAccess.memberName() == "blockhash") - { - if (v050) - m_errorReporter.typeError( - _memberAccess.location(), - "\"block.blockhash()\" has been deprecated in favor of \"blockhash()\"" - ); - else - m_errorReporter.warning( - _memberAccess.location(), - "\"block.blockhash()\" has been deprecated in favor of \"blockhash()\"" - ); - } + m_errorReporter.typeError( + _memberAccess.location(), + "\"msg.gas\" has been deprecated in favor of \"gasleft()\"" + ); + else if (type->kind() == MagicType::Kind::Block && _memberAccess.memberName() == "blockhash") + m_errorReporter.typeError( + _memberAccess.location(), + "\"block.blockhash()\" has been deprecated in favor of \"blockhash()\"" + ); } - if (m_nonPayablePublic && !m_library) - if (MagicType const* type = dynamic_cast<MagicType const*>(_memberAccess.expression().annotation().type.get())) - if (type->kind() == MagicType::Kind::Message && _memberAccess.memberName() == "value") - m_errorReporter.warning( - _memberAccess.location(), - "\"msg.value\" used in non-payable function. Do you want to add the \"payable\" modifier to this function?" - ); - if (_memberAccess.memberName() == "callcode") if (auto const* type = dynamic_cast<FunctionType const*>(_memberAccess.annotation().type.get())) if (type->kind() == FunctionType::Kind::BareCallCode) - { - if (v050) - m_errorReporter.typeError( - _memberAccess.location(), - "\"callcode\" has been deprecated in favour of \"delegatecall\"." - ); - else - m_errorReporter.warning( - _memberAccess.location(), - "\"callcode\" has been deprecated in favour of \"delegatecall\"." - ); - } + m_errorReporter.typeError( + _memberAccess.location(), + "\"callcode\" has been deprecated in favour of \"delegatecall\"." + ); if (m_constructor) { diff --git a/libsolidity/analysis/StaticAnalyzer.h b/libsolidity/analysis/StaticAnalyzer.h index 2a62e391..7f5c743a 100644 --- a/libsolidity/analysis/StaticAnalyzer.h +++ b/libsolidity/analysis/StaticAnalyzer.h @@ -75,9 +75,6 @@ private: /// Flag that indicates whether the current contract definition is a library. bool m_library = false; - /// Flag that indicates whether a public function does not contain the "payable" modifier. - bool m_nonPayablePublic = false; - /// Number of uses of each (named) local variable in a function, counter is initialized with zero. /// Pairs of AST ids and pointers are used as keys to ensure a deterministic order /// when traversing. diff --git a/libsolidity/analysis/SyntaxChecker.cpp b/libsolidity/analysis/SyntaxChecker.cpp index 396058f4..3f9f8373 100644 --- a/libsolidity/analysis/SyntaxChecker.cpp +++ b/libsolidity/analysis/SyntaxChecker.cpp @@ -22,6 +22,10 @@ #include <libsolidity/analysis/SemVerHandler.h> #include <libsolidity/interface/ErrorReporter.h> #include <libsolidity/interface/Version.h> +#include <boost/algorithm/cxx11/all_of.hpp> + +#include <boost/algorithm/string.hpp> +#include <string> using namespace std; using namespace dev; @@ -49,7 +53,7 @@ void SyntaxChecker::endVisit(SourceUnit const& _sourceUnit) SemVerVersion recommendedVersion{string(VersionString)}; if (!recommendedVersion.isPrerelease()) errorString += - "Consider adding \"pragma solidity ^" + + " Consider adding \"pragma solidity ^" + to_string(recommendedVersion.major()) + string(".") + to_string(recommendedVersion.minor()) + @@ -72,7 +76,7 @@ bool SyntaxChecker::visit(PragmaDirective const& _pragma) { solAssert(m_sourceUnit, ""); vector<string> literals(_pragma.literals().begin() + 1, _pragma.literals().end()); - if (literals.size() == 0) + if (literals.empty()) m_errorReporter.syntaxError( _pragma.location(), "Experimental feature name is missing." @@ -102,7 +106,7 @@ bool SyntaxChecker::visit(PragmaDirective const& _pragma) } else if (_pragma.literals()[0] == "solidity") { - vector<Token::Value> tokens(_pragma.tokens().begin() + 1, _pragma.tokens().end()); + vector<Token> tokens(_pragma.tokens().begin() + 1, _pragma.tokens().end()); vector<string> literals(_pragma.literals().begin() + 1, _pragma.literals().end()); SemVerMatchExpressionParser parser(tokens, literals); auto matchExpression = parser.parse(); @@ -134,9 +138,25 @@ void SyntaxChecker::endVisit(ModifierDefinition const& _modifier) m_placeholderFound = false; } -bool SyntaxChecker::visit(WhileStatement const&) +void SyntaxChecker::checkSingleStatementVariableDeclaration(ASTNode const& _statement) +{ + auto varDecl = dynamic_cast<VariableDeclarationStatement const*>(&_statement); + if (varDecl) + m_errorReporter.syntaxError(_statement.location(), "Variable declarations can only be used inside blocks."); +} + +bool SyntaxChecker::visit(IfStatement const& _ifStatement) +{ + checkSingleStatementVariableDeclaration(_ifStatement.trueStatement()); + if (Statement const* _statement = _ifStatement.falseStatement()) + checkSingleStatementVariableDeclaration(*_statement); + return true; +} + +bool SyntaxChecker::visit(WhileStatement const& _whileStatement) { m_inLoopDepth++; + checkSingleStatementVariableDeclaration(_whileStatement.body()); return true; } @@ -145,9 +165,10 @@ void SyntaxChecker::endVisit(WhileStatement const&) m_inLoopDepth--; } -bool SyntaxChecker::visit(ForStatement const&) +bool SyntaxChecker::visit(ForStatement const& _forStatement) { m_inLoopDepth++; + checkSingleStatementVariableDeclaration(_forStatement.body()); return true; } @@ -174,33 +195,58 @@ bool SyntaxChecker::visit(Break const& _breakStatement) bool SyntaxChecker::visit(Throw const& _throwStatement) { - bool const v050 = m_sourceUnit->annotation().experimentalFeatures.count(ExperimentalFeature::V050); - - if (v050) - m_errorReporter.syntaxError( - _throwStatement.location(), - "\"throw\" is deprecated in favour of \"revert()\", \"require()\" and \"assert()\"." - ); - else - m_errorReporter.warning( - _throwStatement.location(), - "\"throw\" is deprecated in favour of \"revert()\", \"require()\" and \"assert()\"." - ); + m_errorReporter.syntaxError( + _throwStatement.location(), + "\"throw\" is deprecated in favour of \"revert()\", \"require()\" and \"assert()\"." + ); return true; } -bool SyntaxChecker::visit(UnaryOperation const& _operation) +bool SyntaxChecker::visit(Literal const& _literal) { - bool const v050 = m_sourceUnit->annotation().experimentalFeatures.count(ExperimentalFeature::V050); + if (_literal.token() != Token::Number) + return true; - if (_operation.getOperator() == Token::Add) + ASTString const& value = _literal.value(); + solAssert(!value.empty(), ""); + + // Generic checks no matter what base this number literal is of: + if (value.back() == '_') { - if (v050) - m_errorReporter.syntaxError(_operation.location(), "Use of unary + is deprecated."); - else - m_errorReporter.warning(_operation.location(), "Use of unary + is deprecated."); + m_errorReporter.syntaxError(_literal.location(), "Invalid use of underscores in number literal. No trailing underscores allowed."); + return true; + } + + if (value.find("__") != ASTString::npos) + { + m_errorReporter.syntaxError(_literal.location(), "Invalid use of underscores in number literal. Only one consecutive underscores between digits allowed."); + return true; + } + + if (!_literal.isHexNumber()) // decimal literal + { + if (value.find("._") != ASTString::npos) + m_errorReporter.syntaxError(_literal.location(), "Invalid use of underscores in number literal. No underscores in front of the fraction part allowed."); + + if (value.find("_.") != ASTString::npos) + m_errorReporter.syntaxError(_literal.location(), "Invalid use of underscores in number literal. No underscores in front of the fraction part allowed."); + + if (value.find("_e") != ASTString::npos) + m_errorReporter.syntaxError(_literal.location(), "Invalid use of underscores in number literal. No underscore at the end of the mantissa allowed."); + + if (value.find("e_") != ASTString::npos) + m_errorReporter.syntaxError(_literal.location(), "Invalid use of underscores in number literal. No underscore in front of exponent allowed."); } + + return true; +} + +bool SyntaxChecker::visit(UnaryOperation const& _operation) +{ + if (_operation.getOperator() == Token::Add) + m_errorReporter.syntaxError(_operation.location(), "Use of unary + is disallowed."); + return true; } @@ -210,40 +256,34 @@ bool SyntaxChecker::visit(PlaceholderStatement const&) return true; } -bool SyntaxChecker::visit(FunctionDefinition const& _function) +bool SyntaxChecker::visit(ContractDefinition const& _contract) { - bool const v050 = m_sourceUnit->annotation().experimentalFeatures.count(ExperimentalFeature::V050); - - if (v050 && _function.noVisibilitySpecified()) - m_errorReporter.syntaxError(_function.location(), "No visibility specified."); + m_isInterface = _contract.contractKind() == ContractDefinition::ContractKind::Interface; - if (_function.isOldStyleConstructor()) - { - if (v050) - m_errorReporter.syntaxError( - _function.location(), + ASTString const& contractName = _contract.name(); + for (FunctionDefinition const* function: _contract.definedFunctions()) + if (function->name() == contractName) + m_errorReporter.syntaxError(function->location(), "Functions are not allowed to have the same name as the contract. " "If you intend this to be a constructor, use \"constructor(...) { ... }\" to define it." ); - else - m_errorReporter.warning( - _function.location(), - "Defining constructors as functions with the same name as the contract is deprecated. " - "Use \"constructor(...) { ... }\" instead." - ); - } - if (!_function.isImplemented() && !_function.modifiers().empty()) + return true; +} + +bool SyntaxChecker::visit(FunctionDefinition const& _function) +{ + if (_function.noVisibilitySpecified()) { - if (v050) - m_errorReporter.syntaxError(_function.location(), "Functions without implementation cannot have modifiers."); - else - m_errorReporter.warning(_function.location(), "Modifiers of functions without implementation are ignored." ); - } - if (_function.name() == "constructor") - m_errorReporter.warning(_function.location(), - "This function is named \"constructor\" but is not the constructor of the contract. " - "If you intend this to be a constructor, use \"constructor(...) { ... }\" without the \"function\" keyword to define it." + string suggestedVisibility = _function.isFallback() || m_isInterface ? "external" : "public"; + m_errorReporter.syntaxError( + _function.location(), + "No visibility specified. Did you intend to add \"" + suggestedVisibility + "\"?" ); + } + + if (!_function.isImplemented() && !_function.modifiers().empty()) + m_errorReporter.syntaxError(_function.location(), "Functions without implementation cannot have modifiers."); + return true; } @@ -255,35 +295,27 @@ bool SyntaxChecker::visit(FunctionTypeName const& _node) for (auto const& decl: _node.returnParameterTypeList()->parameters()) if (!decl->name().empty()) - m_errorReporter.warning(decl->location(), "Naming function type return parameters is deprecated."); + m_errorReporter.syntaxError(decl->location(), "Return parameters in function types may not be named."); return true; } -bool SyntaxChecker::visit(VariableDeclaration const& _declaration) +bool SyntaxChecker::visit(VariableDeclarationStatement const& _statement) { - bool const v050 = m_sourceUnit->annotation().experimentalFeatures.count(ExperimentalFeature::V050); + // Report if none of the variable components in the tuple have a name (only possible via deprecated "var") + if (boost::algorithm::all_of_equal(_statement.declarations(), nullptr)) + m_errorReporter.syntaxError( + _statement.location(), + "The use of the \"var\" keyword is disallowed. The declaration part of the statement can be removed, since it is empty." + ); - if (!_declaration.typeName()) - { - if (v050) - m_errorReporter.syntaxError(_declaration.location(), "Use of the \"var\" keyword is deprecated."); - else - m_errorReporter.warning(_declaration.location(), "Use of the \"var\" keyword is deprecated."); - } return true; } bool SyntaxChecker::visit(StructDefinition const& _struct) { - bool const v050 = m_sourceUnit->annotation().experimentalFeatures.count(ExperimentalFeature::V050); - if (_struct.members().empty()) - { - if (v050) - m_errorReporter.syntaxError(_struct.location(), "Defining empty structs is disallowed."); - else - m_errorReporter.warning(_struct.location(), "Defining empty structs is deprecated."); - } + m_errorReporter.syntaxError(_struct.location(), "Defining empty structs is disallowed."); + return true; } diff --git a/libsolidity/analysis/SyntaxChecker.h b/libsolidity/analysis/SyntaxChecker.h index 1579df57..f5716bf9 100644 --- a/libsolidity/analysis/SyntaxChecker.h +++ b/libsolidity/analysis/SyntaxChecker.h @@ -52,6 +52,12 @@ private: virtual bool visit(ModifierDefinition const& _modifier) override; virtual void endVisit(ModifierDefinition const& _modifier) override; + /// Reports an error if _statement is a VariableDeclarationStatement. + /// Used by if/while/for to check for single statement variable declarations + /// without a block. + void checkSingleStatementVariableDeclaration(ASTNode const& _statement); + + virtual bool visit(IfStatement const& _ifStatement) override; virtual bool visit(WhileStatement const& _whileStatement) override; virtual void endVisit(WhileStatement const& _whileStatement) override; virtual bool visit(ForStatement const& _forStatement) override; @@ -66,12 +72,14 @@ private: virtual bool visit(PlaceholderStatement const& _placeholderStatement) override; + virtual bool visit(ContractDefinition const& _contract) override; virtual bool visit(FunctionDefinition const& _function) override; virtual bool visit(FunctionTypeName const& _node) override; - virtual bool visit(VariableDeclaration const& _declaration) override; + virtual bool visit(VariableDeclarationStatement const& _statement) override; virtual bool visit(StructDefinition const& _struct) override; + virtual bool visit(Literal const& _literal) override; ErrorReporter& m_errorReporter; @@ -82,6 +90,7 @@ private: bool m_versionPragmaFound = false; int m_inLoopDepth = 0; + bool m_isInterface = false; SourceUnit const* m_sourceUnit = nullptr; }; diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index 2062458e..c5e6488b 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -22,13 +22,16 @@ #include <libsolidity/analysis/TypeChecker.h> #include <memory> +#include <boost/algorithm/cxx11/all_of.hpp> #include <boost/algorithm/string/predicate.hpp> +#include <boost/algorithm/string/join.hpp> #include <boost/range/adaptor/reversed.hpp> #include <libsolidity/ast/AST.h> #include <libsolidity/inlineasm/AsmAnalysis.h> #include <libsolidity/inlineasm/AsmAnalysisInfo.h> #include <libsolidity/inlineasm/AsmData.h> #include <libsolidity/interface/ErrorReporter.h> +#include <libdevcore/Algorithms.h> using namespace std; using namespace dev; @@ -41,15 +44,13 @@ bool typeSupportedByOldABIEncoder(Type const& _type) { if (_type.dataStoredIn(DataLocation::Storage)) return true; - else if (_type.category() == Type::Category::Struct) + if (_type.category() == Type::Category::Struct) return false; - else if (_type.category() == Type::Category::Array) + if (_type.category() == Type::Category::Array) { auto const& arrayType = dynamic_cast<ArrayType const&>(_type); auto base = arrayType.baseType(); - if (!typeSupportedByOldABIEncoder(*base)) - return false; - else if (base->category() == Type::Category::Array && base->isDynamicallySized()) + if (!typeSupportedByOldABIEncoder(*base) || (base->category() == Type::Category::Array && base->isDynamicallySized())) return false; } return true; @@ -125,10 +126,7 @@ bool TypeChecker::visit(ContractDefinition const& _contract) m_errorReporter.typeError(function->parameterList().location(), "Fallback function cannot take parameters."); if (!function->returnParameters().empty()) m_errorReporter.typeError(function->returnParameterList()->location(), "Fallback function cannot return values."); - if ( - _contract.sourceUnit().annotation().experimentalFeatures.count(ExperimentalFeature::V050) && - function->visibility() != FunctionDefinition::Visibility::External - ) + if (function->visibility() != FunctionDefinition::Visibility::External) m_errorReporter.typeError(function->location(), "Fallback function must be defined as \"external\"."); } @@ -216,7 +214,7 @@ void TypeChecker::findDuplicateDefinitions(map<string, vector<T>> const& _defini SecondarySourceLocation ssl; for (size_t j = i + 1; j < overloads.size(); ++j) - if (FunctionType(*overloads[i]).hasEqualArgumentTypes(FunctionType(*overloads[j]))) + if (FunctionType(*overloads[i]).hasEqualParameterTypes(FunctionType(*overloads[j]))) { ssl.append("Other declaration is here:", overloads[j]->location()); reported.insert(j); @@ -254,7 +252,7 @@ void TypeChecker::checkContractAbstractFunctions(ContractDefinition const& _cont FunctionTypePointer funType = make_shared<FunctionType>(*function); auto it = find_if(overloads.begin(), overloads.end(), [&](FunTypeAndFlag const& _funAndFlag) { - return funType->hasEqualArgumentTypes(*_funAndFlag.first); + return funType->hasEqualParameterTypes(*_funAndFlag.first); }); if (it == overloads.end()) overloads.push_back(make_pair(funType, function->isImplemented())); @@ -281,8 +279,6 @@ void TypeChecker::checkContractAbstractFunctions(ContractDefinition const& _cont void TypeChecker::checkContractBaseConstructorArguments(ContractDefinition const& _contract) { - bool const v050 = _contract.sourceUnit().annotation().experimentalFeatures.count(ExperimentalFeature::V050); - vector<ContractDefinition const*> const& bases = _contract.annotation().linearizedBaseContracts; // Determine the arguments that are used for the base constructors. @@ -290,27 +286,19 @@ void TypeChecker::checkContractBaseConstructorArguments(ContractDefinition const { if (FunctionDefinition const* constructor = contract->constructor()) for (auto const& modifier: constructor->modifiers()) - { - auto baseContract = dynamic_cast<ContractDefinition const*>(&dereference(*modifier->name())); - if (modifier->arguments()) + if (auto baseContract = dynamic_cast<ContractDefinition const*>(&dereference(*modifier->name()))) { - if (baseContract && baseContract->constructor()) - annotateBaseConstructorArguments(_contract, baseContract->constructor(), modifier.get()); - } - else - { - if (v050) - m_errorReporter.declarationError( - modifier->location(), - "Modifier-style base constructor call without arguments." - ); + if (modifier->arguments()) + { + if (baseContract->constructor()) + annotateBaseConstructorArguments(_contract, baseContract->constructor(), modifier.get()); + } else - m_errorReporter.warning( + m_errorReporter.declarationError( modifier->location(), "Modifier-style base constructor call without arguments." ); } - } for (ASTPointer<InheritanceSpecifier> const& base: contract->baseContracts()) { @@ -337,8 +325,6 @@ void TypeChecker::annotateBaseConstructorArguments( ASTNode const* _argumentNode ) { - bool const v050 = _currentContract.sourceUnit().annotation().experimentalFeatures.count(ExperimentalFeature::V050); - solAssert(_baseConstructor, ""); solAssert(_argumentNode, ""); @@ -351,7 +337,7 @@ void TypeChecker::annotateBaseConstructorArguments( SourceLocation const* mainLocation = nullptr; SecondarySourceLocation ssl; - + if ( _currentContract.location().contains(previousNode->location()) || _currentContract.location().contains(_argumentNode->location()) @@ -367,18 +353,11 @@ void TypeChecker::annotateBaseConstructorArguments( ssl.append("Second constructor call is here: ", previousNode->location()); } - if (v050) - m_errorReporter.declarationError( - *mainLocation, - ssl, - "Base constructor arguments given twice." - ); - else - m_errorReporter.warning( - *mainLocation, - "Base constructor arguments given twice.", - ssl - ); + m_errorReporter.declarationError( + *mainLocation, + ssl, + "Base constructor arguments given twice." + ); } } @@ -425,7 +404,7 @@ void TypeChecker::checkFunctionOverride(FunctionDefinition const& function, Func FunctionType functionType(function); FunctionType superType(super); - if (!functionType.hasEqualArgumentTypes(superType)) + if (!functionType.hasEqualParameterTypes(superType)) return; if (!function.annotation().superFunction) @@ -462,7 +441,7 @@ void TypeChecker::overrideError(FunctionDefinition const& function, FunctionDefi { m_errorReporter.typeError( function.location(), - SecondarySourceLocation().append("Overriden function is here:", super.location()), + SecondarySourceLocation().append("Overridden function is here:", super.location()), message ); } @@ -496,7 +475,7 @@ void TypeChecker::checkContractExternalTypeClashes(ContractDefinition const& _co for (auto const& it: externalDeclarations) for (size_t i = 0; i < it.second.size(); ++i) for (size_t j = i + 1; j < it.second.size(); ++j) - if (!it.second[i].second->hasEqualArgumentTypes(*it.second[j].second)) + if (!it.second[i].second->hasEqualParameterTypes(*it.second[j].second)) m_errorReporter.typeError( it.second[j].first->location(), "Function overload clash during conversion to external types for arguments." @@ -519,7 +498,12 @@ void TypeChecker::checkDoubleStorageAssignment(Assignment const& _assignment) TupleType const& lhs = dynamic_cast<TupleType const&>(*type(_assignment.leftHandSide())); TupleType const& rhs = dynamic_cast<TupleType const&>(*type(_assignment.rightHandSide())); - bool fillRight = !lhs.components().empty() && (!lhs.components().back() || lhs.components().front()); + if (lhs.components().size() != rhs.components().size()) + { + solAssert(m_errorReporter.hasErrors(), ""); + return; + } + size_t storageToStorageCopies = 0; size_t toStorageCopies = 0; for (size_t i = 0; i < lhs.components().size(); ++i) @@ -527,10 +511,8 @@ void TypeChecker::checkDoubleStorageAssignment(Assignment const& _assignment) ReferenceType const* ref = dynamic_cast<ReferenceType const*>(lhs.components()[i].get()); if (!ref || !ref->dataStoredIn(DataLocation::Storage) || ref->isPointer()) continue; - size_t rhsPos = fillRight ? i : rhs.components().size() - (lhs.components().size() - i); - solAssert(rhsPos < rhs.components().size(), ""); toStorageCopies++; - if (rhs.components()[rhsPos]->dataStoredIn(DataLocation::Storage)) + if (rhs.components()[i]->dataStoredIn(DataLocation::Storage)) storageToStorageCopies++; } if (storageToStorageCopies >= 1 && toStorageCopies >= 2) @@ -543,6 +525,76 @@ void TypeChecker::checkDoubleStorageAssignment(Assignment const& _assignment) ); } +TypePointers TypeChecker::typeCheckABIDecodeAndRetrieveReturnType(FunctionCall const& _functionCall, bool _abiEncoderV2) +{ + vector<ASTPointer<Expression const>> arguments = _functionCall.arguments(); + if (arguments.size() != 2) + m_errorReporter.typeError( + _functionCall.location(), + "This function takes two arguments, but " + + toString(arguments.size()) + + " were provided." + ); + if (arguments.size() >= 1 && !type(*arguments.front())->isImplicitlyConvertibleTo(ArrayType::bytesMemory())) + m_errorReporter.typeError( + arguments.front()->location(), + "Invalid type for argument in function call. " + "Invalid implicit conversion from " + + type(*arguments.front())->toString() + + " to bytes memory requested." + ); + + if (arguments.size() < 2) + return {}; + + // The following is a rather syntactic restriction, but we check it here anyway: + // The second argument has to be a tuple expression containing type names. + TupleExpression const* tupleExpression = dynamic_cast<TupleExpression const*>(arguments[1].get()); + if (!tupleExpression) + { + m_errorReporter.typeError( + arguments[1]->location(), + "The second argument to \"abi.decode\" has to be a tuple of types." + ); + return {}; + } + + TypePointers components; + for (auto const& typeArgument: tupleExpression->components()) + { + solAssert(typeArgument, ""); + if (TypeType const* argTypeType = dynamic_cast<TypeType const*>(type(*typeArgument).get())) + { + TypePointer actualType = argTypeType->actualType(); + solAssert(actualType, ""); + // We force memory because the parser currently cannot handle + // data locations. Furthermore, storage can be a little dangerous and + // calldata is not really implemented anyway. + actualType = ReferenceType::copyForLocationIfReference(DataLocation::Memory, actualType); + // We force address payable for address types. + if (actualType->category() == Type::Category::Address) + actualType = make_shared<AddressType>(StateMutability::Payable); + solAssert( + !actualType->dataStoredIn(DataLocation::CallData) && + !actualType->dataStoredIn(DataLocation::Storage), + "" + ); + if (!actualType->fullEncodingType(false, _abiEncoderV2, false)) + m_errorReporter.typeError( + typeArgument->location(), + "Decoding type " + actualType->toString(false) + " not supported." + ); + components.push_back(actualType); + } + else + { + m_errorReporter.typeError(typeArgument->location(), "Argument has to be a type name."); + components.push_back(make_shared<TupleType>()); + } + } + return components; +} + void TypeChecker::endVisit(InheritanceSpecifier const& _inheritance) { auto base = dynamic_cast<ContractDefinition const*>(&dereference(_inheritance.name())); @@ -562,33 +614,18 @@ void TypeChecker::endVisit(InheritanceSpecifier const& _inheritance) if (arguments) { - bool v050 = m_scope->sourceUnit().annotation().experimentalFeatures.count(ExperimentalFeature::V050); - if (parameterTypes.size() != arguments->size()) { - if (arguments->size() == 0 && !v050) - m_errorReporter.warning( - _inheritance.location(), - "Wrong argument count for constructor call: " + - toString(arguments->size()) + - " arguments given but expected " + - toString(parameterTypes.size()) + - "." - ); - else - { - m_errorReporter.typeError( - _inheritance.location(), - "Wrong argument count for constructor call: " + - toString(arguments->size()) + - " arguments given but expected " + - toString(parameterTypes.size()) + - "." - ); - return; - } + m_errorReporter.typeError( + _inheritance.location(), + "Wrong argument count for constructor call: " + + toString(arguments->size()) + + " arguments given but expected " + + toString(parameterTypes.size()) + + ". Remove parentheses if you do not want to provide arguments here." + ); } - for (size_t i = 0; i < arguments->size(); ++i) + for (size_t i = 0; i < std::min(arguments->size(), parameterTypes.size()); ++i) if (!type(*(*arguments)[i])->isImplicitlyConvertibleTo(*parameterTypes[i])) m_errorReporter.typeError( (*arguments)[i]->location(), @@ -613,41 +650,44 @@ void TypeChecker::endVisit(UsingForDirective const& _usingFor) bool TypeChecker::visit(StructDefinition const& _struct) { - if (m_scope->contractKind() == ContractDefinition::ContractKind::Interface) - m_errorReporter.typeError(_struct.location(), "Structs cannot be defined in interfaces."); - for (ASTPointer<VariableDeclaration> const& member: _struct.members()) if (!type(*member)->canBeStored()) m_errorReporter.typeError(member->location(), "Type cannot be used in struct."); // Check recursion, fatal error if detected. - using StructPointer = StructDefinition const*; - using StructPointersSet = set<StructPointer>; - function<void(StructPointer,StructPointersSet const&)> check = [&](StructPointer _struct, StructPointersSet const& _parents) - { - if (_parents.count(_struct)) - m_errorReporter.fatalTypeError(_struct->location(), "Recursive struct definition."); - StructPointersSet parents = _parents; - parents.insert(_struct); - for (ASTPointer<VariableDeclaration> const& member: _struct->members()) - if (type(*member)->category() == Type::Category::Struct) + auto visitor = [&](StructDefinition const& _struct, CycleDetector<StructDefinition>& _cycleDetector, size_t _depth) + { + if (_depth >= 256) + m_errorReporter.fatalDeclarationError(_struct.location(), "Struct definition exhausting cyclic dependency validator."); + + for (ASTPointer<VariableDeclaration> const& member: _struct.members()) + { + Type const* memberType = type(*member).get(); + while (auto arrayType = dynamic_cast<ArrayType const*>(memberType)) { - auto const& typeName = dynamic_cast<UserDefinedTypeName const&>(*member->typeName()); - check(&dynamic_cast<StructDefinition const&>(*typeName.annotation().referencedDeclaration), parents); + if (arrayType->isDynamicallySized()) + break; + memberType = arrayType->baseType().get(); } + if (auto structType = dynamic_cast<StructType const*>(memberType)) + if (_cycleDetector.run(structType->structDefinition())) + return; + } }; - check(&_struct, StructPointersSet{}); + if (CycleDetector<StructDefinition>(visitor).run(_struct) != nullptr) + m_errorReporter.fatalTypeError(_struct.location(), "Recursive struct definition."); + bool insideStruct = true; + swap(insideStruct, m_insideStruct); ASTNode::listAccept(_struct.members(), *this); + m_insideStruct = insideStruct; return false; } bool TypeChecker::visit(FunctionDefinition const& _function) { - bool isLibraryFunction = - dynamic_cast<ContractDefinition const*>(_function.scope()) && - dynamic_cast<ContractDefinition const*>(_function.scope())->isLibrary(); + bool isLibraryFunction = _function.inContractKind() == ContractDefinition::ContractKind::Library; if (_function.isPayable()) { if (isLibraryFunction) @@ -657,7 +697,15 @@ bool TypeChecker::visit(FunctionDefinition const& _function) } for (ASTPointer<VariableDeclaration> const& var: _function.parameters() + _function.returnParameters()) { - if (!type(*var)->canLiveOutsideStorage()) + if ( + type(*var)->category() == Type::Category::Mapping && + !type(*var)->dataStoredIn(DataLocation::Storage) + ) + m_errorReporter.typeError(var->location(), "Mapping types can only have a data location of \"storage\"."); + else if ( + !type(*var)->canLiveOutsideStorage() && + _function.visibility() > FunctionDefinition::Visibility::Internal + ) m_errorReporter.typeError(var->location(), "Type is required to live outside storage."); if (_function.visibility() >= FunctionDefinition::Visibility::Public && !(type(*var)->interfaceType(isLibraryFunction))) m_errorReporter.fatalTypeError(var->location(), "Internal or recursive type is not allowed for public or external functions."); @@ -696,18 +744,10 @@ bool TypeChecker::visit(FunctionDefinition const& _function) { if (_function.isImplemented()) m_errorReporter.typeError(_function.location(), "Functions in interfaces cannot have an implementation."); - if (_function.sourceUnit().annotation().experimentalFeatures.count(ExperimentalFeature::V050)) - { - if (_function.visibility() != FunctionDefinition::Visibility::External) - m_errorReporter.typeError(_function.location(), "Functions in interfaces must be declared external."); - } - else - { - if (_function.visibility() < FunctionDefinition::Visibility::Public) - m_errorReporter.typeError(_function.location(), "Functions in interfaces cannot be internal or private."); - else if (_function.visibility() != FunctionDefinition::Visibility::External) - m_errorReporter.warning(_function.location(), "Functions in interfaces should be declared external."); - } + + if (_function.visibility() != FunctionDefinition::Visibility::External) + m_errorReporter.typeError(_function.location(), "Functions in interfaces must be declared external."); + if (_function.isConstructor()) m_errorReporter.typeError(_function.location(), "Constructor cannot be defined in interfaces."); } @@ -726,10 +766,12 @@ bool TypeChecker::visit(FunctionDefinition const& _function) bool TypeChecker::visit(VariableDeclaration const& _variable) { // Forbid any variable declarations inside interfaces unless they are part of - // a function's input/output parameters. + // * a function's input/output parameters, + // * or inside of a struct definition. if ( m_scope->contractKind() == ContractDefinition::ContractKind::Interface && !_variable.isCallableParameter() + && !m_insideStruct ) m_errorReporter.typeError(_variable.location(), "Variables cannot be declared in interfaces."); @@ -742,12 +784,11 @@ bool TypeChecker::visit(VariableDeclaration const& _variable) // TypeChecker at the VariableDeclarationStatement level. TypePointer varType = _variable.annotation().type; solAssert(!!varType, "Failed to infer variable type."); + if (_variable.value()) expectType(*_variable.value(), *varType); if (_variable.isConstant()) { - if (!_variable.isStateVariable()) - m_errorReporter.typeError(_variable.location(), "Illegal use of \"constant\" specifier."); if (!_variable.type()->isValueType()) { bool allowed = false; @@ -760,19 +801,10 @@ bool TypeChecker::visit(VariableDeclaration const& _variable) if (!_variable.value()) m_errorReporter.typeError(_variable.location(), "Uninitialized \"constant\" variable."); else if (!_variable.value()->annotation().isPure) - { - if (_variable.sourceUnit().annotation().experimentalFeatures.count(ExperimentalFeature::V050)) - m_errorReporter.typeError( - _variable.value()->location(), - "Initial value for constant variable has to be compile-time constant." - ); - else - m_errorReporter.warning( - _variable.value()->location(), - "Initial value for constant variable has to be compile-time constant. " - "This will fail to compile with the next breaking version change." - ); - } + m_errorReporter.typeError( + _variable.value()->location(), + "Initial value for constant variable has to be compile-time constant." + ); } if (!_variable.isStateVariable()) { @@ -786,7 +818,9 @@ bool TypeChecker::visit(VariableDeclaration const& _variable) ) m_errorReporter.typeError(_variable.location(), "Internal or recursive type is not allowed for public state variables."); - if (varType->category() == Type::Category::Array) + switch (varType->category()) + { + case Type::Category::Array: if (auto arrayType = dynamic_cast<ArrayType const*>(varType.get())) if ( ((arrayType->location() == DataLocation::Memory) || @@ -794,17 +828,22 @@ bool TypeChecker::visit(VariableDeclaration const& _variable) !arrayType->validForCalldata() ) m_errorReporter.typeError(_variable.location(), "Array is too large to be encoded."); + break; + case Type::Category::Mapping: + if (auto mappingType = dynamic_cast<MappingType const*>(varType.get())) + if ( + mappingType->keyType()->isDynamicallySized() && + _variable.visibility() == Declaration::Visibility::Public + ) + m_errorReporter.typeError(_variable.location(), "Dynamically-sized keys for public mappings are not supported."); + break; + default: + break; + } return false; } -bool TypeChecker::visit(EnumDefinition const& _enum) -{ - if (m_scope->contractKind() == ContractDefinition::ContractKind::Interface) - m_errorReporter.typeError(_enum.location(), "Enumerable cannot be declared in interfaces."); - return false; -} - void TypeChecker::visitManually( ModifierInvocation const& _modifier, vector<ContractDefinition const*> const& _bases @@ -913,9 +952,9 @@ bool TypeChecker::visit(InlineAssembly const& _inlineAssembly) { // External references have already been resolved in a prior stage and stored in the annotation. // We run the resolve step again regardless. - julia::ExternalIdentifierAccess::Resolver identifierAccess = [&]( + yul::ExternalIdentifierAccess::Resolver identifierAccess = [&]( assembly::Identifier const& _identifier, - julia::IdentifierContext _context, + yul::IdentifierContext _context, bool ) { @@ -924,6 +963,7 @@ bool TypeChecker::visit(InlineAssembly const& _inlineAssembly) return size_t(-1); Declaration const* declaration = ref->second.declaration; solAssert(!!declaration, ""); + bool requiresStorage = ref->second.isSlot || ref->second.isOffset; if (auto var = dynamic_cast<VariableDeclaration const*>(declaration)) { if (var->isConstant()) @@ -931,14 +971,14 @@ bool TypeChecker::visit(InlineAssembly const& _inlineAssembly) m_errorReporter.typeError(_identifier.location, "Constant variables not supported by inline assembly."); return size_t(-1); } - else if (ref->second.isSlot || ref->second.isOffset) + else if (requiresStorage) { if (!var->isStateVariable() && !var->type()->dataStoredIn(DataLocation::Storage)) { m_errorReporter.typeError(_identifier.location, "The suffixes _offset and _slot can only be used on storage variables."); return size_t(-1); } - else if (_context != julia::IdentifierContext::RValue) + else if (_context != yul::IdentifierContext::RValue) { m_errorReporter.typeError(_identifier.location, "Storage variables cannot be assigned to."); return size_t(-1); @@ -951,7 +991,7 @@ bool TypeChecker::visit(InlineAssembly const& _inlineAssembly) } else if (var->type()->dataStoredIn(DataLocation::Storage)) { - m_errorReporter.typeError(_identifier.location, "You have to use the _slot or _offset prefix to access storage reference variables."); + m_errorReporter.typeError(_identifier.location, "You have to use the _slot or _offset suffix to access storage reference variables."); return size_t(-1); } else if (var->type()->sizeOnStack() != 1) @@ -963,13 +1003,18 @@ bool TypeChecker::visit(InlineAssembly const& _inlineAssembly) return size_t(-1); } } - else if (_context == julia::IdentifierContext::LValue) + else if (requiresStorage) + { + m_errorReporter.typeError(_identifier.location, "The suffixes _offset and _slot can only be used on storage variables."); + return size_t(-1); + } + else if (_context == yul::IdentifierContext::LValue) { m_errorReporter.typeError(_identifier.location, "Only local variables can be assigned to in inline assembly."); return size_t(-1); } - if (_context == julia::IdentifierContext::RValue) + if (_context == yul::IdentifierContext::RValue) { solAssert(!!declaration->type(), "Type of declaration required but not yet determined."); if (dynamic_cast<FunctionDefinition const*>(declaration)) @@ -994,15 +1039,11 @@ bool TypeChecker::visit(InlineAssembly const& _inlineAssembly) }; solAssert(!_inlineAssembly.annotation().analysisInfo, ""); _inlineAssembly.annotation().analysisInfo = make_shared<assembly::AsmAnalysisInfo>(); - boost::optional<Error::Type> errorTypeForLoose = - m_scope->sourceUnit().annotation().experimentalFeatures.count(ExperimentalFeature::V050) ? - Error::Type::SyntaxError : - Error::Type::Warning; assembly::AsmAnalyzer analyzer( *_inlineAssembly.annotation().analysisInfo, m_errorReporter, m_evmVersion, - errorTypeForLoose, + Error::Type::SyntaxError, assembly::AsmFlavour::Loose, identifierAccess ); @@ -1041,9 +1082,13 @@ bool TypeChecker::visit(ForStatement const& _forStatement) void TypeChecker::endVisit(Return const& _return) { + ParameterList const* params = _return.annotation().functionReturnParameters; if (!_return.expression()) + { + if (params && !params->parameters().empty()) + m_errorReporter.typeError(_return.location(), "Return arguments required."); return; - ParameterList const* params = _return.annotation().functionReturnParameters; + } if (!params) { m_errorReporter.typeError(_return.location(), "Return arguments not allowed."); @@ -1094,29 +1139,87 @@ void TypeChecker::endVisit(EmitStatement const& _emit) m_insideEmitStatement = false; } +namespace +{ +/** + * @returns a suggested left-hand-side of a multi-variable declaration contairing + * the variable declarations given in @a _decls. + */ +string createTupleDecl(vector<ASTPointer<VariableDeclaration>> const& _decls) +{ + vector<string> components; + for (ASTPointer<VariableDeclaration> const& decl: _decls) + if (decl) + { + solAssert(decl->annotation().type, ""); + components.emplace_back(decl->annotation().type->toString(false) + " " + decl->name()); + } + else + components.emplace_back(); + + if (_decls.size() == 1) + return components.front(); + else + return "(" + boost::algorithm::join(components, ", ") + ")"; +} + +bool typeCanBeExpressed(vector<ASTPointer<VariableDeclaration>> const& decls) +{ + for (ASTPointer<VariableDeclaration> const& decl: decls) + { + // skip empty tuples (they can be expressed of course) + if (!decl) + continue; + + if (!decl->annotation().type) + return false; + + if (auto functionType = dynamic_cast<FunctionType const*>(decl->annotation().type.get())) + if ( + functionType->kind() != FunctionType::Kind::Internal && + functionType->kind() != FunctionType::Kind::External + ) + return false; + } + + return true; +} +} + bool TypeChecker::visit(VariableDeclarationStatement const& _statement) { - bool const v050 = m_scope->sourceUnit().annotation().experimentalFeatures.count(ExperimentalFeature::V050); if (!_statement.initialValue()) { // No initial value is only permitted for single variables with specified type. if (_statement.declarations().size() != 1 || !_statement.declarations().front()) - m_errorReporter.fatalTypeError(_statement.location(), "Assignment necessary for type detection."); + { + if (boost::algorithm::all_of_equal(_statement.declarations(), nullptr)) + { + // The syntax checker has already generated an error for this case (empty LHS tuple). + solAssert(m_errorReporter.hasErrors(), ""); + + // It is okay to return here, as there are no named components on the + // left-hand-side that could cause any damage later. + return false; + } + else + // Bailing out *fatal* here, as those (untyped) vars may be used later, and diagnostics wouldn't be helpful then. + m_errorReporter.fatalTypeError(_statement.location(), "Use of the \"var\" keyword is disallowed."); + } + VariableDeclaration const& varDecl = *_statement.declarations().front(); if (!varDecl.annotation().type) - m_errorReporter.fatalTypeError(_statement.location(), "Assignment necessary for type detection."); + m_errorReporter.fatalTypeError(_statement.location(), "Use of the \"var\" keyword is disallowed."); + if (auto ref = dynamic_cast<ReferenceType const*>(type(varDecl).get())) { if (ref->dataStoredIn(DataLocation::Storage)) { string errorText{"Uninitialized storage pointer."}; - if (varDecl.referenceLocation() == VariableDeclaration::Location::Default) + if (varDecl.referenceLocation() == VariableDeclaration::Location::Unspecified) errorText += " Did you mean '<type> memory " + varDecl.name() + "'?"; solAssert(m_scope, ""); - if (v050) - m_errorReporter.declarationError(varDecl.location(), errorText); - else - m_errorReporter.warning(varDecl.location(), errorText); + m_errorReporter.declarationError(varDecl.location(), errorText); } } else if (dynamic_cast<MappingType const*>(type(varDecl).get())) @@ -1138,85 +1241,34 @@ bool TypeChecker::visit(VariableDeclarationStatement const& _statement) else valueTypes = TypePointers{type(*_statement.initialValue())}; - // Determine which component is assigned to which variable. - // If numbers do not match, fill up if variables begin or end empty (not both). - vector<VariableDeclaration const*>& assignments = _statement.annotation().assignments; - assignments.resize(valueTypes.size(), nullptr); vector<ASTPointer<VariableDeclaration>> const& variables = _statement.declarations(); if (variables.empty()) - { - if (!valueTypes.empty()) - m_errorReporter.fatalTypeError( - _statement.location(), - "Too many components (" + - toString(valueTypes.size()) + - ") in value for variable assignment (0) needed" - ); - } + // We already have an error for this in the SyntaxChecker. + solAssert(m_errorReporter.hasErrors(), ""); else if (valueTypes.size() != variables.size()) - { - if (v050) - m_errorReporter.fatalTypeError( - _statement.location(), - "Different number of components on the left hand side (" + - toString(variables.size()) + - ") than on the right hand side (" + - toString(valueTypes.size()) + - ")." - ); - else if (!variables.front() && !variables.back()) - m_errorReporter.fatalTypeError( - _statement.location(), - "Wildcard both at beginning and end of variable declaration list is only allowed " - "if the number of components is equal." - ); - else - m_errorReporter.warning( - _statement.location(), - "Different number of components on the left hand side (" + - toString(variables.size()) + - ") than on the right hand side (" + - toString(valueTypes.size()) + - ")." - ); - } - size_t minNumValues = variables.size(); - if (!variables.empty() && (!variables.back() || !variables.front())) - --minNumValues; - if (valueTypes.size() < minNumValues) - m_errorReporter.fatalTypeError( - _statement.location(), - "Not enough components (" + - toString(valueTypes.size()) + - ") in value to assign all variables (" + - toString(minNumValues) + ")." - ); - if (valueTypes.size() > variables.size() && variables.front() && variables.back()) - m_errorReporter.fatalTypeError( + m_errorReporter.typeError( _statement.location(), - "Too many components (" + + "Different number of components on the left hand side (" + + toString(variables.size()) + + ") than on the right hand side (" + toString(valueTypes.size()) + - ") in value for variable assignment (" + - toString(minNumValues) + - " needed)." + ")." ); - bool fillRight = !variables.empty() && (!variables.back() || variables.front()); - for (size_t i = 0; i < min(variables.size(), valueTypes.size()); ++i) - if (fillRight) - assignments[i] = variables[i].get(); - else - assignments[assignments.size() - i - 1] = variables[variables.size() - i - 1].get(); - for (size_t i = 0; i < assignments.size(); ++i) + bool autoTypeDeductionNeeded = false; + + for (size_t i = 0; i < min(variables.size(), valueTypes.size()); ++i) { - if (!assignments[i]) + if (!variables[i]) continue; - VariableDeclaration const& var = *assignments[i]; + VariableDeclaration const& var = *variables[i]; solAssert(!var.value(), "Value has to be tied to statement."); TypePointer const& valueComponentType = valueTypes[i]; solAssert(!!valueComponentType, ""); if (!var.annotation().type) { + autoTypeDeductionNeeded = true; + // Infer type from value. solAssert(!var.typeName(), ""); var.annotation().type = valueComponentType->mobileType(); @@ -1260,14 +1312,6 @@ bool TypeChecker::visit(VariableDeclarationStatement const& _statement) } else solAssert(dynamic_cast<FixedPointType const*>(var.annotation().type.get()), "Unknown type."); - - m_errorReporter.warning( - _statement.location(), - "The type of this variable was inferred as " + - typeName + - extension + - ". This is probably not desired. Use an explicit type to silence this warning." - ); } var.accept(*this); @@ -1304,6 +1348,23 @@ bool TypeChecker::visit(VariableDeclarationStatement const& _statement) } } } + + if (autoTypeDeductionNeeded) + { + if (!typeCanBeExpressed(variables)) + m_errorReporter.syntaxError( + _statement.location(), + "Use of the \"var\" keyword is disallowed. " + "Type cannot be expressed in syntax." + ); + else + m_errorReporter.syntaxError( + _statement.location(), + "Use of the \"var\" keyword is disallowed. " + "Use explicit declaration `" + createTupleDecl(variables) + " = ...´ instead." + ); + } + return false; } @@ -1321,7 +1382,8 @@ void TypeChecker::endVisit(ExpressionStatement const& _statement) if ( kind == FunctionType::Kind::BareCall || kind == FunctionType::Kind::BareCallCode || - kind == FunctionType::Kind::BareDelegateCall + kind == FunctionType::Kind::BareDelegateCall || + kind == FunctionType::Kind::BareStaticCall ) m_errorReporter.warning(_statement.location(), "Return value of low-level calls not used."); else if (kind == FunctionType::Kind::Send) @@ -1375,12 +1437,45 @@ bool TypeChecker::visit(Conditional const& _conditional) return false; } +void TypeChecker::checkExpressionAssignment(Type const& _type, Expression const& _expression) +{ + if (auto const* tupleExpression = dynamic_cast<TupleExpression const*>(&_expression)) + { + auto const* tupleType = dynamic_cast<TupleType const*>(&_type); + auto const& types = tupleType ? tupleType->components() : vector<TypePointer> { _type.shared_from_this() }; + + solAssert( + tupleExpression->components().size() == types.size() || m_errorReporter.hasErrors(), + "Array sizes don't match or no errors generated." + ); + + for (size_t i = 0; i < min(tupleExpression->components().size(), types.size()); i++) + if (types[i]) + { + solAssert(!!tupleExpression->components()[i], ""); + checkExpressionAssignment(*types[i], *tupleExpression->components()[i]); + } + } + else if (_type.category() == Type::Category::Mapping) + { + bool isLocalOrReturn = false; + if (auto const* identifier = dynamic_cast<Identifier const*>(&_expression)) + if (auto const *variableDeclaration = dynamic_cast<VariableDeclaration const*>(identifier->annotation().referencedDeclaration)) + if (variableDeclaration->isLocalOrReturn()) + isLocalOrReturn = true; + if (!isLocalOrReturn) + m_errorReporter.typeError(_expression.location(), "Mappings cannot be assigned to."); + } +} + bool TypeChecker::visit(Assignment const& _assignment) { - bool const v050 = m_scope->sourceUnit().annotation().experimentalFeatures.count(ExperimentalFeature::V050); requireLValue(_assignment.leftHandSide()); TypePointer t = type(_assignment.leftHandSide()); _assignment.annotation().type = t; + + checkExpressionAssignment(*t, _assignment.leftHandSide()); + if (TupleType const* tupleType = dynamic_cast<TupleType const*>(t.get())) { if (_assignment.assignmentOperator() != Token::Assign) @@ -1394,30 +1489,8 @@ bool TypeChecker::visit(Assignment const& _assignment) expectType(_assignment.rightHandSide(), *tupleType); // expectType does not cause fatal errors, so we have to check again here. - if (TupleType const* rhsType = dynamic_cast<TupleType const*>(type(_assignment.rightHandSide()).get())) - { + if (dynamic_cast<TupleType const*>(type(_assignment.rightHandSide()).get())) checkDoubleStorageAssignment(_assignment); - // @todo For 0.5.0, this code shoud move to TupleType::isImplicitlyConvertibleTo, - // but we cannot do it right now. - if (rhsType->components().size() != tupleType->components().size()) - { - string message = - "Different number of components on the left hand side (" + - toString(tupleType->components().size()) + - ") than on the right hand side (" + - toString(rhsType->components().size()) + - ")."; - if (v050) - m_errorReporter.typeError(_assignment.location(), message); - else - m_errorReporter.warning(_assignment.location(), message); - } - } - } - else if (t->category() == Type::Category::Mapping) - { - m_errorReporter.typeError(_assignment.location(), "Mappings cannot be assigned to."); - _assignment.rightHandSide().accept(*this); } else if (_assignment.assignmentOperator() == Token::Assign) expectType(_assignment.rightHandSide(), *t); @@ -1426,14 +1499,14 @@ bool TypeChecker::visit(Assignment const& _assignment) // compound assignment _assignment.rightHandSide().accept(*this); TypePointer resultType = t->binaryOperatorResult( - Token::AssignmentToBinaryOp(_assignment.assignmentOperator()), + TokenTraits::AssignmentToBinaryOp(_assignment.assignmentOperator()), type(_assignment.rightHandSide()) ); if (!resultType || *resultType != *t) m_errorReporter.typeError( _assignment.location(), "Operator " + - string(Token::toString(_assignment.assignmentOperator())) + + string(TokenTraits::toString(_assignment.assignmentOperator())) + " not compatible with types " + t->toString() + " and " + @@ -1469,14 +1542,12 @@ bool TypeChecker::visit(TupleExpression const& _tuple) } else { - bool const v050 = m_scope->sourceUnit().annotation().experimentalFeatures.count(ExperimentalFeature::V050); bool isPure = true; TypePointer inlineArrayType; for (size_t i = 0; i < components.size(); ++i) { - // Outside of an lvalue-context, the only situation where a component can be empty is (x,). - if (!components[i] && !(i == 1 && components.size() == 2)) + if (!components[i]) m_errorReporter.fatalTypeError(_tuple.location(), "Tuple component cannot be empty."); else if (components[i]) { @@ -1488,10 +1559,7 @@ bool TypeChecker::visit(TupleExpression const& _tuple) { if (_tuple.isInlineArray()) m_errorReporter.fatalTypeError(components[i]->location(), "Array component cannot be empty."); - if (v050) - m_errorReporter.fatalTypeError(components[i]->location(), "Tuple component cannot be empty."); - else - m_errorReporter.warning(components[i]->location(), "Tuple component cannot be empty."); + m_errorReporter.typeError(components[i]->location(), "Tuple component cannot be empty."); } // Note: code generation will visit each of the expression even if they are not assigned from. @@ -1529,11 +1597,7 @@ bool TypeChecker::visit(TupleExpression const& _tuple) if (components.size() == 1) _tuple.annotation().type = type(*components[0]); else - { - if (components.size() == 2 && !components[1]) - types.pop_back(); _tuple.annotation().type = make_shared<TupleType>(types); - } } } @@ -1543,8 +1607,8 @@ bool TypeChecker::visit(TupleExpression const& _tuple) bool TypeChecker::visit(UnaryOperation const& _operation) { // Inc, Dec, Add, Sub, Not, BitNot, Delete - Token::Value op = _operation.getOperator(); - bool const modifying = (op == Token::Value::Inc || op == Token::Value::Dec || op == Token::Value::Delete); + Token op = _operation.getOperator(); + bool const modifying = (op == Token::Inc || op == Token::Dec || op == Token::Delete); if (modifying) requireLValue(_operation.subExpression()); else @@ -1556,7 +1620,7 @@ bool TypeChecker::visit(UnaryOperation const& _operation) m_errorReporter.typeError( _operation.location(), "Unary operator " + - string(Token::toString(op)) + + string(TokenTraits::toString(op)) + " cannot be applied to type " + subExprType->toString() ); @@ -1577,7 +1641,7 @@ void TypeChecker::endVisit(BinaryOperation const& _operation) m_errorReporter.typeError( _operation.location(), "Operator " + - string(Token::toString(_operation.getOperator())) + + string(TokenTraits::toString(_operation.getOperator())) + " not compatible with types " + leftType->toString() + " and " + @@ -1587,7 +1651,7 @@ void TypeChecker::endVisit(BinaryOperation const& _operation) } _operation.annotation().commonType = commonType; _operation.annotation().type = - Token::isCompareOp(_operation.getOperator()) ? + TokenTraits::isCompareOp(_operation.getOperator()) ? make_shared<BoolType>() : commonType; _operation.annotation().isPure = @@ -1617,61 +1681,91 @@ void TypeChecker::endVisit(BinaryOperation const& _operation) } } -bool TypeChecker::visit(FunctionCall const& _functionCall) +TypePointer TypeChecker::typeCheckTypeConversionAndRetrieveReturnType( + FunctionCall const& _functionCall +) { - bool isPositionalCall = _functionCall.names().empty(); - vector<ASTPointer<Expression const>> arguments = _functionCall.arguments(); - vector<ASTPointer<ASTString>> const& argumentNames = _functionCall.names(); - - bool isPure = true; - - // We need to check arguments' type first as they will be needed for overload resolution. - shared_ptr<TypePointers> argumentTypes; - if (isPositionalCall) - argumentTypes = make_shared<TypePointers>(); - for (ASTPointer<Expression const> const& argument: arguments) - { - argument->accept(*this); - if (!argument->annotation().isPure) - isPure = false; - // only store them for positional calls - if (isPositionalCall) - argumentTypes->push_back(type(*argument)); - } - if (isPositionalCall) - _functionCall.expression().annotation().argumentTypes = move(argumentTypes); + solAssert(_functionCall.annotation().kind == FunctionCallKind::TypeConversion, ""); + TypePointer const& expressionType = type(_functionCall.expression()); - _functionCall.expression().accept(*this); - TypePointer expressionType = type(_functionCall.expression()); + vector<ASTPointer<Expression const>> const& arguments = _functionCall.arguments(); + bool const isPositionalCall = _functionCall.names().empty(); - if (auto const* typeType = dynamic_cast<TypeType const*>(expressionType.get())) - { - if (typeType->actualType()->category() == Type::Category::Struct) - _functionCall.annotation().kind = FunctionCallKind::StructConstructorCall; - else - _functionCall.annotation().kind = FunctionCallKind::TypeConversion; - - } + TypePointer resultType = dynamic_cast<TypeType const&>(*expressionType).actualType(); + if (arguments.size() != 1) + m_errorReporter.typeError( + _functionCall.location(), + "Exactly one argument expected for explicit type conversion." + ); + else if (!isPositionalCall) + m_errorReporter.typeError( + _functionCall.location(), + "Type conversion cannot allow named arguments." + ); else - _functionCall.annotation().kind = FunctionCallKind::FunctionCall; - solAssert(_functionCall.annotation().kind != FunctionCallKind::Unset, ""); - - if (_functionCall.annotation().kind == FunctionCallKind::TypeConversion) { - TypeType const& t = dynamic_cast<TypeType const&>(*expressionType); - TypePointer resultType = t.actualType(); - if (arguments.size() != 1) - m_errorReporter.typeError(_functionCall.location(), "Exactly one argument expected for explicit type conversion."); - else if (!isPositionalCall) - m_errorReporter.typeError(_functionCall.location(), "Type conversion cannot allow named arguments."); + TypePointer const& argType = type(*arguments.front()); + // Resulting data location is memory unless we are converting from a reference + // type with a different data location. + // (data location cannot yet be specified for type conversions) + DataLocation dataLoc = DataLocation::Memory; + if (auto argRefType = dynamic_cast<ReferenceType const*>(argType.get())) + dataLoc = argRefType->location(); + if (auto type = dynamic_cast<ReferenceType const*>(resultType.get())) + resultType = type->copyForLocation(dataLoc, type->isPointer()); + if (argType->isExplicitlyConvertibleTo(*resultType)) + { + if (auto argArrayType = dynamic_cast<ArrayType const*>(argType.get())) + { + auto resultArrayType = dynamic_cast<ArrayType const*>(resultType.get()); + solAssert(!!resultArrayType, ""); + solAssert( + argArrayType->location() != DataLocation::Storage || + ( + ( + resultArrayType->isPointer() || + (argArrayType->isByteArray() && resultArrayType->isByteArray()) + ) && + resultArrayType->location() == DataLocation::Storage + ), + "Invalid explicit conversion to storage type." + ); + } + } else { - TypePointer const& argType = type(*arguments.front()); - if (auto argRefType = dynamic_cast<ReferenceType const*>(argType.get())) - // do not change the data location when converting - // (data location cannot yet be specified for type conversions) - resultType = ReferenceType::copyForLocationIfReference(argRefType->location(), resultType); - if (!argType->isExplicitlyConvertibleTo(*resultType)) + if ( + resultType->category() == Type::Category::Contract && + argType->category() == Type::Category::Address + ) + { + solAssert(dynamic_cast<ContractType const*>(resultType.get())->isPayable(), ""); + solAssert( + dynamic_cast<AddressType const*>(argType.get())->stateMutability() < + StateMutability::Payable, + "" + ); + SecondarySourceLocation ssl; + if ( + auto const* identifier = dynamic_cast<Identifier const*>(arguments.front().get()) + ) + if ( + auto const* variableDeclaration = dynamic_cast<VariableDeclaration const*>( + identifier->annotation().referencedDeclaration + ) + ) + ssl.append( + "Did you mean to declare this variable as \"address payable\"?", + variableDeclaration->location() + ); + m_errorReporter.typeError( + _functionCall.location(), ssl, + "Explicit type conversion not allowed from non-payable \"address\" to \"" + + resultType->toString() + + "\", which has a payable fallback function." + ); + } + else m_errorReporter.typeError( _functionCall.location(), "Explicit type conversion not allowed from \"" + @@ -1681,267 +1775,475 @@ bool TypeChecker::visit(FunctionCall const& _functionCall) "\"." ); } - _functionCall.annotation().type = resultType; - _functionCall.annotation().isPure = isPure; - - return false; + if (resultType->category() == Type::Category::Address) + { + bool const payable = argType->isExplicitlyConvertibleTo(AddressType::addressPayable()); + resultType = make_shared<AddressType>( + payable ? StateMutability::Payable : StateMutability::NonPayable + ); + } } + return resultType; +} +void TypeChecker::typeCheckFunctionCall( + FunctionCall const& _functionCall, + FunctionTypePointer _functionType +) +{ // Actual function call or struct constructor call. - FunctionTypePointer functionType; + solAssert(!!_functionType, ""); + solAssert(_functionType->kind() != FunctionType::Kind::ABIDecode, ""); + + // Check for unsupported use of bare static call + if ( + _functionType->kind() == FunctionType::Kind::BareStaticCall && + !m_evmVersion.hasStaticCall() + ) + m_errorReporter.typeError( + _functionCall.location(), + "\"staticcall\" is not supported by the VM version." + ); - /// For error message: Struct members that were removed during conversion to memory. - set<string> membersRemovedForStructConstructor; - if (_functionCall.annotation().kind == FunctionCallKind::StructConstructorCall) + // Check for deprecated function names + if (_functionType->kind() == FunctionType::Kind::KECCAK256) { - TypeType const& t = dynamic_cast<TypeType const&>(*expressionType); - auto const& structType = dynamic_cast<StructType const&>(*t.actualType()); - functionType = structType.constructorType(); - membersRemovedForStructConstructor = structType.membersMissingInMemory(); - _functionCall.annotation().isPure = isPure; + if (auto functionName = dynamic_cast<Identifier const*>(&_functionCall.expression())) + if (functionName->name() == "sha3") + m_errorReporter.typeError( + _functionCall.location(), + "\"sha3\" has been deprecated in favour of \"keccak256\"" + ); } - else if ((functionType = dynamic_pointer_cast<FunctionType const>(expressionType))) - _functionCall.annotation().isPure = - isPure && - _functionCall.expression().annotation().isPure && - functionType->isPure(); - - bool allowDynamicTypes = m_evmVersion.supportsReturndata(); - if (!functionType) + else if (_functionType->kind() == FunctionType::Kind::Selfdestruct) { - m_errorReporter.typeError(_functionCall.location(), "Type is not callable"); - _functionCall.annotation().type = make_shared<TupleType>(); - return false; + if (auto functionName = dynamic_cast<Identifier const*>(&_functionCall.expression())) + if (functionName->name() == "suicide") + m_errorReporter.typeError( + _functionCall.location(), + "\"suicide\" has been deprecated in favour of \"selfdestruct\"" + ); } - auto returnTypes = - allowDynamicTypes ? - functionType->returnParameterTypes() : - functionType->returnParameterTypesWithoutDynamicTypes(); - if (returnTypes.size() == 1) - _functionCall.annotation().type = returnTypes.front(); - else - _functionCall.annotation().type = make_shared<TupleType>(returnTypes); + // Check for event outside of emit statement + if (!m_insideEmitStatement && _functionType->kind() == FunctionType::Kind::Event) + m_errorReporter.typeError( + _functionCall.location(), + "Event invocations have to be prefixed by \"emit\"." + ); + + // Perform standard function call type checking + typeCheckFunctionGeneralChecks(_functionCall, _functionType); +} - bool const v050 = m_scope->sourceUnit().annotation().experimentalFeatures.count(ExperimentalFeature::V050); +void TypeChecker::typeCheckABIEncodeFunctions( + FunctionCall const& _functionCall, + FunctionTypePointer _functionType +) +{ + solAssert(!!_functionType, ""); + solAssert( + _functionType->kind() == FunctionType::Kind::ABIEncode || + _functionType->kind() == FunctionType::Kind::ABIEncodePacked || + _functionType->kind() == FunctionType::Kind::ABIEncodeWithSelector || + _functionType->kind() == FunctionType::Kind::ABIEncodeWithSignature, + "ABI function has unexpected FunctionType::Kind." + ); + solAssert(_functionType->takesArbitraryParameters(), "ABI functions should be variadic."); + + bool const isPacked = _functionType->kind() == FunctionType::Kind::ABIEncodePacked; + solAssert(_functionType->padArguments() != isPacked, "ABI function with unexpected padding"); - if (auto functionName = dynamic_cast<Identifier const*>(&_functionCall.expression())) + bool const abiEncoderV2 = m_scope->sourceUnit().annotation().experimentalFeatures.count( + ExperimentalFeature::ABIEncoderV2 + ); + + // Check for named arguments + if (!_functionCall.names().empty()) { - string msg; - if (functionName->name() == "sha3" && functionType->kind() == FunctionType::Kind::SHA3) - msg = "\"sha3\" has been deprecated in favour of \"keccak256\""; - else if (functionName->name() == "suicide" && functionType->kind() == FunctionType::Kind::Selfdestruct) - msg = "\"suicide\" has been deprecated in favour of \"selfdestruct\""; - if (!msg.empty()) + m_errorReporter.typeError( + _functionCall.location(), + "Named arguments cannot be used for functions that take arbitrary parameters." + ); + return; + } + + // Perform standard function call type checking + typeCheckFunctionGeneralChecks(_functionCall, _functionType); + + // Check additional arguments for variadic functions + vector<ASTPointer<Expression const>> const& arguments = _functionCall.arguments(); + for (size_t i = 0; i < arguments.size(); ++i) + { + auto const& argType = type(*arguments[i]); + + if (argType->category() == Type::Category::RationalNumber) { - if (v050) - m_errorReporter.typeError(_functionCall.location(), msg); - else - m_errorReporter.warning(_functionCall.location(), msg); + if (!argType->mobileType()) + { + m_errorReporter.typeError( + arguments[i]->location(), + "Invalid rational number (too large or division by zero)." + ); + continue; + } + else if (isPacked) + { + m_errorReporter.typeError( + arguments[i]->location(), + "Cannot perform packed encoding for a literal." + " Please convert it to an explicit type first." + ); + continue; + } } + + if (!argType->fullEncodingType(false, abiEncoderV2, !_functionType->padArguments())) + m_errorReporter.typeError( + arguments[i]->location(), + "This type cannot be encoded." + ); } - if (!m_insideEmitStatement && functionType->kind() == FunctionType::Kind::Event) +} + +void TypeChecker::typeCheckFunctionGeneralChecks( + FunctionCall const& _functionCall, + FunctionTypePointer _functionType +) +{ + // Actual function call or struct constructor call. + + solAssert(!!_functionType, ""); + solAssert(_functionType->kind() != FunctionType::Kind::ABIDecode, ""); + + bool const isPositionalCall = _functionCall.names().empty(); + bool const isVariadic = _functionType->takesArbitraryParameters(); + + solAssert( + !isVariadic || _functionCall.annotation().kind == FunctionCallKind::FunctionCall, + "Struct constructor calls cannot be variadic." + ); + + TypePointers const& parameterTypes = _functionType->parameterTypes(); + vector<ASTPointer<Expression const>> const& arguments = _functionCall.arguments(); + vector<ASTPointer<ASTString>> const& argumentNames = _functionCall.names(); + + // Check number of passed in arguments + if ( + arguments.size() < parameterTypes.size() || + (!isVariadic && arguments.size() > parameterTypes.size()) + ) { - if (m_scope->sourceUnit().annotation().experimentalFeatures.count(ExperimentalFeature::V050)) - m_errorReporter.typeError(_functionCall.location(), "Event invocations have to be prefixed by \"emit\"."); + bool const isStructConstructorCall = + _functionCall.annotation().kind == FunctionCallKind::StructConstructorCall; + + string msg; + + if (isVariadic) + msg += + "Need at least " + + toString(parameterTypes.size()) + + " arguments for " + + string(isStructConstructorCall ? "struct constructor" : "function call") + + ", but provided only " + + toString(arguments.size()) + + "."; else - m_errorReporter.warning(_functionCall.location(), "Invoking events without \"emit\" prefix is deprecated."); + msg += + "Wrong argument count for " + + string(isStructConstructorCall ? "struct constructor" : "function call") + + ": " + + toString(arguments.size()) + + " arguments given but " + + string(isVariadic ? "need at least " : "expected ") + + toString(parameterTypes.size()) + + "."; + + // Extend error message in case we try to construct a struct with mapping member. + if (isStructConstructorCall) + { + /// For error message: Struct members that were removed during conversion to memory. + TypePointer const expressionType = type(_functionCall.expression()); + TypeType const& t = dynamic_cast<TypeType const&>(*expressionType); + auto const& structType = dynamic_cast<StructType const&>(*t.actualType()); + set<string> membersRemovedForStructConstructor = structType.membersMissingInMemory(); + + if (!membersRemovedForStructConstructor.empty()) + { + msg += " Members that have to be skipped in memory:"; + for (auto const& member: membersRemovedForStructConstructor) + msg += " " + member; + } + } + else if ( + _functionType->kind() == FunctionType::Kind::BareCall || + _functionType->kind() == FunctionType::Kind::BareCallCode || + _functionType->kind() == FunctionType::Kind::BareDelegateCall || + _functionType->kind() == FunctionType::Kind::BareStaticCall + ) + { + if (arguments.empty()) + msg += + " This function requires a single bytes argument." + " Use \"\" as argument to provide empty calldata."; + else + msg += + " This function requires a single bytes argument." + " If all your arguments are value types, you can use" + " abi.encode(...) to properly generate it."; + } + else if ( + _functionType->kind() == FunctionType::Kind::KECCAK256 || + _functionType->kind() == FunctionType::Kind::SHA256 || + _functionType->kind() == FunctionType::Kind::RIPEMD160 + ) + msg += + " This function requires a single bytes argument." + " Use abi.encodePacked(...) to obtain the pre-0.5.0" + " behaviour or abi.encode(...) to use ABI encoding."; + m_errorReporter.typeError(_functionCall.location(), msg); + return; } - TypePointers parameterTypes = functionType->parameterTypes(); + // Parameter to argument map + std::vector<Expression const*> paramArgMap(parameterTypes.size()); - if (!functionType->padArguments()) + // Map parameters to arguments - trivially for positional calls, less so for named calls + if (isPositionalCall) + for (size_t i = 0; i < paramArgMap.size(); ++i) + paramArgMap[i] = arguments[i].get(); + else { - for (size_t i = 0; i < arguments.size(); ++i) + auto const& parameterNames = _functionType->parameterNames(); + + // Check for expected number of named arguments + if (parameterNames.size() != argumentNames.size()) { - auto const& argType = type(*arguments[i]); - if (auto literal = dynamic_cast<RationalNumberType const*>(argType.get())) - { - /* If no mobile type is available an error will be raised elsewhere. */ - if (literal->mobileType()) - { - if (v050) + m_errorReporter.typeError( + _functionCall.location(), + parameterNames.size() > argumentNames.size() ? + "Some argument names are missing." : + "Too many arguments." + ); + return; + } + + // Check for duplicate argument names + { + bool duplication = false; + for (size_t i = 0; i < argumentNames.size(); i++) + for (size_t j = i + 1; j < argumentNames.size(); j++) + if (*argumentNames[i] == *argumentNames[j]) + { + duplication = true; m_errorReporter.typeError( arguments[i]->location(), - "Cannot perform packed encoding for a literal. Please convert it to an explicit type first." - ); - else - m_errorReporter.warning( - arguments[i]->location(), - "The type of \"" + - argType->toString() + - "\" was inferred as " + - literal->mobileType()->toString() + - ". This is probably not desired. Use an explicit type to silence this warning." + "Duplicate named argument \"" + *argumentNames[i] + "\"." ); + } + if (duplication) + return; + } + + // map parameter names to argument names + { + bool not_all_mapped = false; + + for (size_t i = 0; i < paramArgMap.size(); i++) + { + size_t j; + for (j = 0; j < argumentNames.size(); j++) + if (parameterNames[i] == *argumentNames[j]) + break; + + if (j < argumentNames.size()) + paramArgMap[i] = arguments[j].get(); + else + { + paramArgMap[i] = nullptr; + not_all_mapped = true; + m_errorReporter.typeError( + _functionCall.location(), + "Named argument \"" + + *argumentNames[i] + + "\" does not match function declaration." + ); } } + + if (not_all_mapped) + return; } } - if (functionType->takesSinglePackedBytesParameter()) + // Check for compatible types between arguments and parameters + for (size_t i = 0; i < paramArgMap.size(); ++i) { - if ( - (arguments.size() > 1) || - (arguments.size() == 1 && !type(*arguments.front())->isImplicitlyConvertibleTo(ArrayType(DataLocation::Memory))) - ) + solAssert(!!paramArgMap[i], "unmapped parameter"); + if (!type(*paramArgMap[i])->isImplicitlyConvertibleTo(*parameterTypes[i])) { string msg = - "This function only accepts a single \"bytes\" argument. Please use " - "\"abi.encodePacked(...)\" or a similar function to encode the data."; - if (v050) - m_errorReporter.typeError(_functionCall.location(), msg); - else - m_errorReporter.warning(_functionCall.location(), msg); + "Invalid type for argument in function call. " + "Invalid implicit conversion from " + + type(*paramArgMap[i])->toString() + + " to " + + parameterTypes[i]->toString() + + " requested."; + if ( + _functionType->kind() == FunctionType::Kind::BareCall || + _functionType->kind() == FunctionType::Kind::BareCallCode || + _functionType->kind() == FunctionType::Kind::BareDelegateCall || + _functionType->kind() == FunctionType::Kind::BareStaticCall + ) + msg += + " This function requires a single bytes argument." + " If all your arguments are value types, you can" + " use abi.encode(...) to properly generate it."; + else if ( + _functionType->kind() == FunctionType::Kind::KECCAK256 || + _functionType->kind() == FunctionType::Kind::SHA256 || + _functionType->kind() == FunctionType::Kind::RIPEMD160 + ) + msg += + " This function requires a single bytes argument." + " Use abi.encodePacked(...) to obtain the pre-0.5.0" + " behaviour or abi.encode(...) to use ABI encoding."; + m_errorReporter.typeError(paramArgMap[i]->location(), msg); } + } +} - if (arguments.size() == 1 && !type(*arguments.front())->isImplicitlyConvertibleTo(ArrayType(DataLocation::Memory))) - { - string msg = - "The provided argument of type " + - type(*arguments.front())->toString() + - " is not implicitly convertible to expected type bytes memory."; - if (v050) - m_errorReporter.typeError(_functionCall.location(), msg); - else - m_errorReporter.warning(_functionCall.location(), msg); - } +bool TypeChecker::visit(FunctionCall const& _functionCall) +{ + vector<ASTPointer<Expression const>> const& arguments = _functionCall.arguments(); + bool argumentsArePure = true; + + // We need to check arguments' type first as they will be needed for overload resolution. + for (ASTPointer<Expression const> const& argument: arguments) + { + argument->accept(*this); + if (!argument->annotation().isPure) + argumentsArePure = false; } - if (functionType->takesArbitraryParameters() && arguments.size() < parameterTypes.size()) + // For positional calls only, store argument types + if (_functionCall.names().empty()) { - solAssert(_functionCall.annotation().kind == FunctionCallKind::FunctionCall, ""); - m_errorReporter.typeError( - _functionCall.location(), - "Need at least " + - toString(parameterTypes.size()) + - " arguments for function call, but provided only " + - toString(arguments.size()) + - "." - ); + shared_ptr<TypePointers> argumentTypes = make_shared<TypePointers>(); + for (ASTPointer<Expression const> const& argument: arguments) + argumentTypes->push_back(type(*argument)); + _functionCall.expression().annotation().argumentTypes = move(argumentTypes); } - else if (!functionType->takesArbitraryParameters() && parameterTypes.size() != arguments.size()) + + _functionCall.expression().accept(*this); + + TypePointer const& expressionType = type(_functionCall.expression()); + + // Determine function call kind and function type for this FunctionCall node + FunctionCallAnnotation& funcCallAnno = _functionCall.annotation(); + FunctionTypePointer functionType; + + // Determine and assign function call kind, purity and function type for this FunctionCall node + switch (expressionType->category()) { - bool isStructConstructorCall = _functionCall.annotation().kind == FunctionCallKind::StructConstructorCall; + case Type::Category::Function: + functionType = dynamic_pointer_cast<FunctionType const>(expressionType); + funcCallAnno.kind = FunctionCallKind::FunctionCall; - string msg = - "Wrong argument count for " + - string(isStructConstructorCall ? "struct constructor" : "function call") + - ": " + - toString(arguments.size()) + - " arguments given but expected " + - toString(parameterTypes.size()) + - "."; - // Extend error message in case we try to construct a struct with mapping member. - if (_functionCall.annotation().kind == FunctionCallKind::StructConstructorCall && !membersRemovedForStructConstructor.empty()) + // Purity for function calls also depends upon the callee and its FunctionType + funcCallAnno.isPure = + argumentsArePure && + _functionCall.expression().annotation().isPure && + functionType && + functionType->isPure(); + + break; + + case Type::Category::TypeType: + { + // Determine type for type conversion or struct construction expressions + TypePointer const& actualType = dynamic_cast<TypeType const&>(*expressionType).actualType(); + solAssert(!!actualType, ""); + + if (actualType->category() == Type::Category::Struct) { - msg += " Members that have to be skipped in memory:"; - for (auto const& member: membersRemovedForStructConstructor) - msg += " " + member; + functionType = dynamic_cast<StructType const&>(*actualType).constructorType(); + funcCallAnno.kind = FunctionCallKind::StructConstructorCall; + funcCallAnno.isPure = argumentsArePure; } - m_errorReporter.typeError(_functionCall.location(), msg); + else + { + funcCallAnno.kind = FunctionCallKind::TypeConversion; + funcCallAnno.isPure = argumentsArePure; + } + + break; + } + + default: + m_errorReporter.typeError(_functionCall.location(), "Type is not callable"); + funcCallAnno.kind = FunctionCallKind::Unset; + funcCallAnno.isPure = argumentsArePure; + break; } - else if (isPositionalCall) + + // Determine return types + switch (funcCallAnno.kind) + { + case FunctionCallKind::TypeConversion: + funcCallAnno.type = typeCheckTypeConversionAndRetrieveReturnType(_functionCall); + break; + + case FunctionCallKind::StructConstructorCall: // fall-through + case FunctionCallKind::FunctionCall: { - bool const abiEncodeV2 = m_scope->sourceUnit().annotation().experimentalFeatures.count(ExperimentalFeature::ABIEncoderV2); + TypePointers returnTypes; - for (size_t i = 0; i < arguments.size(); ++i) + switch (functionType->kind()) { - auto const& argType = type(*arguments[i]); - if (functionType->takesArbitraryParameters() && i >= parameterTypes.size()) - { - bool errored = false; - if (auto t = dynamic_cast<RationalNumberType const*>(argType.get())) - if (!t->mobileType()) - { - m_errorReporter.typeError(arguments[i]->location(), "Invalid rational number (too large or division by zero)."); - errored = true; - } - if (!errored) - { - TypePointer encodingType; - if ( - argType->mobileType() && - argType->mobileType()->interfaceType(false) && - argType->mobileType()->interfaceType(false)->encodingType() - ) - encodingType = argType->mobileType()->interfaceType(false)->encodingType(); - // Structs are fine as long as ABIV2 is activated and we do not do packed encoding. - if (!encodingType || ( - dynamic_cast<StructType const*>(encodingType.get()) && - !(abiEncodeV2 && functionType->padArguments()) - )) - m_errorReporter.typeError(arguments[i]->location(), "This type cannot be encoded."); - } - } - else if (!type(*arguments[i])->isImplicitlyConvertibleTo(*parameterTypes[i])) - m_errorReporter.typeError( - arguments[i]->location(), - "Invalid type for argument in function call. " - "Invalid implicit conversion from " + - type(*arguments[i])->toString() + - " to " + - parameterTypes[i]->toString() + - " requested." + case FunctionType::Kind::ABIDecode: + { + bool const abiEncoderV2 = + m_scope->sourceUnit().annotation().experimentalFeatures.count( + ExperimentalFeature::ABIEncoderV2 ); + returnTypes = typeCheckABIDecodeAndRetrieveReturnType(_functionCall, abiEncoderV2); + break; } - } - else - { - // call by named arguments - auto const& parameterNames = functionType->parameterNames(); - if (functionType->takesArbitraryParameters()) - m_errorReporter.typeError( - _functionCall.location(), - "Named arguments cannnot be used for functions that take arbitrary parameters." - ); - else if (parameterNames.size() > argumentNames.size()) - m_errorReporter.typeError(_functionCall.location(), "Some argument names are missing."); - else if (parameterNames.size() < argumentNames.size()) - m_errorReporter.typeError(_functionCall.location(), "Too many arguments."); - else + case FunctionType::Kind::ABIEncode: + case FunctionType::Kind::ABIEncodePacked: + case FunctionType::Kind::ABIEncodeWithSelector: + case FunctionType::Kind::ABIEncodeWithSignature: { - // check duplicate names - bool duplication = false; - for (size_t i = 0; i < argumentNames.size(); i++) - for (size_t j = i + 1; j < argumentNames.size(); j++) - if (*argumentNames[i] == *argumentNames[j]) - { - duplication = true; - m_errorReporter.typeError(arguments[i]->location(), "Duplicate named argument."); - } - - // check actual types - if (!duplication) - for (size_t i = 0; i < argumentNames.size(); i++) - { - bool found = false; - for (size_t j = 0; j < parameterNames.size(); j++) - if (parameterNames[j] == *argumentNames[i]) - { - found = true; - // check type convertible - if (!type(*arguments[i])->isImplicitlyConvertibleTo(*parameterTypes[j])) - m_errorReporter.typeError( - arguments[i]->location(), - "Invalid type for argument in function call. " - "Invalid implicit conversion from " + - type(*arguments[i])->toString() + - " to " + - parameterTypes[i]->toString() + - " requested." - ); - break; - } - - if (!found) - m_errorReporter.typeError( - _functionCall.location(), - "Named argument does not match function declaration." - ); - } + typeCheckABIEncodeFunctions(_functionCall, functionType); + returnTypes = functionType->returnParameterTypes(); + break; + } + default: + { + typeCheckFunctionCall(_functionCall, functionType); + returnTypes = m_evmVersion.supportsReturndata() ? + functionType->returnParameterTypes() : + functionType->returnParameterTypesWithoutDynamicTypes(); + break; + } } + + funcCallAnno.type = returnTypes.size() == 1 ? + move(returnTypes.front()) : + make_shared<TupleType>(move(returnTypes)); + + break; + } + + case FunctionCallKind::Unset: // fall-through + default: + // for non-callables, ensure error reported and annotate node to void function + solAssert(m_errorReporter.hasErrors(), ""); + funcCallAnno.kind = FunctionCallKind::FunctionCall; + funcCallAnno.type = make_shared<TupleType>(); + break; } return false; @@ -1959,7 +2261,7 @@ void TypeChecker::endVisit(NewExpression const& _newExpression) if (!contract) m_errorReporter.fatalTypeError(_newExpression.location(), "Identifier is not a contract."); if (contract->contractKind() == ContractDefinition::ContractKind::Interface) - m_errorReporter.fatalTypeError(_newExpression.location(), "Cannot instantiate an interface."); + m_errorReporter.fatalTypeError(_newExpression.location(), "Cannot instantiate an interface."); if (!contract->annotation().unimplementedFunctions.empty()) { SecondarySourceLocation ssl; @@ -2040,7 +2342,10 @@ bool TypeChecker::visit(MemberAccess const& _memberAccess) else ++it; } - if (possibleMembers.size() == 0) + + auto& annotation = _memberAccess.annotation(); + + if (possibleMembers.empty()) { if (initialMemberCount == 0) { @@ -2057,11 +2362,38 @@ bool TypeChecker::visit(MemberAccess const& _memberAccess) " outside of storage." ); } + string errorMsg = "Member \"" + memberName + "\" not found or not visible " + "after argument-dependent lookup in " + exprType->toString() + "."; + if (memberName == "value") + { + errorMsg.pop_back(); + errorMsg += " - did you forget the \"payable\" modifier?"; + } + else if (exprType->category() == Type::Category::Function) + { + if (auto const& funType = dynamic_pointer_cast<FunctionType const>(exprType)) + { + auto const& t = funType->returnParameterTypes(); + if (t.size() == 1) + if ( + t.front()->category() == Type::Category::Contract || + t.front()->category() == Type::Category::Struct + ) + errorMsg += " Did you intend to call the function?"; + } + } + if (exprType->category() == Type::Category::Contract) + for (auto const& addressMember: AddressType::addressPayable().nativeMembers(nullptr)) + if (addressMember.name == memberName) + { + Identifier const* var = dynamic_cast<Identifier const*>(&_memberAccess.expression()); + string varName = var ? var->name() : "..."; + errorMsg += " Use \"address(" + varName + ")." + memberName + "\" to access this address member."; + break; + } m_errorReporter.fatalTypeError( _memberAccess.location(), - "Member \"" + memberName + "\" not found or not visible " - "after argument-dependent lookup in " + exprType->toString() + - (memberName == "value" ? " - did you forget the \"payable\" modifier?" : "") + errorMsg ); } else if (possibleMembers.size() > 1) @@ -2069,10 +2401,9 @@ bool TypeChecker::visit(MemberAccess const& _memberAccess) _memberAccess.location(), "Member \"" + memberName + "\" not unique " "after argument-dependent lookup in " + exprType->toString() + - (memberName == "value" ? " - did you forget the \"payable\" modifier?" : "") + (memberName == "value" ? " - did you forget the \"payable\" modifier?" : ".") ); - auto& annotation = _memberAccess.annotation(); annotation.referencedDeclaration = possibleMembers.front().declaration; annotation.type = possibleMembers.front().type; @@ -2081,7 +2412,7 @@ bool TypeChecker::visit(MemberAccess const& _memberAccess) m_errorReporter.typeError( _memberAccess.location(), "Function \"" + memberName + "\" cannot be called on an object of type " + - exprType->toString() + " (expected " + funType->selfType()->toString() + ")" + exprType->toString() + " (expected " + funType->selfType()->toString() + ")." ); if (exprType->category() == Type::Category::Struct) @@ -2105,20 +2436,6 @@ bool TypeChecker::visit(MemberAccess const& _memberAccess) if (exprType->category() == Type::Category::Contract) { - // Warn about using address members on contracts - bool v050 = m_scope->sourceUnit().annotation().experimentalFeatures.count(ExperimentalFeature::V050); - for (auto const& addressMember: IntegerType(160, IntegerType::Modifier::Address).nativeMembers(nullptr)) - if (addressMember.name == memberName && *annotation.type == *addressMember.type) - { - solAssert(!v050, "Address member still present on contract in v0.5.0."); - m_errorReporter.warning( - _memberAccess.location(), - "Using contract member \"" + memberName +"\" inherited from the address type is deprecated." + - " Convert the contract to \"address\" type to access the member," - " for example use \"address(contract)." + memberName + "\" instead." - ); - } - // Warn about using send or transfer with a non-payable fallback function. if (auto callType = dynamic_cast<FunctionType const*>(type(_memberAccess).get())) { @@ -2172,12 +2489,13 @@ bool TypeChecker::visit(IndexAccess const& _access) else { expectType(*index, IntegerType(256)); - if (auto numberType = dynamic_cast<RationalNumberType const*>(type(*index).get())) - { - if (!numberType->isFractional()) // error is reported above + if (!m_errorReporter.hasErrors()) + if (auto numberType = dynamic_cast<RationalNumberType const*>(type(*index).get())) + { + solAssert(!numberType->isFractional(), ""); if (!actualType.isDynamicallySized() && actualType.length() <= numberType->literalValue(nullptr)) m_errorReporter.typeError(_access.location(), "Out of bounds array access."); - } + } } resultType = actualType.baseType(); isLValue = actualType.location() != DataLocation::CallData; @@ -2220,7 +2538,8 @@ bool TypeChecker::visit(IndexAccess const& _access) m_errorReporter.typeError(_access.location(), "Index expression cannot be omitted."); else { - expectType(*index, IntegerType(256)); + if (!expectType(*index, IntegerType(256))) + m_errorReporter.fatalTypeError(_access.location(), "Index expression cannot be represented as an unsigned integer."); if (auto integerType = dynamic_cast<RationalNumberType const*>(type(*index).get())) if (bytesType.numBytes() <= integerType->literalValue(nullptr)) m_errorReporter.typeError(_access.location(), "Out of bounds array access."); @@ -2313,51 +2632,46 @@ void TypeChecker::endVisit(ElementaryTypeNameExpression const& _expr) void TypeChecker::endVisit(Literal const& _literal) { - bool const v050 = m_scope->sourceUnit().annotation().experimentalFeatures.count(ExperimentalFeature::V050); - if (_literal.looksLikeAddress()) { - if (_literal.passesAddressChecksum()) - _literal.annotation().type = make_shared<IntegerType>(160, IntegerType::Modifier::Address); - else - m_errorReporter.warning( + // Assign type here if it even looks like an address. This prevents double errors for invalid addresses + _literal.annotation().type = make_shared<AddressType>(StateMutability::Payable); + + string msg; + if (_literal.valueWithoutUnderscores().length() != 42) // "0x" + 40 hex digits + // looksLikeAddress enforces that it is a hex literal starting with "0x" + msg = + "This looks like an address but is not exactly 40 hex digits. It is " + + to_string(_literal.valueWithoutUnderscores().length() - 2) + + " hex digits."; + else if (!_literal.passesAddressChecksum()) + { + msg = "This looks like an address but has an invalid checksum."; + if (!_literal.getChecksummedAddress().empty()) + msg += " Correct checksummed address: \"" + _literal.getChecksummedAddress() + "\"."; + } + + if (!msg.empty()) + m_errorReporter.syntaxError( _literal.location(), - "This looks like an address but has an invalid checksum. " - "If this is not used as an address, please prepend '00'. " + - (!_literal.getChecksummedAddress().empty() ? "Correct checksummed address: '" + _literal.getChecksummedAddress() + "'. " : "") + + msg + + " If this is not used as an address, please prepend '00'. " + "For more information please see https://solidity.readthedocs.io/en/develop/types.html#address-literals" ); } if (_literal.isHexNumber() && _literal.subDenomination() != Literal::SubDenomination::None) - { - if (v050) - m_errorReporter.fatalTypeError( - _literal.location(), - "Hexadecimal numbers cannot be used with unit denominations. " - "You can use an expression of the form \"0x1234 * 1 day\" instead." - ); - else - m_errorReporter.warning( - _literal.location(), - "Hexadecimal numbers with unit denominations are deprecated. " - "You can use an expression of the form \"0x1234 * 1 day\" instead." - ); - } + m_errorReporter.fatalTypeError( + _literal.location(), + "Hexadecimal numbers cannot be used with unit denominations. " + "You can use an expression of the form \"0x1234 * 1 day\" instead." + ); if (_literal.subDenomination() == Literal::SubDenomination::Year) - { - if (v050) - m_errorReporter.typeError( - _literal.location(), - "Using \"years\" as a unit denomination is deprecated." - ); - else - m_errorReporter.warning( - _literal.location(), - "Using \"years\" as a unit denomination is deprecated." - ); - } + m_errorReporter.typeError( + _literal.location(), + "Using \"years\" as a unit denomination is deprecated." + ); if (!_literal.annotation().type) _literal.annotation().type = Type::forLiteral(_literal); @@ -2396,7 +2710,7 @@ Declaration const& TypeChecker::dereference(UserDefinedTypeName const& _typeName return *_typeName.annotation().referencedDeclaration; } -void TypeChecker::expectType(Expression const& _expression, Type const& _expectedType) +bool TypeChecker::expectType(Expression const& _expression, Type const& _expectedType) { _expression.accept(*this); if (!type(_expression)->isImplicitlyConvertibleTo(_expectedType)) @@ -2425,23 +2739,9 @@ void TypeChecker::expectType(Expression const& _expression, Type const& _expecte _expectedType.toString() + "." ); + return false; } - - if ( - type(_expression)->category() == Type::Category::RationalNumber && - _expectedType.category() == Type::Category::FixedBytes - ) - { - auto literal = dynamic_cast<Literal const*>(&_expression); - - if (literal && !literal->isHexNumber()) - m_errorReporter.warning( - _expression.location(), - "Decimal literal assigned to bytesXX variable will be left-aligned. " - "Use an explicit conversion to silence this warning." - ); - } - + return true; } void TypeChecker::requireLValue(Expression const& _expression) diff --git a/libsolidity/analysis/TypeChecker.h b/libsolidity/analysis/TypeChecker.h index 2245abd6..c76fa466 100644 --- a/libsolidity/analysis/TypeChecker.h +++ b/libsolidity/analysis/TypeChecker.h @@ -68,7 +68,7 @@ private: void checkContractDuplicateFunctions(ContractDefinition const& _contract); void checkContractDuplicateEvents(ContractDefinition const& _contract); void checkContractIllegalOverrides(ContractDefinition const& _contract); - /// Reports a type error with an appropiate message if overriden function signature differs. + /// Reports a type error with an appropriate message if overridden function signature differs. /// Also stores the direct super function in the AST annotations. void checkFunctionOverride(FunctionDefinition const& function, FunctionDefinition const& super); void overrideError(FunctionDefinition const& function, FunctionDefinition const& super, std::string message); @@ -87,13 +87,46 @@ private: /// Checks (and warns) if a tuple assignment might cause unexpected overwrites in storage. /// Should only be called if the left hand side is tuple-typed. void checkDoubleStorageAssignment(Assignment const& _assignment); + // Checks whether the expression @arg _expression can be assigned from type @arg _type + // and reports an error, if not. + void checkExpressionAssignment(Type const& _type, Expression const& _expression); + + /// Performs type checks for ``abi.decode(bytes memory, (...))`` and returns the + /// vector of return types (which is basically the second argument) if successful. It returns + /// the empty vector on error. + TypePointers typeCheckABIDecodeAndRetrieveReturnType( + FunctionCall const& _functionCall, + bool _abiEncoderV2 + ); + + /// Performs type checks and determines result types for type conversion FunctionCall nodes. + TypePointer typeCheckTypeConversionAndRetrieveReturnType( + FunctionCall const& _functionCall + ); + + /// Performs type checks on function call and struct ctor FunctionCall nodes (except for kind ABIDecode). + void typeCheckFunctionCall( + FunctionCall const& _functionCall, + FunctionTypePointer _functionType + ); + + /// Performs general number and type checks of arguments against function call and struct ctor FunctionCall node parameters. + void typeCheckFunctionGeneralChecks( + FunctionCall const& _functionCall, + FunctionTypePointer _functionType + ); + + /// Performs general checks and checks specific to ABI encode functions + void typeCheckABIEncodeFunctions( + FunctionCall const& _functionCall, + FunctionTypePointer _functionType + ); virtual void endVisit(InheritanceSpecifier const& _inheritance) override; virtual void endVisit(UsingForDirective const& _usingFor) override; virtual bool visit(StructDefinition const& _struct) override; virtual bool visit(FunctionDefinition const& _function) override; virtual bool visit(VariableDeclaration const& _variable) override; - virtual bool visit(EnumDefinition const& _enum) override; /// We need to do this manually because we want to pass the bases of the current contract in /// case this is a base constructor call. void visitManually(ModifierInvocation const& _modifier, std::vector<ContractDefinition const*> const& _bases); @@ -136,7 +169,7 @@ private: /// Runs type checks on @a _expression to infer its type and then checks that it is implicitly /// convertible to @a _expectedType. - void expectType(Expression const& _expression, Type const& _expectedType); + bool expectType(Expression const& _expression, Type const& _expectedType); /// Runs type checks on @a _expression to infer its type and then checks that it is an LValue. void requireLValue(Expression const& _expression); @@ -147,6 +180,9 @@ private: /// Flag indicating whether we are currently inside an EmitStatement. bool m_insideEmitStatement = false; + /// Flag indicating whether we are currently inside a StructDefinition. + bool m_insideStruct = false; + ErrorReporter& m_errorReporter; }; diff --git a/libsolidity/analysis/ViewPureChecker.cpp b/libsolidity/analysis/ViewPureChecker.cpp index d9843012..b0cacc43 100644 --- a/libsolidity/analysis/ViewPureChecker.cpp +++ b/libsolidity/analysis/ViewPureChecker.cpp @@ -116,31 +116,22 @@ private: bool ViewPureChecker::check() { - // The bool means "enforce view with errors". - map<ContractDefinition const*, bool> contracts; + vector<ContractDefinition const*> contracts; for (auto const& node: m_ast) { SourceUnit const* source = dynamic_cast<SourceUnit const*>(node.get()); solAssert(source, ""); - bool enforceView = source->annotation().experimentalFeatures.count(ExperimentalFeature::V050); - for (ContractDefinition const* c: source->filteredNodes<ContractDefinition>(source->nodes())) - contracts[c] = enforceView; + contracts += source->filteredNodes<ContractDefinition>(source->nodes()); } // Check modifiers first to infer their state mutability. for (auto const& contract: contracts) - { - m_enforceViewWithError = contract.second; - for (ModifierDefinition const* mod: contract.first->functionModifiers()) + for (ModifierDefinition const* mod: contract->functionModifiers()) mod->accept(*this); - } for (auto const& contract: contracts) - { - m_enforceViewWithError = contract.second; - contract.first->accept(*this); - } + contract->accept(*this); return !m_errors; } @@ -151,7 +142,7 @@ bool ViewPureChecker::visit(FunctionDefinition const& _funDef) { solAssert(!m_currentFunction, ""); m_currentFunction = &_funDef; - m_currentBestMutability = StateMutability::Pure; + m_bestMutabilityAndLocation = {StateMutability::Pure, _funDef.location()}; return true; } @@ -159,7 +150,7 @@ void ViewPureChecker::endVisit(FunctionDefinition const& _funDef) { solAssert(m_currentFunction == &_funDef, ""); if ( - m_currentBestMutability < _funDef.stateMutability() && + m_bestMutabilityAndLocation.mutability < _funDef.stateMutability() && _funDef.stateMutability() != StateMutability::Payable && _funDef.isImplemented() && !_funDef.isConstructor() && @@ -168,22 +159,22 @@ void ViewPureChecker::endVisit(FunctionDefinition const& _funDef) ) m_errorReporter.warning( _funDef.location(), - "Function state mutability can be restricted to " + stateMutabilityToString(m_currentBestMutability) + "Function state mutability can be restricted to " + stateMutabilityToString(m_bestMutabilityAndLocation.mutability) ); m_currentFunction = nullptr; } -bool ViewPureChecker::visit(ModifierDefinition const&) +bool ViewPureChecker::visit(ModifierDefinition const& _modifier) { solAssert(m_currentFunction == nullptr, ""); - m_currentBestMutability = StateMutability::Pure; + m_bestMutabilityAndLocation = {StateMutability::Pure, _modifier.location()}; return true; } void ViewPureChecker::endVisit(ModifierDefinition const& _modifierDef) { solAssert(m_currentFunction == nullptr, ""); - m_inferredMutability[&_modifierDef] = m_currentBestMutability; + m_inferredMutability[&_modifierDef] = std::move(m_bestMutabilityAndLocation); } void ViewPureChecker::endVisit(Identifier const& _identifier) @@ -228,39 +219,72 @@ void ViewPureChecker::endVisit(InlineAssembly const& _inlineAssembly) }(_inlineAssembly.operations()); } -void ViewPureChecker::reportMutability(StateMutability _mutability, SourceLocation const& _location) +void ViewPureChecker::reportMutability( + StateMutability _mutability, + SourceLocation const& _location, + boost::optional<SourceLocation> const& _nestedLocation +) { - if (m_currentFunction && m_currentFunction->stateMutability() < _mutability) + if (_mutability > m_bestMutabilityAndLocation.mutability) + m_bestMutabilityAndLocation = MutabilityAndLocation{_mutability, _location}; + if (!m_currentFunction || _mutability <= m_currentFunction->stateMutability()) + return; + + // Check for payable here, because any occurrence of `msg.value` + // will set mutability to payable. + if (_mutability == StateMutability::View || ( + _mutability == StateMutability::Payable && + m_currentFunction->stateMutability() == StateMutability::Pure + )) { - string text; - if (_mutability == StateMutability::View) - text = - "Function declared as pure, but this expression (potentially) reads from the " - "environment or state and thus requires \"view\"."; - else if (_mutability == StateMutability::NonPayable) - text = - "Function declared as " + - stateMutabilityToString(m_currentFunction->stateMutability()) + - ", but this expression (potentially) modifies the state and thus " - "requires non-payable (the default) or payable."; - else - solAssert(false, ""); - - solAssert( - m_currentFunction->stateMutability() == StateMutability::View || - m_currentFunction->stateMutability() == StateMutability::Pure, - "" + m_errorReporter.typeError( + _location, + "Function declared as pure, but this expression (potentially) reads from the " + "environment or state and thus requires \"view\"." ); - if (!m_enforceViewWithError && m_currentFunction->stateMutability() == StateMutability::View) - m_errorReporter.warning(_location, text); - else + m_errors = true; + } + else if (_mutability == StateMutability::NonPayable) + { + m_errorReporter.typeError( + _location, + "Function declared as " + + stateMutabilityToString(m_currentFunction->stateMutability()) + + ", but this expression (potentially) modifies the state and thus " + "requires non-payable (the default) or payable." + ); + m_errors = true; + } + else if (_mutability == StateMutability::Payable) + { + // We do not warn for library functions because they cannot be payable anyway. + // Also internal functions should be allowed to use `msg.value`. + if (m_currentFunction->isPublic() && m_currentFunction->inContractKind() != ContractDefinition::ContractKind::Library) { + if (_nestedLocation) + m_errorReporter.typeError( + _location, + SecondarySourceLocation().append("\"msg.value\" appears here inside the modifier.", *_nestedLocation), + "This modifier uses \"msg.value\" and thus the function has to be payable or internal." + ); + else + m_errorReporter.typeError( + _location, + "\"msg.value\" can only be used in payable public functions. Make the function " + "\"payable\" or use an internal function to avoid this error." + ); m_errors = true; - m_errorReporter.typeError(_location, text); } } - if (_mutability > m_currentBestMutability) - m_currentBestMutability = _mutability; + else + solAssert(false, ""); + + solAssert( + m_currentFunction->stateMutability() == StateMutability::View || + m_currentFunction->stateMutability() == StateMutability::Pure || + m_currentFunction->stateMutability() == StateMutability::NonPayable, + "" + ); } void ViewPureChecker::endVisit(FunctionCall const& _functionCall) @@ -268,11 +292,11 @@ void ViewPureChecker::endVisit(FunctionCall const& _functionCall) if (_functionCall.annotation().kind != FunctionCallKind::FunctionCall) return; - StateMutability mut = dynamic_cast<FunctionType const&>(*_functionCall.expression().annotation().type).stateMutability(); + StateMutability mutability = dynamic_cast<FunctionType const&>(*_functionCall.expression().annotation().type).stateMutability(); // We only require "nonpayable" to call a payble function. - if (mut == StateMutability::Payable) - mut = StateMutability::NonPayable; - reportMutability(mut, _functionCall.location()); + if (mutability == StateMutability::Payable) + mutability = StateMutability::NonPayable; + reportMutability(mutability, _functionCall.location()); } bool ViewPureChecker::visit(MemberAccess const& _memberAccess) @@ -299,19 +323,34 @@ void ViewPureChecker::endVisit(MemberAccess const& _memberAccess) ASTString const& member = _memberAccess.memberName(); switch (_memberAccess.expression().annotation().type->category()) { - case Type::Category::Contract: - case Type::Category::Integer: - if (member == "balance" && !_memberAccess.annotation().referencedDeclaration) + case Type::Category::Address: + if (member == "balance") mutability = StateMutability::View; break; case Type::Category::Magic: { - // we can ignore the kind of magic and only look at the name of the member - set<string> static const pureMembers{ - "encode", "encodePacked", "encodeWithSelector", "encodeWithSignature", "data", "sig", "blockhash" + using MagicMember = pair<MagicType::Kind, string>; + set<MagicMember> static const pureMembers{ + {MagicType::Kind::ABI, "decode"}, + {MagicType::Kind::ABI, "encode"}, + {MagicType::Kind::ABI, "encodePacked"}, + {MagicType::Kind::ABI, "encodeWithSelector"}, + {MagicType::Kind::ABI, "encodeWithSignature"}, + {MagicType::Kind::Block, "blockhash"}, + {MagicType::Kind::Message, "data"}, + {MagicType::Kind::Message, "sig"} }; - if (!pureMembers.count(member)) + set<MagicMember> static const payableMembers{ + {MagicType::Kind::Message, "value"} + }; + + auto const& type = dynamic_cast<MagicType const&>(*_memberAccess.expression().annotation().type); + MagicMember magicMember(type.kind(), member); + + if (!pureMembers.count(magicMember)) mutability = StateMutability::View; + if (payableMembers.count(magicMember)) + mutability = StateMutability::Payable; break; } case Type::Category::Struct: @@ -351,7 +390,8 @@ void ViewPureChecker::endVisit(ModifierInvocation const& _modifier) if (ModifierDefinition const* mod = dynamic_cast<decltype(mod)>(_modifier.name()->annotation().referencedDeclaration)) { solAssert(m_inferredMutability.count(mod), ""); - reportMutability(m_inferredMutability.at(mod), _modifier.location()); + auto const& mutAndLocation = m_inferredMutability.at(mod); + reportMutability(mutAndLocation.mutability, _modifier.location(), mutAndLocation.location); } else solAssert(dynamic_cast<ContractDefinition const*>(_modifier.name()->annotation().referencedDeclaration), ""); diff --git a/libsolidity/analysis/ViewPureChecker.h b/libsolidity/analysis/ViewPureChecker.h index 0b882cd8..faa5b698 100644 --- a/libsolidity/analysis/ViewPureChecker.h +++ b/libsolidity/analysis/ViewPureChecker.h @@ -31,16 +31,6 @@ namespace dev namespace solidity { -class ASTNode; -class FunctionDefinition; -class ModifierDefinition; -class Identifier; -class MemberAccess; -class IndexAccess; -class ModifierInvocation; -class FunctionCall; -class InlineAssembly; - class ViewPureChecker: private ASTConstVisitor { public: @@ -50,6 +40,11 @@ public: bool check(); private: + struct MutabilityAndLocation + { + StateMutability mutability; + SourceLocation location; + }; virtual bool visit(FunctionDefinition const& _funDef) override; virtual void endVisit(FunctionDefinition const& _funDef) override; @@ -65,16 +60,19 @@ private: /// Called when an element of mutability @a _mutability is encountered. /// Creates appropriate warnings and errors and sets @a m_currentBestMutability. - void reportMutability(StateMutability _mutability, SourceLocation const& _location); + void reportMutability( + StateMutability _mutability, + SourceLocation const& _location, + boost::optional<SourceLocation> const& _nestedLocation = {} + ); std::vector<std::shared_ptr<ASTNode>> const& m_ast; ErrorReporter& m_errorReporter; bool m_errors = false; - bool m_enforceViewWithError = false; - StateMutability m_currentBestMutability = StateMutability::Payable; + MutabilityAndLocation m_bestMutabilityAndLocation = MutabilityAndLocation{StateMutability::Payable, SourceLocation()}; FunctionDefinition const* m_currentFunction = nullptr; - std::map<ModifierDefinition const*, StateMutability> m_inferredMutability; + std::map<ModifierDefinition const*, MutabilityAndLocation> m_inferredMutability; }; } diff --git a/libsolidity/ast/AST.cpp b/libsolidity/ast/AST.cpp index 80f5d642..3ae6bd6d 100644 --- a/libsolidity/ast/AST.cpp +++ b/libsolidity/ast/AST.cpp @@ -24,7 +24,7 @@ #include <libsolidity/ast/ASTVisitor.h> #include <libsolidity/ast/AST_accept.h> -#include <libdevcore/SHA3.h> +#include <libdevcore/Keccak256.h> #include <boost/algorithm/string.hpp> @@ -311,8 +311,6 @@ FunctionTypePointer FunctionDefinition::functionType(bool _internal) const return make_shared<FunctionType>(*this, _internal); case Declaration::Visibility::External: return {}; - default: - solAssert(false, "visibility() should not return a Visibility"); } } else @@ -327,8 +325,6 @@ FunctionTypePointer FunctionDefinition::functionType(bool _internal) const case Declaration::Visibility::Public: case Declaration::Visibility::External: return make_shared<FunctionType>(*this, _internal); - default: - solAssert(false, "visibility() should not return a Visibility"); } } @@ -347,15 +343,6 @@ string FunctionDefinition::externalSignature() const return FunctionType(*this).externalSignature(); } -string FunctionDefinition::fullyQualifiedName() const -{ - auto const* contract = dynamic_cast<ContractDefinition const*>(scope()); - solAssert(contract, "Enclosing scope of function definition was not set."); - - auto fname = name().empty() ? "<fallback>" : name(); - return sourceUnitName() + ":" + contract->name() + "." + fname; -} - FunctionDefinitionAnnotation& FunctionDefinition::annotation() const { if (!m_annotation) @@ -406,7 +393,7 @@ SourceUnit const& Scopable::sourceUnit() const { ASTNode const* s = scope(); solAssert(s, ""); - // will not always be a declaratoion + // will not always be a declaration while (dynamic_cast<Scopable const*>(s) && dynamic_cast<Scopable const*>(s)->scope()) s = dynamic_cast<Scopable const*>(s)->scope(); return dynamic_cast<SourceUnit const&>(*s); @@ -427,6 +414,7 @@ bool VariableDeclaration::isLocalVariable() const { auto s = scope(); return + dynamic_cast<FunctionTypeName const*>(s) || dynamic_cast<CallableDeclaration const*>(s) || dynamic_cast<Block const*>(s) || dynamic_cast<ForStatement const*>(s); @@ -434,14 +422,18 @@ bool VariableDeclaration::isLocalVariable() const bool VariableDeclaration::isCallableParameter() const { - auto const* callable = dynamic_cast<CallableDeclaration const*>(scope()); - if (!callable) - return false; - for (auto const& variable: callable->parameters()) - if (variable.get() == this) - return true; - if (callable->returnParameterList()) - for (auto const& variable: callable->returnParameterList()->parameters()) + if (isReturnParameter()) + return true; + + vector<ASTPointer<VariableDeclaration>> const* parameters = nullptr; + + if (auto const* funTypeName = dynamic_cast<FunctionTypeName const*>(scope())) + parameters = &funTypeName->parameterTypes(); + else if (auto const* callable = dynamic_cast<CallableDeclaration const*>(scope())) + parameters = &callable->parameters(); + + if (parameters) + for (auto const& variable: *parameters) if (variable.get() == this) return true; return false; @@ -454,11 +446,16 @@ bool VariableDeclaration::isLocalOrReturn() const bool VariableDeclaration::isReturnParameter() const { - auto const* callable = dynamic_cast<CallableDeclaration const*>(scope()); - if (!callable) - return false; - if (callable->returnParameterList()) - for (auto const& variable: callable->returnParameterList()->parameters()) + vector<ASTPointer<VariableDeclaration>> const* returnParameters = nullptr; + + if (auto const* funTypeName = dynamic_cast<FunctionTypeName const*>(scope())) + returnParameters = &funTypeName->returnParameterTypes(); + else if (auto const* callable = dynamic_cast<CallableDeclaration const*>(scope())) + if (callable->returnParameterList()) + returnParameters = &callable->returnParameterList()->parameters(); + + if (returnParameters) + for (auto const& variable: *returnParameters) if (variable.get() == this) return true; return false; @@ -466,18 +463,86 @@ bool VariableDeclaration::isReturnParameter() const bool VariableDeclaration::isExternalCallableParameter() const { - auto const* callable = dynamic_cast<CallableDeclaration const*>(scope()); - if (!callable || callable->visibility() != Declaration::Visibility::External) + if (!isCallableParameter()) return false; - for (auto const& variable: callable->parameters()) - if (variable.get() == this) - return true; + + if (auto const* callable = dynamic_cast<CallableDeclaration const*>(scope())) + if (callable->visibility() == Declaration::Visibility::External) + return !isReturnParameter(); + return false; } -bool VariableDeclaration::canHaveAutoType() const +bool VariableDeclaration::isInternalCallableParameter() const +{ + if (!isCallableParameter()) + return false; + + if (auto const* funTypeName = dynamic_cast<FunctionTypeName const*>(scope())) + return funTypeName->visibility() == Declaration::Visibility::Internal; + else if (auto const* callable = dynamic_cast<CallableDeclaration const*>(scope())) + return callable->visibility() <= Declaration::Visibility::Internal; + return false; +} + +bool VariableDeclaration::isLibraryFunctionParameter() const +{ + if (!isCallableParameter()) + return false; + if (auto const* funDef = dynamic_cast<FunctionDefinition const*>(scope())) + return dynamic_cast<ContractDefinition const&>(*funDef->scope()).isLibrary(); + else + return false; +} + +bool VariableDeclaration::isEventParameter() const +{ + return dynamic_cast<EventDefinition const*>(scope()) != nullptr; +} + +bool VariableDeclaration::hasReferenceOrMappingType() const { - return isLocalVariable() && !isCallableParameter(); + solAssert(typeName(), ""); + solAssert(typeName()->annotation().type, "Can only be called after reference resolution"); + TypePointer const& type = typeName()->annotation().type; + return type->category() == Type::Category::Mapping || dynamic_cast<ReferenceType const*>(type.get()); +} + +set<VariableDeclaration::Location> VariableDeclaration::allowedDataLocations() const +{ + using Location = VariableDeclaration::Location; + + if (!hasReferenceOrMappingType() || isStateVariable() || isEventParameter()) + return set<Location>{ Location::Unspecified }; + else if (isStateVariable() && isConstant()) + return set<Location>{ Location::Memory }; + else if (isExternalCallableParameter()) + { + set<Location> locations{ Location::CallData }; + if (isLibraryFunctionParameter()) + locations.insert(Location::Storage); + return locations; + } + else if (isCallableParameter()) + { + set<Location> locations{ Location::Memory }; + if (isInternalCallableParameter() || isLibraryFunctionParameter()) + locations.insert(Location::Storage); + return locations; + } + else if (isLocalVariable()) + { + solAssert(typeName(), ""); + solAssert(typeName()->annotation().type, "Can only be called after reference resolution"); + if (typeName()->annotation().type->category() == Type::Category::Mapping) + return set<Location>{ Location::Storage }; + else + // TODO: add Location::Calldata once implemented for local variables. + return set<Location>{ Location::Memory, Location::Storage }; + } + else + // Struct members etc. + return set<Location>{ Location::Unspecified }; } TypePointer VariableDeclaration::type() const @@ -499,8 +564,6 @@ FunctionTypePointer VariableDeclaration::functionType(bool _internal) const case Declaration::Visibility::Public: case Declaration::Visibility::External: return make_shared<FunctionType>(*this); - default: - solAssert(false, "visibility() should not return a Visibility"); } // To make the compiler happy @@ -535,13 +598,6 @@ ReturnAnnotation& Return::annotation() const return dynamic_cast<ReturnAnnotation&>(*m_annotation); } -VariableDeclarationStatementAnnotation& VariableDeclarationStatement::annotation() const -{ - if (!m_annotation) - m_annotation = new VariableDeclarationStatementAnnotation(); - return dynamic_cast<VariableDeclarationStatementAnnotation&>(*m_annotation); -} - ExpressionAnnotation& Expression::annotation() const { if (!m_annotation) @@ -577,6 +633,11 @@ IdentifierAnnotation& Identifier::annotation() const return dynamic_cast<IdentifierAnnotation&>(*m_annotation); } +ASTString Literal::valueWithoutUnderscores() const +{ + return boost::erase_all_copy(value(), "_"); +} + bool Literal::isHexNumber() const { if (token() != Token::Number) @@ -592,20 +653,20 @@ bool Literal::looksLikeAddress() const if (!isHexNumber()) return false; - return abs(int(value().length()) - 42) <= 1; + return abs(int(valueWithoutUnderscores().length()) - 42) <= 1; } bool Literal::passesAddressChecksum() const { solAssert(isHexNumber(), "Expected hex number"); - return dev::passesAddressChecksum(value(), true); + return dev::passesAddressChecksum(valueWithoutUnderscores(), true); } -std::string Literal::getChecksummedAddress() const +string Literal::getChecksummedAddress() const { solAssert(isHexNumber(), "Expected hex number"); /// Pad literal to be a proper hex address. - string address = value().substr(2); + string address = valueWithoutUnderscores().substr(2); if (address.length() > 40) return string(); address.insert(address.begin(), 40 - address.size(), '0'); diff --git a/libsolidity/ast/AST.h b/libsolidity/ast/AST.h index fa0d6921..4fd2bcb8 100644 --- a/libsolidity/ast/AST.h +++ b/libsolidity/ast/AST.h @@ -206,7 +206,6 @@ public: bool isVisibleInDerivedContracts() const { return isVisibleInContract() && visibility() >= Visibility::Internal; } bool isVisibleAsLibraryMember() const { return visibility() >= Visibility::Internal; } - std::string fullyQualifiedName() const { return sourceUnitName() + ":" + name(); } virtual bool isLValue() const { return false; } virtual bool isPartOfExternalInterface() const { return false; } @@ -238,7 +237,7 @@ class PragmaDirective: public ASTNode public: PragmaDirective( SourceLocation const& _location, - std::vector<Token::Value> const& _tokens, + std::vector<Token> const& _tokens, std::vector<ASTString> const& _literals ): ASTNode(_location), m_tokens(_tokens), m_literals(_literals) {} @@ -246,13 +245,13 @@ public: virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTConstVisitor& _visitor) const override; - std::vector<Token::Value> const& tokens() const { return m_tokens; } + std::vector<Token> const& tokens() const { return m_tokens; } std::vector<ASTString> const& literals() const { return m_literals; } private: /// Sequence of tokens following the "pragma" keyword. - std::vector<Token::Value> m_tokens; + std::vector<Token> m_tokens; /// Sequence of literals following the "pragma" keyword. std::vector<ASTString> m_literals; }; @@ -406,6 +405,8 @@ public: /// Returns the fallback function or nullptr if no fallback function was specified. FunctionDefinition const* fallbackFunction() const; + std::string fullyQualifiedName() const { return sourceUnitName() + ":" + name(); } + virtual TypePointer type() const override; virtual ContractDefinitionAnnotation& annotation() const override; @@ -614,13 +615,11 @@ public: StateMutability stateMutability() const { return m_stateMutability; } bool isConstructor() const { return m_isConstructor; } - bool isOldStyleConstructor() const { return m_isConstructor && !name().empty(); } bool isFallback() const { return !m_isConstructor && name().empty(); } bool isPayable() const { return m_stateMutability == StateMutability::Payable; } std::vector<ASTPointer<ModifierInvocation>> const& modifiers() const { return m_functionModifiers; } std::vector<ASTPointer<VariableDeclaration>> const& returnParameters() const { return m_returnParameters->parameters(); } Block const& body() const { solAssert(m_body, ""); return *m_body; } - std::string fullyQualifiedName() const; virtual bool isVisibleInContract() const override { return Declaration::isVisibleInContract() && !isConstructor() && !isFallback(); @@ -656,7 +655,7 @@ private: class VariableDeclaration: public Declaration { public: - enum Location { Default, Storage, Memory }; + enum Location { Unspecified, Storage, Memory, CallData }; VariableDeclaration( SourceLocation const& _sourceLocation, @@ -667,7 +666,7 @@ public: bool _isStateVar = false, bool _isIndexed = false, bool _isConstant = false, - Location _referenceLocation = Location::Default + Location _referenceLocation = Location::Unspecified ): Declaration(_sourceLocation, _name, _visibility), m_typeName(_type), @@ -686,6 +685,8 @@ public: virtual bool isLValue() const override; virtual bool isPartOfExternalInterface() const override { return isPublic(); } + /// @returns true iff this variable is the parameter (or return parameter) of a function + /// (or function type name or event) or declared inside a function body. bool isLocalVariable() const; /// @returns true if this variable is a parameter or return parameter of a function. bool isCallableParameter() const; @@ -694,14 +695,27 @@ public: /// @returns true if this variable is a local variable or return parameter. bool isLocalOrReturn() const; /// @returns true if this variable is a parameter (not return parameter) of an external function. + /// This excludes parameters of external function type names. bool isExternalCallableParameter() const; + /// @returns true if this variable is a parameter or return parameter of an internal function + /// or a function type of internal visibility. + bool isInternalCallableParameter() const; + /// @returns true iff this variable is a parameter(or return parameter of a library function + bool isLibraryFunctionParameter() const; /// @returns true if the type of the variable does not need to be specified, i.e. it is declared /// in the body of a function or modifier. - bool canHaveAutoType() const; + /// @returns true if this variable is a parameter of an event. + bool isEventParameter() const; + /// @returns true if the type of the variable is a reference or mapping type, i.e. + /// array, struct or mapping. These types can take a data location (and often require it). + /// Can only be called after reference resolution. + bool hasReferenceOrMappingType() const; bool isStateVariable() const { return m_isStateVariable; } bool isIndexed() const { return m_isIndexed; } bool isConstant() const { return m_isConstant; } Location referenceLocation() const { return m_location; } + /// @returns a set of allowed storage locations for the variable. + std::set<Location> allowedDataLocations() const; virtual TypePointer type() const override; @@ -862,23 +876,31 @@ public: }; /** - * Any pre-defined type name represented by a single keyword, i.e. it excludes mappings, - * contracts, functions, etc. + * Any pre-defined type name represented by a single keyword (and possibly a state mutability for address types), + * i.e. it excludes mappings, contracts, functions, etc. */ class ElementaryTypeName: public TypeName { public: - ElementaryTypeName(SourceLocation const& _location, ElementaryTypeNameToken const& _elem): - TypeName(_location), m_type(_elem) - {} + ElementaryTypeName( + SourceLocation const& _location, + ElementaryTypeNameToken const& _elem, + boost::optional<StateMutability> _stateMutability = {} + ): TypeName(_location), m_type(_elem), m_stateMutability(_stateMutability) + { + solAssert(!_stateMutability.is_initialized() || _elem.token() == Token::Address, ""); + } virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTConstVisitor& _visitor) const override; ElementaryTypeNameToken const& typeName() const { return m_type; } + boost::optional<StateMutability> const& stateMutability() const { return m_stateMutability; } + private: ElementaryTypeNameToken m_type; + boost::optional<StateMutability> m_stateMutability; ///< state mutability for address type }; /** @@ -1169,11 +1191,11 @@ public: Statement const& body() const { return *m_body; } private: - /// For statement's initialization expresion. for(XXX; ; ). Can be empty + /// For statement's initialization expression. for (XXX; ; ). Can be empty ASTPointer<Statement> m_initExpression; - /// For statement's condition expresion. for(; XXX ; ). Can be empty + /// For statement's condition expression. for (; XXX ; ). Can be empty ASTPointer<Expression> m_condExpression; - /// For statement's loop expresion. for(;;XXX). Can be empty + /// For statement's loop expression. for (;;XXX). Can be empty ASTPointer<ExpressionStatement> m_loopExpression; /// The body of the loop ASTPointer<Statement> m_body; @@ -1250,13 +1272,12 @@ private: }; /** - * Definition of a variable as a statement inside a function. It requires a type name (which can - * also be "var") but the actual assignment can be missing. - * Examples: var a = 2; uint256 a; - * As a second form, multiple variables can be declared, cannot have a type and must be assigned - * right away. If the first or last component is unnamed, it can "consume" an arbitrary number - * of components. - * Examples: var (a, b) = f(); var (a,,,c) = g(); var (a,) = d(); + * Definition of one or more variables as a statement inside a function. + * If multiple variables are declared, a value has to be assigned directly. + * If only a single variable is declared, the value can be missing. + * Examples: + * uint[] memory a; uint a = 2; + * (uint a, bytes32 b, ) = f(); (, uint a, , StructName storage x) = g(); */ class VariableDeclarationStatement: public Statement { @@ -1271,13 +1292,14 @@ public: virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTConstVisitor& _visitor) const override; - VariableDeclarationStatementAnnotation& annotation() const override; - std::vector<ASTPointer<VariableDeclaration>> const& declarations() const { return m_variables; } Expression const* initialValue() const { return m_initialValue.get(); } private: /// List of variables, some of which can be empty pointers (unnamed components). + /// Note that the ``m_value`` member of these is unused. Instead, ``m_initialValue`` + /// below is used, because the initial value can be a single expression assigned + /// to all variables. std::vector<ASTPointer<VariableDeclaration>> m_variables; /// The assigned expression / initial value. ASTPointer<Expression> m_initialValue; @@ -1357,7 +1379,7 @@ public: Assignment( SourceLocation const& _location, ASTPointer<Expression> const& _leftHandSide, - Token::Value _assignmentOperator, + Token _assignmentOperator, ASTPointer<Expression> const& _rightHandSide ): Expression(_location), @@ -1365,18 +1387,18 @@ public: m_assigmentOperator(_assignmentOperator), m_rightHandSide(_rightHandSide) { - solAssert(Token::isAssignmentOp(_assignmentOperator), ""); + solAssert(TokenTraits::isAssignmentOp(_assignmentOperator), ""); } virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTConstVisitor& _visitor) const override; Expression const& leftHandSide() const { return *m_leftHandSide; } - Token::Value assignmentOperator() const { return m_assigmentOperator; } + Token assignmentOperator() const { return m_assigmentOperator; } Expression const& rightHandSide() const { return *m_rightHandSide; } private: ASTPointer<Expression> m_leftHandSide; - Token::Value m_assigmentOperator; + Token m_assigmentOperator; ASTPointer<Expression> m_rightHandSide; }; @@ -1419,7 +1441,7 @@ class UnaryOperation: public Expression public: UnaryOperation( SourceLocation const& _location, - Token::Value _operator, + Token _operator, ASTPointer<Expression> const& _subExpression, bool _isPrefix ): @@ -1428,17 +1450,17 @@ public: m_subExpression(_subExpression), m_isPrefix(_isPrefix) { - solAssert(Token::isUnaryOp(_operator), ""); + solAssert(TokenTraits::isUnaryOp(_operator), ""); } virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTConstVisitor& _visitor) const override; - Token::Value getOperator() const { return m_operator; } + Token getOperator() const { return m_operator; } bool isPrefixOperation() const { return m_isPrefix; } Expression const& subExpression() const { return *m_subExpression; } private: - Token::Value m_operator; + Token m_operator; ASTPointer<Expression> m_subExpression; bool m_isPrefix; }; @@ -1453,25 +1475,25 @@ public: BinaryOperation( SourceLocation const& _location, ASTPointer<Expression> const& _left, - Token::Value _operator, + Token _operator, ASTPointer<Expression> const& _right ): Expression(_location), m_left(_left), m_operator(_operator), m_right(_right) { - solAssert(Token::isBinaryOp(_operator) || Token::isCompareOp(_operator), ""); + solAssert(TokenTraits::isBinaryOp(_operator) || TokenTraits::isCompareOp(_operator), ""); } virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTConstVisitor& _visitor) const override; Expression const& leftExpression() const { return *m_left; } Expression const& rightExpression() const { return *m_right; } - Token::Value getOperator() const { return m_operator; } + Token getOperator() const { return m_operator; } BinaryOperationAnnotation& annotation() const override; private: ASTPointer<Expression> m_left; - Token::Value m_operator; + Token m_operator; ASTPointer<Expression> m_right; }; @@ -1631,21 +1653,21 @@ class Literal: public PrimaryExpression public: enum class SubDenomination { - None = Token::Illegal, - Wei = Token::SubWei, - Szabo = Token::SubSzabo, - Finney = Token::SubFinney, - Ether = Token::SubEther, - Second = Token::SubSecond, - Minute = Token::SubMinute, - Hour = Token::SubHour, - Day = Token::SubDay, - Week = Token::SubWeek, - Year = Token::SubYear + None = static_cast<int>(Token::Illegal), + Wei = static_cast<int>(Token::SubWei), + Szabo = static_cast<int>(Token::SubSzabo), + Finney = static_cast<int>(Token::SubFinney), + Ether = static_cast<int>(Token::SubEther), + Second = static_cast<int>(Token::SubSecond), + Minute = static_cast<int>(Token::SubMinute), + Hour = static_cast<int>(Token::SubHour), + Day = static_cast<int>(Token::SubDay), + Week = static_cast<int>(Token::SubWeek), + Year = static_cast<int>(Token::SubYear) }; Literal( SourceLocation const& _location, - Token::Value _token, + Token _token, ASTPointer<ASTString> const& _value, SubDenomination _sub = SubDenomination::None ): @@ -1653,10 +1675,12 @@ public: virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTConstVisitor& _visitor) const override; - Token::Value token() const { return m_token; } + Token token() const { return m_token; } /// @returns the non-parsed value of the literal ASTString const& value() const { return *m_value; } + ASTString valueWithoutUnderscores() const; + SubDenomination subDenomination() const { return m_subDenomination; } /// @returns true if this is a number with a hex prefix. @@ -1670,7 +1694,7 @@ public: std::string getChecksummedAddress() const; private: - Token::Value m_token; + Token m_token; ASTPointer<ASTString> m_value; SubDenomination m_subDenomination; }; diff --git a/libsolidity/ast/ASTAnnotations.h b/libsolidity/ast/ASTAnnotations.h index 5cbe42bd..e0b3f492 100644 --- a/libsolidity/ast/ASTAnnotations.h +++ b/libsolidity/ast/ASTAnnotations.h @@ -164,13 +164,6 @@ struct UserDefinedTypeNameAnnotation: TypeNameAnnotation ContractDefinition const* contractScope = nullptr; }; -struct VariableDeclarationStatementAnnotation: StatementAnnotation -{ - /// Information about which component of the value is assigned to which variable. - /// The pointer can be null to signify that the component is discarded. - std::vector<VariableDeclaration const*> assignments; -}; - struct ExpressionAnnotation: ASTAnnotation { /// Inferred type of the expression. diff --git a/libsolidity/ast/ASTJsonConverter.cpp b/libsolidity/ast/ASTJsonConverter.cpp index b8e00b60..2d26ce8a 100644 --- a/libsolidity/ast/ASTJsonConverter.cpp +++ b/libsolidity/ast/ASTJsonConverter.cpp @@ -126,7 +126,7 @@ string ASTJsonConverter::sourceLocationToString(SourceLocation const& _location) int length = -1; if (_location.start >= 0 && _location.end >= 0) length = _location.end - _location.start; - return std::to_string(_location.start) + ":" + std::to_string(length) + ":" + std::to_string(sourceIndex); + return to_string(_location.start) + ":" + to_string(length) + ":" + to_string(sourceIndex); } string ASTJsonConverter::namePathToString(std::vector<ASTString> const& _namePath) @@ -325,20 +325,19 @@ bool ASTJsonConverter::visit(FunctionDefinition const& _node) std::vector<pair<string, Json::Value>> attributes = { make_pair("name", _node.name()), make_pair("documentation", _node.documentation() ? Json::Value(*_node.documentation()) : Json::nullValue), - // FIXME: remove with next breaking release - make_pair(m_legacy ? "constant" : "isDeclaredConst", _node.stateMutability() <= StateMutability::View), - make_pair("payable", _node.isPayable()), + make_pair("kind", _node.isConstructor() ? "constructor" : (_node.isFallback() ? "fallback" : "function")), make_pair("stateMutability", stateMutabilityToString(_node.stateMutability())), make_pair("superFunction", idOrNull(_node.annotation().superFunction)), make_pair("visibility", Declaration::visibilityToString(_node.visibility())), make_pair("parameters", toJson(_node.parameterList())), - make_pair("isConstructor", _node.isConstructor()), make_pair("returnParameters", toJson(*_node.returnParameterList())), make_pair("modifiers", toJson(_node.modifiers())), make_pair("body", _node.isImplemented() ? toJson(_node.body()) : Json::nullValue), make_pair("implemented", _node.isImplemented()), make_pair("scope", idOrNull(_node.scope())) }; + if (m_legacy) + attributes.emplace_back("isConstructor", _node.isConstructor()); setJsonNode(_node, "FunctionDefinition", std::move(attributes)); return false; } @@ -397,10 +396,15 @@ bool ASTJsonConverter::visit(EventDefinition const& _node) bool ASTJsonConverter::visit(ElementaryTypeName const& _node) { - setJsonNode(_node, "ElementaryTypeName", { + std::vector<pair<string, Json::Value>> attributes = { make_pair("name", _node.typeName().toString()), make_pair("typeDescriptions", typePointerToJson(_node.annotation().type, true)) - }); + }; + + if (_node.stateMutability()) + attributes.emplace_back(make_pair("stateMutability", stateMutabilityToString(*_node.stateMutability()))); + + setJsonNode(_node, "ElementaryTypeName", std::move(attributes)); return false; } @@ -418,11 +422,8 @@ bool ASTJsonConverter::visit(UserDefinedTypeName const& _node) bool ASTJsonConverter::visit(FunctionTypeName const& _node) { setJsonNode(_node, "FunctionTypeName", { - make_pair("payable", _node.isPayable()), make_pair("visibility", Declaration::visibilityToString(_node.visibility())), make_pair("stateMutability", stateMutabilityToString(_node.stateMutability())), - // FIXME: remove with next breaking release - make_pair(m_legacy ? "constant" : "isDeclaredConst", _node.stateMutability() <= StateMutability::View), make_pair("parameterTypes", toJson(*_node.parameterTypeList())), make_pair("returnParameterTypes", toJson(*_node.returnParameterTypeList())), make_pair("typeDescriptions", typePointerToJson(_node.annotation().type, true)) @@ -458,7 +459,7 @@ bool ASTJsonConverter::visit(InlineAssembly const& _node) if (it.first) { Json::Value tuple(Json::objectValue); - tuple[it.first->name] = inlineAssemblyIdentifierToJson(it); + tuple[it.first->name.str()] = inlineAssemblyIdentifierToJson(it); externalReferences.append(tuple); } } @@ -555,8 +556,8 @@ bool ASTJsonConverter::visit(EmitStatement const& _node) bool ASTJsonConverter::visit(VariableDeclarationStatement const& _node) { Json::Value varDecs(Json::arrayValue); - for (auto const& v: _node.annotation().assignments) - appendMove(varDecs, idOrNull(v)); + for (auto const& v: _node.declarations()) + appendMove(varDecs, idOrNull(v.get())); setJsonNode(_node, "VariableDeclarationStatement", { make_pair("assignments", std::move(varDecs)), make_pair("declarations", toJson(_node.declarations())), @@ -588,7 +589,7 @@ bool ASTJsonConverter::visit(Conditional const& _node) bool ASTJsonConverter::visit(Assignment const& _node) { std::vector<pair<string, Json::Value>> attributes = { - make_pair("operator", Token::toString(_node.assignmentOperator())), + make_pair("operator", TokenTraits::toString(_node.assignmentOperator())), make_pair("leftHandSide", toJson(_node.leftHandSide())), make_pair("rightHandSide", toJson(_node.rightHandSide())) }; @@ -612,7 +613,7 @@ bool ASTJsonConverter::visit(UnaryOperation const& _node) { std::vector<pair<string, Json::Value>> attributes = { make_pair("prefix", _node.isPrefixOperation()), - make_pair("operator", Token::toString(_node.getOperator())), + make_pair("operator", TokenTraits::toString(_node.getOperator())), make_pair("subExpression", toJson(_node.subExpression())) }; appendExpressionAttributes(attributes, _node.annotation()); @@ -623,7 +624,7 @@ bool ASTJsonConverter::visit(UnaryOperation const& _node) bool ASTJsonConverter::visit(BinaryOperation const& _node) { std::vector<pair<string, Json::Value>> attributes = { - make_pair("operator", Token::toString(_node.getOperator())), + make_pair("operator", TokenTraits::toString(_node.getOperator())), make_pair("leftExpression", toJson(_node.leftExpression())), make_pair("rightExpression", toJson(_node.rightExpression())), make_pair("commonType", typePointerToJson(_node.annotation().commonType)), @@ -718,7 +719,7 @@ bool ASTJsonConverter::visit(Literal const& _node) Json::Value value{_node.value()}; if (!dev::validateUTF8(_node.value())) value = Json::nullValue; - Token::Value subdenomination = Token::Value(_node.subDenomination()); + Token subdenomination = Token(_node.subDenomination()); std::vector<pair<string, Json::Value>> attributes = { make_pair(m_legacy ? "token" : "kind", literalTokenKind(_node.token())), make_pair("value", value), @@ -727,7 +728,7 @@ bool ASTJsonConverter::visit(Literal const& _node) "subdenomination", subdenomination == Token::Illegal ? Json::nullValue : - Json::Value{Token::toString(subdenomination)} + Json::Value{TokenTraits::toString(subdenomination)} ) }; appendExpressionAttributes(attributes, _node.annotation()); @@ -745,15 +746,17 @@ string ASTJsonConverter::location(VariableDeclaration::Location _location) { switch (_location) { - case VariableDeclaration::Location::Default: + case VariableDeclaration::Location::Unspecified: return "default"; case VariableDeclaration::Location::Storage: return "storage"; case VariableDeclaration::Location::Memory: return "memory"; - default: - solAssert(false, "Unknown declaration location."); + case VariableDeclaration::Location::CallData: + return "calldata"; } + // To make the compiler happy + return {}; } string ASTJsonConverter::contractKind(ContractDefinition::ContractKind _kind) @@ -766,9 +769,10 @@ string ASTJsonConverter::contractKind(ContractDefinition::ContractKind _kind) return "contract"; case ContractDefinition::ContractKind::Library: return "library"; - default: - solAssert(false, "Unknown kind of contract."); } + + // To make the compiler happy + return {}; } string ASTJsonConverter::functionCallKind(FunctionCallKind _kind) @@ -786,7 +790,7 @@ string ASTJsonConverter::functionCallKind(FunctionCallKind _kind) } } -string ASTJsonConverter::literalTokenKind(Token::Value _token) +string ASTJsonConverter::literalTokenKind(Token _token) { switch (_token) { diff --git a/libsolidity/ast/ASTJsonConverter.h b/libsolidity/ast/ASTJsonConverter.h index 29712f3b..8429708c 100644 --- a/libsolidity/ast/ASTJsonConverter.h +++ b/libsolidity/ast/ASTJsonConverter.h @@ -134,7 +134,7 @@ private: static std::string location(VariableDeclaration::Location _location); static std::string contractKind(ContractDefinition::ContractKind _kind); static std::string functionCallKind(FunctionCallKind _kind); - static std::string literalTokenKind(Token::Value _token); + static std::string literalTokenKind(Token _token); static std::string type(Expression const& _expression); static std::string type(VariableDeclaration const& _varDecl); static int nodeId(ASTNode const& _node) diff --git a/libsolidity/ast/ASTPrinter.cpp b/libsolidity/ast/ASTPrinter.cpp index 4273f225..255cb9be 100644 --- a/libsolidity/ast/ASTPrinter.cpp +++ b/libsolidity/ast/ASTPrinter.cpp @@ -289,7 +289,7 @@ bool ASTPrinter::visit(Conditional const& _node) bool ASTPrinter::visit(Assignment const& _node) { - writeLine(string("Assignment using operator ") + Token::toString(_node.assignmentOperator())); + writeLine(string("Assignment using operator ") + TokenTraits::toString(_node.assignmentOperator())); printType(_node); printSourcePart(_node); return goDeeper(); @@ -306,7 +306,7 @@ bool ASTPrinter::visit(TupleExpression const& _node) bool ASTPrinter::visit(UnaryOperation const& _node) { writeLine(string("UnaryOperation (") + (_node.isPrefixOperation() ? "prefix" : "postfix") + - ") " + Token::toString(_node.getOperator())); + ") " + TokenTraits::toString(_node.getOperator())); printType(_node); printSourcePart(_node); return goDeeper(); @@ -314,7 +314,7 @@ bool ASTPrinter::visit(UnaryOperation const& _node) bool ASTPrinter::visit(BinaryOperation const& _node) { - writeLine(string("BinaryOperation using operator ") + Token::toString(_node.getOperator())); + writeLine(string("BinaryOperation using operator ") + TokenTraits::toString(_node.getOperator())); printType(_node); printSourcePart(_node); return goDeeper(); @@ -370,7 +370,7 @@ bool ASTPrinter::visit(ElementaryTypeNameExpression const& _node) bool ASTPrinter::visit(Literal const& _node) { - char const* tokenString = Token::toString(_node.token()); + char const* tokenString = TokenTraits::toString(_node.token()); if (!tokenString) tokenString = "[no token]"; writeLine(string("Literal, token: ") + tokenString + " value: " + _node.value()); diff --git a/libsolidity/ast/ASTUtils.cpp b/libsolidity/ast/ASTUtils.cpp deleted file mode 100644 index 7d180490..00000000 --- a/libsolidity/ast/ASTUtils.cpp +++ /dev/null @@ -1,48 +0,0 @@ -/* - This file is part of solidity. - - solidity is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - solidity is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with solidity. If not, see <http://www.gnu.org/licenses/>. -*/ -/** - * @author Christian <c@ethdev.com> - * @date 2015 - * Utilities to work with the AST. - */ - -#include <libsolidity/ast/ASTUtils.h> - -using namespace std; -using namespace dev; -using namespace dev::solidity; - - - -ASTNode const* LocationFinder::leastUpperBound() -{ - m_bestMatch = nullptr; - for (ASTNode const* rootNode: m_rootNodes) - rootNode->accept(*this); - - return m_bestMatch; -} - -bool LocationFinder::visitNode(const ASTNode& _node) -{ - if (_node.location().contains(m_location)) - { - m_bestMatch = &_node; - return true; - } - return false; -} diff --git a/libsolidity/ast/ASTUtils.h b/libsolidity/ast/ASTUtils.h deleted file mode 100644 index f7cacf3e..00000000 --- a/libsolidity/ast/ASTUtils.h +++ /dev/null @@ -1,54 +0,0 @@ -/* - This file is part of solidity. - - solidity is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - solidity is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with solidity. If not, see <http://www.gnu.org/licenses/>. -*/ -/** - * @author Christian <c@ethdev.com> - * @date 2015 - * Utilities to work with the AST. - */ - -#pragma once - -#include <libevmasm/SourceLocation.h> -#include <libsolidity/ast/ASTVisitor.h> - -namespace dev -{ -namespace solidity -{ - -class LocationFinder: private ASTConstVisitor -{ -public: - LocationFinder(SourceLocation const& _location, std::vector<ASTNode const*> _rootNodes): - m_rootNodes(_rootNodes), m_location(_location) - { - } - - /// @returns the "closest" (in the sense of most-leafward) AST node which is a descendant of - /// _node and whose source location contains _location. - ASTNode const* leastUpperBound(); - -private: - bool visitNode(ASTNode const& _node); - - std::vector<ASTNode const*> m_rootNodes; - SourceLocation m_location; - ASTNode const* m_bestMatch = nullptr; -}; - -} -} diff --git a/libsolidity/ast/ExperimentalFeatures.h b/libsolidity/ast/ExperimentalFeatures.h index 30ea7ec5..54aad573 100644 --- a/libsolidity/ast/ExperimentalFeatures.h +++ b/libsolidity/ast/ExperimentalFeatures.h @@ -29,9 +29,8 @@ namespace solidity enum class ExperimentalFeature { - ABIEncoderV2, // new ABI encoder that makes use of JULIA + ABIEncoderV2, // new ABI encoder that makes use of Yul SMTChecker, - V050, // v0.5.0 breaking changes Test, TestOnlyAnalysis }; @@ -40,14 +39,12 @@ static const std::map<ExperimentalFeature, bool> ExperimentalFeatureOnlyAnalysis { { ExperimentalFeature::SMTChecker, true }, { ExperimentalFeature::TestOnlyAnalysis, true }, - { ExperimentalFeature::V050, true } }; static const std::map<std::string, ExperimentalFeature> ExperimentalFeatureNames = { { "ABIEncoderV2", ExperimentalFeature::ABIEncoderV2 }, { "SMTChecker", ExperimentalFeature::SMTChecker }, - { "v0.5.0", ExperimentalFeature::V050 }, { "__test", ExperimentalFeature::Test }, { "__testOnlyAnalysis", ExperimentalFeature::TestOnlyAnalysis }, }; diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp index 60e3183c..4b31d2e8 100644 --- a/libsolidity/ast/Types.cpp +++ b/libsolidity/ast/Types.cpp @@ -26,17 +26,20 @@ #include <libdevcore/CommonIO.h> #include <libdevcore/CommonData.h> -#include <libdevcore/SHA3.h> +#include <libdevcore/Keccak256.h> #include <libdevcore/UTF8.h> #include <libdevcore/Algorithms.h> #include <boost/algorithm/string/join.hpp> #include <boost/algorithm/string/replace.hpp> #include <boost/algorithm/string/predicate.hpp> +#include <boost/algorithm/string/classification.hpp> +#include <boost/algorithm/string/split.hpp> #include <boost/range/adaptor/reversed.hpp> #include <boost/range/algorithm/copy.hpp> #include <boost/range/adaptor/sliced.hpp> #include <boost/range/adaptor/transformed.hpp> +#include <boost/algorithm/string.hpp> #include <limits> @@ -167,15 +170,6 @@ pair<u256, unsigned> const* StorageOffsets::offset(size_t _index) const return nullptr; } -MemberList& MemberList::operator=(MemberList&& _other) -{ - solAssert(&_other != this, ""); - - m_memberTypes = move(_other.m_memberTypes); - m_storageOffsets = move(_other.m_storageOffsets); - return *this; -} - void MemberList::combine(MemberList const & _other) { m_memberTypes += _other.m_memberTypes; @@ -261,13 +255,24 @@ string Type::escapeIdentifier(string const& _identifier) return ret; } +string Type::identifier() const +{ + string ret = escapeIdentifier(richIdentifier()); + solAssert(ret.find_first_of("0123456789") != 0, "Identifier cannot start with a number."); + solAssert( + ret.find_first_not_of("0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMONPQRSTUVWXYZ_$") == string::npos, + "Identifier contains invalid characters." + ); + return ret; +} + TypePointer Type::fromElementaryTypeName(ElementaryTypeNameToken const& _type) { - solAssert(Token::isElementaryTypeName(_type.token()), + solAssert(TokenTraits::isElementaryTypeName(_type.token()), "Expected an elementary type name but got " + _type.toString() ); - Token::Value token = _type.token(); + Token token = _type.token(); unsigned m = _type.firstNumber(); unsigned n = _type.secondNumber(); @@ -294,7 +299,7 @@ TypePointer Type::fromElementaryTypeName(ElementaryTypeNameToken const& _type) case Token::Byte: return make_shared<FixedBytesType>(1); case Token::Address: - return make_shared<IntegerType>(160, IntegerType::Modifier::Address); + return make_shared<AddressType>(StateMutability::NonPayable); case Token::Bool: return make_shared<BoolType>(); case Token::Bytes: @@ -312,22 +317,45 @@ TypePointer Type::fromElementaryTypeName(ElementaryTypeNameToken const& _type) TypePointer Type::fromElementaryTypeName(string const& _name) { - string name = _name; - DataLocation location = DataLocation::Storage; - if (boost::algorithm::ends_with(name, " memory")) - { - name = name.substr(0, name.length() - 7); - location = DataLocation::Memory; - } - unsigned short firstNum; - unsigned short secondNum; - Token::Value token; - tie(token, firstNum, secondNum) = Token::fromIdentifierOrKeyword(name); + vector<string> nameParts; + boost::split(nameParts, _name, boost::is_any_of(" ")); + solAssert(nameParts.size() == 1 || nameParts.size() == 2, "Cannot parse elementary type: " + _name); + Token token; + unsigned short firstNum, secondNum; + tie(token, firstNum, secondNum) = TokenTraits::fromIdentifierOrKeyword(nameParts[0]); auto t = fromElementaryTypeName(ElementaryTypeNameToken(token, firstNum, secondNum)); if (auto* ref = dynamic_cast<ReferenceType const*>(t.get())) + { + DataLocation location = DataLocation::Storage; + if (nameParts.size() == 2) + { + if (nameParts[1] == "storage") + location = DataLocation::Storage; + else if (nameParts[1] == "calldata") + location = DataLocation::CallData; + else if (nameParts[1] == "memory") + location = DataLocation::Memory; + else + solAssert(false, "Unknown data location: " + nameParts[1]); + } return ref->copyForLocation(location, true); + } + else if (t->category() == Type::Category::Address) + { + if (nameParts.size() == 2) + { + if (nameParts[1] == "payable") + return make_shared<AddressType>(StateMutability::Payable); + else + solAssert(false, "Invalid state mutability for address type: " + nameParts[1]); + } + return make_shared<AddressType>(StateMutability::NonPayable); + } else + { + solAssert(nameParts.size() == 1, "Storage location suffix only allowed for reference types"); return t; + } } TypePointer Type::forLiteral(Literal const& _literal) @@ -338,13 +366,7 @@ TypePointer Type::forLiteral(Literal const& _literal) case Token::FalseLiteral: return make_shared<BoolType>(); case Token::Number: - { - tuple<bool, rational> validLiteral = RationalNumberType::isValidLiteral(_literal); - if (get<0>(validLiteral) == true) - return make_shared<RationalNumberType>(get<1>(validLiteral)); - else - return TypePointer(); - } + return RationalNumberType::forLiteral(_literal); case Token::StringLiteral: return make_shared<StringLiteralType>(_literal); default: @@ -376,6 +398,27 @@ MemberList const& Type::members(ContractDefinition const* _currentScope) const return *m_members[_currentScope]; } +TypePointer Type::fullEncodingType(bool _inLibraryCall, bool _encoderV2, bool _packed) const +{ + TypePointer encodingType = mobileType(); + if (encodingType) + encodingType = encodingType->interfaceType(_inLibraryCall); + if (encodingType) + encodingType = encodingType->encodingType(); + // Structs are fine in the following circumstances: + // - ABIv2 without packed encoding or, + // - storage struct for a library + if (_inLibraryCall && encodingType->dataStoredIn(DataLocation::Storage)) + return encodingType; + TypePointer baseType = encodingType; + while (auto const* arrayType = dynamic_cast<ArrayType const*>(baseType.get())) + baseType = arrayType->baseType(); + if (dynamic_cast<StructType const*>(baseType.get())) + if (!_encoderV2 || _packed) + return TypePointer(); + return encodingType; +} + MemberList::MemberMap Type::boundFunctions(Type const& _type, ContractDefinition const& _scope) { // Normalise data location of type. @@ -407,16 +450,108 @@ MemberList::MemberMap Type::boundFunctions(Type const& _type, ContractDefinition return members; } +AddressType::AddressType(StateMutability _stateMutability): + m_stateMutability(_stateMutability) +{ + solAssert(m_stateMutability == StateMutability::Payable || m_stateMutability == StateMutability::NonPayable, ""); +} + +string AddressType::richIdentifier() const +{ + if (m_stateMutability == StateMutability::Payable) + return "t_address_payable"; + else + return "t_address"; +} + +bool AddressType::isImplicitlyConvertibleTo(Type const& _other) const +{ + if (_other.category() != category()) + return false; + AddressType const& other = dynamic_cast<AddressType const&>(_other); + + return other.m_stateMutability <= m_stateMutability; +} + +bool AddressType::isExplicitlyConvertibleTo(Type const& _convertTo) const +{ + if (auto const* contractType = dynamic_cast<ContractType const*>(&_convertTo)) + return (m_stateMutability >= StateMutability::Payable) || !contractType->isPayable(); + return isImplicitlyConvertibleTo(_convertTo) || + _convertTo.category() == Category::Integer || + (_convertTo.category() == Category::FixedBytes && 160 == dynamic_cast<FixedBytesType const&>(_convertTo).numBytes() * 8); +} + +string AddressType::toString(bool) const +{ + if (m_stateMutability == StateMutability::Payable) + return "address payable"; + else + return "address"; +} + +string AddressType::canonicalName() const +{ + return "address"; +} + +u256 AddressType::literalValue(Literal const* _literal) const +{ + solAssert(_literal, ""); + solAssert(_literal->value().substr(0, 2) == "0x", ""); + return u256(_literal->valueWithoutUnderscores()); +} + +TypePointer AddressType::unaryOperatorResult(Token _operator) const +{ + return _operator == Token::Delete ? make_shared<TupleType>() : TypePointer(); +} + + +TypePointer AddressType::binaryOperatorResult(Token _operator, TypePointer const& _other) const +{ + // Addresses can only be compared. + if (!TokenTraits::isCompareOp(_operator)) + return TypePointer(); + + return Type::commonType(shared_from_this(), _other); +} + +bool AddressType::operator==(Type const& _other) const +{ + if (_other.category() != category()) + return false; + AddressType const& other = dynamic_cast<AddressType const&>(_other); + return other.m_stateMutability == m_stateMutability; +} + +MemberList::MemberMap AddressType::nativeMembers(ContractDefinition const*) const +{ + MemberList::MemberMap members = { + {"balance", make_shared<IntegerType>(256)}, + {"call", make_shared<FunctionType>(strings{"bytes memory"}, strings{"bool", "bytes memory"}, FunctionType::Kind::BareCall, false, StateMutability::Payable)}, + {"callcode", make_shared<FunctionType>(strings{"bytes memory"}, strings{"bool", "bytes memory"}, FunctionType::Kind::BareCallCode, false, StateMutability::Payable)}, + {"delegatecall", make_shared<FunctionType>(strings{"bytes memory"}, strings{"bool", "bytes memory"}, FunctionType::Kind::BareDelegateCall, false)}, + {"staticcall", make_shared<FunctionType>(strings{"bytes memory"}, strings{"bool", "bytes memory"}, FunctionType::Kind::BareStaticCall, false, StateMutability::View)} + }; + if (m_stateMutability == StateMutability::Payable) + { + members.emplace_back(MemberList::Member{"send", make_shared<FunctionType>(strings{"uint"}, strings{"bool"}, FunctionType::Kind::Send)}); + members.emplace_back(MemberList::Member{"transfer", make_shared<FunctionType>(strings{"uint"}, strings(), FunctionType::Kind::Transfer)}); + } + return members; +} + namespace { -bool isValidShiftAndAmountType(Token::Value _operator, Type const& _shiftAmountType) +bool isValidShiftAndAmountType(Token _operator, Type const& _shiftAmountType) { // Disable >>> here. if (_operator == Token::SHR) return false; else if (IntegerType const* otherInt = dynamic_cast<decltype(otherInt)>(&_shiftAmountType)) - return !otherInt->isAddress(); + return true; else if (RationalNumberType const* otherRat = dynamic_cast<decltype(otherRat)>(&_shiftAmountType)) return !otherRat->isFractional() && otherRat->integerType() && !otherRat->integerType()->isSigned(); else @@ -428,8 +563,6 @@ bool isValidShiftAndAmountType(Token::Value _operator, Type const& _shiftAmountT IntegerType::IntegerType(unsigned _bits, IntegerType::Modifier _modifier): m_bits(_bits), m_modifier(_modifier) { - if (isAddress()) - solAssert(m_bits == 160, ""); solAssert( m_bits > 0 && m_bits <= 256 && m_bits % 8 == 0, "Invalid bit number for integer type: " + dev::toString(m_bits) @@ -438,10 +571,7 @@ IntegerType::IntegerType(unsigned _bits, IntegerType::Modifier _modifier): string IntegerType::richIdentifier() const { - if (isAddress()) - return "t_address"; - else - return "t_" + string(isSigned() ? "" : "u") + "int" + std::to_string(numBits()); + return "t_" + string(isSigned() ? "" : "u") + "int" + to_string(numBits()); } bool IntegerType::isImplicitlyConvertibleTo(Type const& _convertTo) const @@ -451,8 +581,6 @@ bool IntegerType::isImplicitlyConvertibleTo(Type const& _convertTo) const IntegerType const& convertTo = dynamic_cast<IntegerType const&>(_convertTo); if (convertTo.m_bits < m_bits) return false; - if (isAddress()) - return convertTo.isAddress(); else if (isSigned()) return convertTo.isSigned(); else @@ -461,11 +589,7 @@ bool IntegerType::isImplicitlyConvertibleTo(Type const& _convertTo) const else if (_convertTo.category() == Category::FixedPoint) { FixedPointType const& convertTo = dynamic_cast<FixedPointType const&>(_convertTo); - - if (isAddress()) - return false; - else - return maxValue() <= convertTo.maxIntegerValue() && minValue() >= convertTo.minIntegerValue(); + return maxValue() <= convertTo.maxIntegerValue() && minValue() >= convertTo.minIntegerValue(); } else return false; @@ -474,21 +598,19 @@ bool IntegerType::isImplicitlyConvertibleTo(Type const& _convertTo) const bool IntegerType::isExplicitlyConvertibleTo(Type const& _convertTo) const { return _convertTo.category() == category() || + _convertTo.category() == Category::Address || _convertTo.category() == Category::Contract || _convertTo.category() == Category::Enum || - _convertTo.category() == Category::FixedBytes || + (_convertTo.category() == Category::FixedBytes && numBits() == dynamic_cast<FixedBytesType const&>(_convertTo).numBytes() * 8) || _convertTo.category() == Category::FixedPoint; } -TypePointer IntegerType::unaryOperatorResult(Token::Value _operator) const +TypePointer IntegerType::unaryOperatorResult(Token _operator) const { // "delete" is ok for all integer types if (_operator == Token::Delete) return make_shared<TupleType>(); - // no further unary operators for addresses - else if (isAddress()) - return TypePointer(); - // for non-address integers, we allow +, -, ++ and -- + // we allow +, -, ++ and -- else if (_operator == Token::Add || _operator == Token::Sub || _operator == Token::Inc || _operator == Token::Dec || _operator == Token::BitNot) @@ -507,20 +629,10 @@ bool IntegerType::operator==(Type const& _other) const string IntegerType::toString(bool) const { - if (isAddress()) - return "address"; string prefix = isSigned() ? "int" : "uint"; return prefix + dev::toString(m_bits); } -u256 IntegerType::literalValue(Literal const* _literal) const -{ - solAssert(m_modifier == Modifier::Address, ""); - solAssert(_literal, ""); - solAssert(_literal->value().substr(0, 2) == "0x", ""); - return u256(_literal->value()); -} - bigint IntegerType::minValue() const { if (isSigned()) @@ -537,7 +649,7 @@ bigint IntegerType::maxValue() const return (bigint(1) << m_bits) - 1; } -TypePointer IntegerType::binaryOperatorResult(Token::Value _operator, TypePointer const& _other) const +TypePointer IntegerType::binaryOperatorResult(Token _operator, TypePointer const& _other) const { if ( _other->category() != Category::RationalNumber && @@ -545,31 +657,26 @@ TypePointer IntegerType::binaryOperatorResult(Token::Value _operator, TypePointe _other->category() != category() ) return TypePointer(); - if (Token::isShiftOp(_operator)) + if (TokenTraits::isShiftOp(_operator)) { // Shifts are not symmetric with respect to the type - if (isAddress()) - return TypePointer(); if (isValidShiftAndAmountType(_operator, *_other)) return shared_from_this(); else return TypePointer(); } - auto commonType = Type::commonType(shared_from_this(), _other); //might be a integer or fixed point + auto commonType = Type::commonType(shared_from_this(), _other); //might be an integer or fixed point if (!commonType) return TypePointer(); // All integer types can be compared - if (Token::isCompareOp(_operator)) + if (TokenTraits::isCompareOp(_operator)) return commonType; - if (Token::isBooleanOp(_operator)) + if (TokenTraits::isBooleanOp(_operator)) return TypePointer(); if (auto intType = dynamic_pointer_cast<IntegerType const>(commonType)) { - // Nothing else can be done with addresses - if (intType->isAddress()) - return TypePointer(); // Signed EXP is not allowed if (Token::Exp == _operator && intType->isSigned()) return TypePointer(); @@ -580,21 +687,6 @@ TypePointer IntegerType::binaryOperatorResult(Token::Value _operator, TypePointe return commonType; } -MemberList::MemberMap IntegerType::nativeMembers(ContractDefinition const*) const -{ - if (isAddress()) - return { - {"balance", make_shared<IntegerType>(256)}, - {"call", make_shared<FunctionType>(strings(), strings{"bool"}, FunctionType::Kind::BareCall, true, StateMutability::Payable)}, - {"callcode", make_shared<FunctionType>(strings(), strings{"bool"}, FunctionType::Kind::BareCallCode, true, StateMutability::Payable)}, - {"delegatecall", make_shared<FunctionType>(strings(), strings{"bool"}, FunctionType::Kind::BareDelegateCall, true)}, - {"send", make_shared<FunctionType>(strings{"uint"}, strings{"bool"}, FunctionType::Kind::Send)}, - {"transfer", make_shared<FunctionType>(strings{"uint"}, strings(), FunctionType::Kind::Transfer)} - }; - else - return MemberList::MemberMap(); -} - FixedPointType::FixedPointType(unsigned _totalBits, unsigned _fractionalDigits, FixedPointType::Modifier _modifier): m_totalBits(_totalBits), m_fractionalDigits(_fractionalDigits), m_modifier(_modifier) { @@ -607,7 +699,7 @@ FixedPointType::FixedPointType(unsigned _totalBits, unsigned _fractionalDigits, string FixedPointType::richIdentifier() const { - return "t_" + string(isSigned() ? "" : "u") + "fixed" + std::to_string(m_totalBits) + "x" + std::to_string(m_fractionalDigits); + return "t_" + string(isSigned() ? "" : "u") + "fixed" + to_string(m_totalBits) + "x" + to_string(m_fractionalDigits); } bool FixedPointType::isImplicitlyConvertibleTo(Type const& _convertTo) const @@ -625,11 +717,10 @@ bool FixedPointType::isImplicitlyConvertibleTo(Type const& _convertTo) const bool FixedPointType::isExplicitlyConvertibleTo(Type const& _convertTo) const { - return _convertTo.category() == category() || - (_convertTo.category() == Category::Integer && !dynamic_cast<IntegerType const&>(_convertTo).isAddress()); + return _convertTo.category() == category() || _convertTo.category() == Category::Integer; } -TypePointer FixedPointType::unaryOperatorResult(Token::Value _operator) const +TypePointer FixedPointType::unaryOperatorResult(Token _operator) const { switch(_operator) { @@ -664,7 +755,7 @@ string FixedPointType::toString(bool) const bigint FixedPointType::maxIntegerValue() const { bigint maxValue = (bigint(1) << (m_totalBits - (isSigned() ? 1 : 0))) - 1; - return maxValue / pow(bigint(10), m_fractionalDigits); + return maxValue / boost::multiprecision::pow(bigint(10), m_fractionalDigits); } bigint FixedPointType::minIntegerValue() const @@ -672,13 +763,13 @@ bigint FixedPointType::minIntegerValue() const if (isSigned()) { bigint minValue = -(bigint(1) << (m_totalBits - (isSigned() ? 1 : 0))); - return minValue / pow(bigint(10), m_fractionalDigits); + return minValue / boost::multiprecision::pow(bigint(10), m_fractionalDigits); } else return bigint(0); } -TypePointer FixedPointType::binaryOperatorResult(Token::Value _operator, TypePointer const& _other) const +TypePointer FixedPointType::binaryOperatorResult(Token _operator, TypePointer const& _other) const { auto commonType = Type::commonType(shared_from_this(), _other); @@ -686,9 +777,9 @@ TypePointer FixedPointType::binaryOperatorResult(Token::Value _operator, TypePoi return TypePointer(); // All fixed types can be compared - if (Token::isCompareOp(_operator)) + if (TokenTraits::isCompareOp(_operator)) return commonType; - if (Token::isBitOp(_operator) || Token::isBooleanOp(_operator) || _operator == Token::Exp) + if (TokenTraits::isBitOp(_operator) || TokenTraits::isBooleanOp(_operator) || _operator == Token::Exp) return TypePointer(); return commonType; } @@ -741,36 +832,60 @@ tuple<bool, rational> RationalNumberType::parseRational(string const& _value) } } +TypePointer RationalNumberType::forLiteral(Literal const& _literal) +{ + solAssert(_literal.token() == Token::Number, ""); + tuple<bool, rational> validLiteral = isValidLiteral(_literal); + if (get<0>(validLiteral)) + { + TypePointer compatibleBytesType; + if (_literal.isHexNumber()) + { + size_t const digitCount = _literal.valueWithoutUnderscores().length() - 2; + if (digitCount % 2 == 0 && (digitCount / 2) <= 32) + compatibleBytesType = make_shared<FixedBytesType>(digitCount / 2); + } + + return make_shared<RationalNumberType>(get<1>(validLiteral), compatibleBytesType); + } + return TypePointer(); +} + tuple<bool, rational> RationalNumberType::isValidLiteral(Literal const& _literal) { rational value; try { - auto expPoint = find(_literal.value().begin(), _literal.value().end(), 'e'); - if (expPoint == _literal.value().end()) - expPoint = find(_literal.value().begin(), _literal.value().end(), 'E'); + ASTString valueString = _literal.valueWithoutUnderscores(); + + auto expPoint = find(valueString.begin(), valueString.end(), 'e'); + if (expPoint == valueString.end()) + expPoint = find(valueString.begin(), valueString.end(), 'E'); - if (boost::starts_with(_literal.value(), "0x")) + if (boost::starts_with(valueString, "0x")) { // process as hex - value = bigint(_literal.value()); + value = bigint(valueString); } - else if (expPoint != _literal.value().end()) + else if (expPoint != valueString.end()) { - // Parse base and exponent. Checks numeric limit. - bigint exp = bigint(string(expPoint + 1, _literal.value().end())); + // Parse mantissa and exponent. Checks numeric limit. + tuple<bool, rational> mantissa = parseRational(string(valueString.begin(), expPoint)); - if (exp > numeric_limits<int32_t>::max() || exp < numeric_limits<int32_t>::min()) + if (!get<0>(mantissa)) return make_tuple(false, rational(0)); + value = get<1>(mantissa); - uint32_t expAbs = bigint(abs(exp)).convert_to<uint32_t>(); - + // 0E... is always zero. + if (value == 0) + return make_tuple(true, rational(0)); - tuple<bool, rational> base = parseRational(string(_literal.value().begin(), expPoint)); + bigint exp = bigint(string(expPoint + 1, valueString.end())); - if (!get<0>(base)) + if (exp > numeric_limits<int32_t>::max() || exp < numeric_limits<int32_t>::min()) return make_tuple(false, rational(0)); - value = get<1>(base); + + uint32_t expAbs = bigint(abs(exp)).convert_to<uint32_t>(); if (exp < 0) { @@ -794,7 +909,7 @@ tuple<bool, rational> RationalNumberType::isValidLiteral(Literal const& _literal else { // parse as rational number - tuple<bool, rational> tmp = parseRational(_literal.value()); + tuple<bool, rational> tmp = parseRational(valueString); if (!get<0>(tmp)) return tmp; value = get<1>(tmp); @@ -842,52 +957,56 @@ tuple<bool, rational> RationalNumberType::isValidLiteral(Literal const& _literal bool RationalNumberType::isImplicitlyConvertibleTo(Type const& _convertTo) const { - if (_convertTo.category() == Category::Integer) + switch (_convertTo.category()) + { + case Category::Integer: { - if (m_value == rational(0)) - return true; if (isFractional()) return false; IntegerType const& targetType = dynamic_cast<IntegerType const&>(_convertTo); + if (m_value == rational(0)) + return true; unsigned forSignBit = (targetType.isSigned() ? 1 : 0); if (m_value > rational(0)) { if (m_value.numerator() <= (u256(-1) >> (256 - targetType.numBits() + forSignBit))) return true; + return false; + } + if (targetType.isSigned()) + { + if (-m_value.numerator() <= (u256(1) << (targetType.numBits() - forSignBit))) + return true; } - else if (targetType.isSigned() && -m_value.numerator() <= (u256(1) << (targetType.numBits() - forSignBit))) - return true; return false; } - else if (_convertTo.category() == Category::FixedPoint) + case Category::FixedPoint: { if (auto fixed = fixedPointType()) return fixed->isImplicitlyConvertibleTo(_convertTo); - else - return false; + return false; } - else if (_convertTo.category() == Category::FixedBytes) - { - FixedBytesType const& fixedBytes = dynamic_cast<FixedBytesType const&>(_convertTo); - if (!isFractional()) - { - if (integerType()) - return fixedBytes.numBytes() * 8 >= integerType()->numBits(); - return false; - } - else - return false; + case Category::FixedBytes: + return (m_value == rational(0)) || (m_compatibleBytesType && *m_compatibleBytesType == _convertTo); + default: + return false; } - return false; } bool RationalNumberType::isExplicitlyConvertibleTo(Type const& _convertTo) const { - TypePointer mobType = mobileType(); - return mobType && mobType->isExplicitlyConvertibleTo(_convertTo); + if (isImplicitlyConvertibleTo(_convertTo)) + return true; + else if (_convertTo.category() != Category::FixedBytes) + { + TypePointer mobType = mobileType(); + return (mobType && mobType->isExplicitlyConvertibleTo(_convertTo)); + } + else + return false; } -TypePointer RationalNumberType::unaryOperatorResult(Token::Value _operator) const +TypePointer RationalNumberType::unaryOperatorResult(Token _operator) const { rational value; switch (_operator) @@ -911,7 +1030,7 @@ TypePointer RationalNumberType::unaryOperatorResult(Token::Value _operator) cons return make_shared<RationalNumberType>(value); } -TypePointer RationalNumberType::binaryOperatorResult(Token::Value _operator, TypePointer const& _other) const +TypePointer RationalNumberType::binaryOperatorResult(Token _operator, TypePointer const& _other) const { if (_other->category() == Category::Integer || _other->category() == Category::FixedPoint) { @@ -924,9 +1043,9 @@ TypePointer RationalNumberType::binaryOperatorResult(Token::Value _operator, Typ return TypePointer(); RationalNumberType const& other = dynamic_cast<RationalNumberType const&>(*_other); - if (Token::isCompareOp(_operator)) + if (TokenTraits::isCompareOp(_operator)) { - // Since we do not have a "BoolConstantType", we have to do the acutal comparison + // Since we do not have a "BoolConstantType", we have to do the actual comparison // at runtime and convert to mobile typse first. Such a comparison is not a very common // use-case and will be optimized away. TypePointer thisMobile = mobileType(); @@ -982,10 +1101,9 @@ TypePointer RationalNumberType::binaryOperatorResult(Token::Value _operator, Typ } else value = m_value.numerator() % other.m_value.numerator(); - break; + break; case Token::Exp: { - using boost::multiprecision::pow; if (other.isFractional()) return TypePointer(); solAssert(other.m_value.denominator() == 1, ""); @@ -1019,23 +1137,22 @@ TypePointer RationalNumberType::binaryOperatorResult(Token::Value _operator, Typ else if (_base == -1) return 1 - 2 * int(_exponent & 1); else - return pow(_base, _exponent); + return boost::multiprecision::pow(_base, _exponent); }; bigint numerator = optimizedPow(m_value.numerator(), absExp); bigint denominator = optimizedPow(m_value.denominator(), absExp); if (exp >= 0) - value = rational(numerator, denominator); + value = makeRational(numerator, denominator); else // invert - value = rational(denominator, numerator); + value = makeRational(denominator, numerator); } break; } case Token::SHL: { - using boost::multiprecision::pow; if (fractional) return TypePointer(); else if (other.m_value < 0) @@ -1049,7 +1166,7 @@ TypePointer RationalNumberType::binaryOperatorResult(Token::Value _operator, Typ uint32_t exponent = other.m_value.numerator().convert_to<uint32_t>(); if (!fitsPrecisionBase2(abs(m_value.numerator()), exponent)) return TypePointer(); - value = m_value.numerator() * pow(bigint(2), exponent); + value = m_value.numerator() * boost::multiprecision::pow(bigint(2), exponent); } break; } @@ -1057,7 +1174,6 @@ TypePointer RationalNumberType::binaryOperatorResult(Token::Value _operator, Typ // determines the resulting type and the type of shift (SAR or SHR). case Token::SAR: { - namespace mp = boost::multiprecision; if (fractional) return TypePointer(); else if (other.m_value < 0) @@ -1069,10 +1185,22 @@ TypePointer RationalNumberType::binaryOperatorResult(Token::Value _operator, Typ else { uint32_t exponent = other.m_value.numerator().convert_to<uint32_t>(); - if (exponent > mostSignificantBit(mp::abs(m_value.numerator()))) - value = 0; + if (exponent > mostSignificantBit(boost::multiprecision::abs(m_value.numerator()))) + value = m_value.numerator() < 0 ? -1 : 0; else - value = rational(m_value.numerator() / mp::pow(bigint(2), exponent), 1); + { + if (m_value.numerator() < 0) + // Add 1 to the negative value before dividing to get a result that is strictly too large, + // then subtract 1 afterwards to round towards negative infinity. + // This is the same algorithm as used in ExpressionCompiler::appendShiftOperatorCode(...). + // To see this note that for negative x, xor(x,all_ones) = (-x-1) and + // therefore xor(div(xor(x,all_ones), exp(2, shift_amount)), all_ones) is + // -(-x - 1) / 2^shift_amount - 1, which is the same as + // (x + 1) / 2^shift_amount - 1. + value = rational((m_value.numerator() + 1) / boost::multiprecision::pow(bigint(2), exponent) - bigint(1), 1); + else + value = rational(m_value.numerator() / boost::multiprecision::pow(bigint(2), exponent), 1); + } } break; } @@ -1090,7 +1218,14 @@ TypePointer RationalNumberType::binaryOperatorResult(Token::Value _operator, Typ string RationalNumberType::richIdentifier() const { - return "t_rational_" + m_value.numerator().str() + "_by_" + m_value.denominator().str(); + // rational seemingly will put the sign always on the numerator, + // but let just make it deterministic here. + bigint numerator = abs(m_value.numerator()); + bigint denominator = abs(m_value.denominator()); + if (m_value < 0) + return "t_rational_minus_" + numerator.str() + "_by_" + denominator.str(); + else + return "t_rational_" + numerator.str() + "_by_" + denominator.str(); } bool RationalNumberType::operator==(Type const& _other) const @@ -1128,20 +1263,20 @@ u256 RationalNumberType::literalValue(Literal const*) const // its value. u256 value; - bigint shiftedValue; + bigint shiftedValue; if (!isFractional()) shiftedValue = m_value.numerator(); else { auto fixed = fixedPointType(); - solAssert(fixed, ""); + solAssert(fixed, "Rational number cannot be represented as fixed point type."); int fractionalDigits = fixed->fractionalDigits(); - shiftedValue = m_value.numerator() * pow(bigint(10), fractionalDigits) / m_value.denominator(); + shiftedValue = m_value.numerator() * boost::multiprecision::pow(bigint(10), fractionalDigits) / m_value.denominator(); } // we ignore the literal and hope that the type was correctly determined - solAssert(shiftedValue <= u256(-1), "Integer constant too large."); + solAssert(shiftedValue <= u256(-1), "Number constant too large."); solAssert(shiftedValue >= -(bigint(1) << 255), "Number constant too small."); if (m_value >= rational(0)) @@ -1180,7 +1315,7 @@ shared_ptr<FixedPointType const> RationalNumberType::fixedPointType() const bool negative = (m_value < 0); unsigned fractionalDigits = 0; rational value = abs(m_value); // We care about the sign later. - rational maxValue = negative ? + rational maxValue = negative ? rational(bigint(1) << 255, 1): rational((bigint(1) << 256) - 1, 1); @@ -1189,12 +1324,13 @@ shared_ptr<FixedPointType const> RationalNumberType::fixedPointType() const value *= 10; fractionalDigits++; } - + if (value > maxValue) return shared_ptr<FixedPointType const>(); // This means we round towards zero for positive and negative values. bigint v = value.numerator() / value.denominator(); - if (negative) + + if (negative && v != 0) // modify value to satisfy bit requirements for negative numbers: // add one bit for sign and decrement because negative numbers can be larger v = (v - 1) << 1; @@ -1281,12 +1417,13 @@ bool FixedBytesType::isImplicitlyConvertibleTo(Type const& _convertTo) const bool FixedBytesType::isExplicitlyConvertibleTo(Type const& _convertTo) const { - return _convertTo.category() == Category::Integer || + return (_convertTo.category() == Category::Integer && numBytes() * 8 == dynamic_cast<IntegerType const&>(_convertTo).numBits()) || + (_convertTo.category() == Category::Address && numBytes() == 20) || _convertTo.category() == Category::FixedPoint || _convertTo.category() == category(); } -TypePointer FixedBytesType::unaryOperatorResult(Token::Value _operator) const +TypePointer FixedBytesType::unaryOperatorResult(Token _operator) const { // "delete" and "~" is okay for FixedBytesType if (_operator == Token::Delete) @@ -1297,9 +1434,9 @@ TypePointer FixedBytesType::unaryOperatorResult(Token::Value _operator) const return TypePointer(); } -TypePointer FixedBytesType::binaryOperatorResult(Token::Value _operator, TypePointer const& _other) const +TypePointer FixedBytesType::binaryOperatorResult(Token _operator, TypePointer const& _other) const { - if (Token::isShiftOp(_operator)) + if (TokenTraits::isShiftOp(_operator)) { if (isValidShiftAndAmountType(_operator, *_other)) return shared_from_this(); @@ -1312,7 +1449,7 @@ TypePointer FixedBytesType::binaryOperatorResult(Token::Value _operator, TypePoi return TypePointer(); // FixedBytes can be compared and have bitwise operators applied to them - if (Token::isCompareOp(_operator) || Token::isBitOp(_operator)) + if (TokenTraits::isCompareOp(_operator) || TokenTraits::isBitOp(_operator)) return commonType; return TypePointer(); @@ -1325,7 +1462,7 @@ MemberList::MemberMap FixedBytesType::nativeMembers(const ContractDefinition*) c string FixedBytesType::richIdentifier() const { - return "t_bytes" + std::to_string(m_bytes); + return "t_bytes" + to_string(m_bytes); } bool FixedBytesType::operator==(Type const& _other) const @@ -1347,18 +1484,18 @@ u256 BoolType::literalValue(Literal const* _literal) const solAssert(false, "Bool type constructed from non-boolean literal."); } -TypePointer BoolType::unaryOperatorResult(Token::Value _operator) const +TypePointer BoolType::unaryOperatorResult(Token _operator) const { if (_operator == Token::Delete) return make_shared<TupleType>(); return (_operator == Token::Not) ? shared_from_this() : TypePointer(); } -TypePointer BoolType::binaryOperatorResult(Token::Value _operator, TypePointer const& _other) const +TypePointer BoolType::binaryOperatorResult(Token _operator, TypePointer const& _other) const { if (category() != _other->category()) return TypePointer(); - if (Token::isCompareOp(_operator) || _operator == Token::And || _operator == Token::Or) + if (_operator == Token::Equal || _operator == Token::NotEqual || _operator == Token::And || _operator == Token::Or) return _other; else return TypePointer(); @@ -1368,25 +1505,24 @@ bool ContractType::isImplicitlyConvertibleTo(Type const& _convertTo) const { if (*this == _convertTo) return true; - if (_convertTo.category() == Category::Integer) - return dynamic_cast<IntegerType const&>(_convertTo).isAddress(); if (_convertTo.category() == Category::Contract) { auto const& bases = contractDefinition().annotation().linearizedBaseContracts; if (m_super && bases.size() <= 1) return false; - return find(m_super ? ++bases.begin() : bases.begin(), bases.end(), - &dynamic_cast<ContractType const&>(_convertTo).contractDefinition()) != bases.end(); + return find( + m_super ? ++bases.begin() : bases.begin(), bases.end(), + &dynamic_cast<ContractType const&>(_convertTo).contractDefinition() + ) != bases.end(); } return false; } bool ContractType::isExplicitlyConvertibleTo(Type const& _convertTo) const { - return - isImplicitlyConvertibleTo(_convertTo) || - _convertTo.category() == Category::Integer || - _convertTo.category() == Category::Contract; + if (auto const* addressType = dynamic_cast<AddressType const*>(&_convertTo)) + return isPayable() || (addressType->stateMutability() < StateMutability::Payable); + return isImplicitlyConvertibleTo(_convertTo); } bool ContractType::isPayable() const @@ -1395,14 +1531,14 @@ bool ContractType::isPayable() const return fallbackFunction && fallbackFunction->isPayable(); } -TypePointer ContractType::unaryOperatorResult(Token::Value _operator) const +TypePointer ContractType::unaryOperatorResult(Token _operator) const { if (isSuper()) return TypePointer{}; return _operator == Token::Delete ? make_shared<TupleType>() : TypePointer(); } -TypePointer ReferenceType::unaryOperatorResult(Token::Value _operator) const +TypePointer ReferenceType::unaryOperatorResult(Token _operator) const { if (_operator != Token::Delete) return TypePointer(); @@ -1416,8 +1552,6 @@ TypePointer ReferenceType::unaryOperatorResult(Token::Value _operator) const return make_shared<TupleType>(); case DataLocation::Storage: return m_isPointer ? TypePointer() : make_shared<TupleType>(); - default: - solAssert(false, ""); } return TypePointer(); } @@ -1452,12 +1586,18 @@ string ReferenceType::stringForReferencePart() const string ReferenceType::identifierLocationSuffix() const { string id; - if (location() == DataLocation::Storage) + switch (location()) + { + case DataLocation::Storage: id += "_storage"; - else if (location() == DataLocation::Memory) + break; + case DataLocation::Memory: id += "_memory"; - else + break; + case DataLocation::CallData: id += "_calldata"; + break; + } if (isPointer()) id += "_ptr"; return id; @@ -1558,6 +1698,9 @@ bool ArrayType::operator==(Type const& _other) const bool ArrayType::validForCalldata() const { + if (auto arrayBaseType = dynamic_cast<ArrayType const*>(baseType().get())) + if (!arrayBaseType->validForCalldata()) + return false; return unlimitedCalldataEncodedSize(true) <= numeric_limits<unsigned>::max(); } @@ -1672,6 +1815,7 @@ MemberList::MemberMap ArrayType::nativeMembers(ContractDefinition const*) const { members.push_back({"length", make_shared<IntegerType>(256)}); if (isDynamicallySized() && location() == DataLocation::Storage) + { members.push_back({"push", make_shared<FunctionType>( TypePointers{baseType()}, TypePointers{make_shared<IntegerType>(256)}, @@ -1679,6 +1823,14 @@ MemberList::MemberMap ArrayType::nativeMembers(ContractDefinition const*) const strings{string()}, isByteArray() ? FunctionType::Kind::ByteArrayPush : FunctionType::Kind::ArrayPush )}); + members.push_back({"pop", make_shared<FunctionType>( + TypePointers{}, + TypePointers{}, + strings{string()}, + strings{string()}, + FunctionType::Kind::ArrayPop + )}); + } } return members; } @@ -1752,7 +1904,7 @@ TypePointer ArrayType::copyForLocation(DataLocation _location, bool _isPointer) string ContractType::richIdentifier() const { - return (m_super ? "t_super" : "t_contract") + parenthesizeUserIdentifier(m_contract.name()) + std::to_string(m_contract.id()); + return (m_super ? "t_super" : "t_contract") + parenthesizeUserIdentifier(m_contract.name()) + to_string(m_contract.id()); } bool ContractType::operator==(Type const& _other) const @@ -1799,7 +1951,7 @@ MemberList::MemberMap ContractType::nativeMembers(ContractDefinition const* _con continue; auto memberType = dynamic_cast<FunctionType const*>(member.type.get()); solAssert(!!memberType, "Override changes type."); - if (!memberType->hasEqualArgumentTypes(*functionType)) + if (!memberType->hasEqualParameterTypes(*functionType)) continue; functionWithEqualArgumentsFound = true; break; @@ -1821,47 +1973,9 @@ MemberList::MemberMap ContractType::nativeMembers(ContractDefinition const* _con &it.second->declaration() )); } - // In 0.5.0 address members are not populated into the contract. - if (!_contract->sourceUnit().annotation().experimentalFeatures.count(ExperimentalFeature::V050)) - addNonConflictingAddressMembers(members); return members; } -void ContractType::addNonConflictingAddressMembers(MemberList::MemberMap& _members) -{ - MemberList::MemberMap addressMembers = IntegerType(160, IntegerType::Modifier::Address).nativeMembers(nullptr); - for (auto const& addressMember: addressMembers) - { - bool clash = false; - for (auto const& member: _members) - { - if ( - member.name == addressMember.name && - ( - // Members with different types are not allowed - member.type->category() != addressMember.type->category() || - // Members must overload functions without clash - ( - member.type->category() == Type::Category::Function && - dynamic_cast<FunctionType const&>(*member.type).hasEqualArgumentTypes(dynamic_cast<FunctionType const&>(*addressMember.type)) - ) - ) - ) - { - clash = true; - break; - } - } - - if (!clash) - _members.push_back(MemberList::Member( - addressMember.name, - addressMember.type, - addressMember.declaration - )); - } -} - shared_ptr<FunctionType const> const& ContractType::newExpressionType() const { if (!m_constructorType) @@ -1904,7 +2018,7 @@ bool StructType::isImplicitlyConvertibleTo(const Type& _convertTo) const string StructType::richIdentifier() const { - return "t_struct" + parenthesizeUserIdentifier(m_struct.name()) + std::to_string(m_struct.id()) + identifierLocationSuffix(); + return "t_struct" + parenthesizeUserIdentifier(m_struct.name()) + to_string(m_struct.id()) + identifierLocationSuffix(); } bool StructType::operator==(Type const& _other) const @@ -2009,8 +2123,11 @@ bool StructType::canBeUsedExternally(bool _inLibrary) const // passed by value and thus the encoding does not differ, but it will disallow // mappings. for (auto const& var: m_struct.members()) + { + solAssert(var->annotation().type, ""); if (!var->annotation().type->canBeUsedExternally(false)) return false; + } } return true; } @@ -2106,7 +2223,7 @@ bool StructType::recursive() const { if (!m_recursive.is_initialized()) { - auto visitor = [&](StructDefinition const& _struct, CycleDetector<StructDefinition>& _cycleDetector) + auto visitor = [&](StructDefinition const& _struct, CycleDetector<StructDefinition>& _cycleDetector, size_t /*_depth*/) { for (ASTPointer<VariableDeclaration> const& variable: _struct.members()) { @@ -2123,14 +2240,14 @@ bool StructType::recursive() const return *m_recursive; } -TypePointer EnumType::unaryOperatorResult(Token::Value _operator) const +TypePointer EnumType::unaryOperatorResult(Token _operator) const { return _operator == Token::Delete ? make_shared<TupleType>() : TypePointer(); } string EnumType::richIdentifier() const { - return "t_enum" + parenthesizeUserIdentifier(m_enum.name()) + std::to_string(m_enum.id()); + return "t_enum" + parenthesizeUserIdentifier(m_enum.name()) + to_string(m_enum.id()); } bool EnumType::operator==(Type const& _other) const @@ -2189,25 +2306,13 @@ bool TupleType::isImplicitlyConvertibleTo(Type const& _other) const TypePointers const& targets = tupleType->components(); if (targets.empty()) return components().empty(); - if (components().size() != targets.size() && !targets.front() && !targets.back()) - return false; // (,a,) = (1,2,3,4) - unable to position `a` in the tuple. - size_t minNumValues = targets.size(); - if (!targets.back() || !targets.front()) - --minNumValues; // wildcards can also match 0 components - if (components().size() < minNumValues) + if (components().size() != targets.size()) return false; - if (components().size() > targets.size() && targets.front() && targets.back()) - return false; // larger source and no wildcard - bool fillRight = !targets.back() || targets.front(); - for (size_t i = 0; i < min(targets.size(), components().size()); ++i) - { - auto const& s = components()[fillRight ? i : components().size() - i - 1]; - auto const& t = targets[fillRight ? i : targets.size() - i - 1]; - if (!s && t) + for (size_t i = 0; i < targets.size(); ++i) + if (!components()[i] && targets[i]) return false; - else if (s && t && !s->isImplicitlyConvertibleTo(*t)) + else if (components()[i] && targets[i] && !components()[i]->isImplicitlyConvertibleTo(*targets[i])) return false; - } return true; } else @@ -2273,16 +2378,14 @@ TypePointer TupleType::closestTemporaryType(TypePointer const& _targetType) cons { solAssert(!!_targetType, ""); TypePointers const& targetComponents = dynamic_cast<TupleType const&>(*_targetType).components(); - bool fillRight = !targetComponents.empty() && (!targetComponents.back() || targetComponents.front()); + solAssert(components().size() == targetComponents.size(), ""); TypePointers tempComponents(targetComponents.size()); - for (size_t i = 0; i < min(targetComponents.size(), components().size()); ++i) + for (size_t i = 0; i < targetComponents.size(); ++i) { - size_t si = fillRight ? i : components().size() - i - 1; - size_t ti = fillRight ? i : targetComponents.size() - i - 1; - if (components()[si] && targetComponents[ti]) + if (components()[i] && targetComponents[i]) { - tempComponents[ti] = components()[si]->closestTemporaryType(targetComponents[ti]); - solAssert(tempComponents[ti], ""); + tempComponents[i] = components()[i]->closestTemporaryType(targetComponents[i]); + solAssert(tempComponents[i], ""); } } return make_shared<TupleType>(tempComponents); @@ -2326,7 +2429,7 @@ FunctionType::FunctionType(VariableDeclaration const& _varDecl): else if (auto arrayType = dynamic_cast<ArrayType const*>(returnType.get())) { if (arrayType->isByteArray()) - // Return byte arrays as as whole. + // Return byte arrays as whole. break; returnType = arrayType->baseType(); m_parameterNames.push_back(""); @@ -2446,7 +2549,14 @@ TypePointers FunctionType::returnParameterTypesWithoutDynamicTypes() const { TypePointers returnParameterTypes = m_returnParameterTypes; - if (m_kind == Kind::External || m_kind == Kind::CallCode || m_kind == Kind::DelegateCall) + if ( + m_kind == Kind::External || + m_kind == Kind::DelegateCall || + m_kind == Kind::BareCall || + m_kind == Kind::BareCallCode || + m_kind == Kind::BareDelegateCall || + m_kind == Kind::BareStaticCall + ) for (auto& param: returnParameterTypes) if (param->isDynamicallySized() && !param->dataStoredIn(DataLocation::Storage)) param = make_shared<InaccessibleDynamicType>(); @@ -2468,15 +2578,15 @@ string FunctionType::richIdentifier() const { case Kind::Internal: id += "internal"; break; case Kind::External: id += "external"; break; - case Kind::CallCode: id += "callcode"; break; case Kind::DelegateCall: id += "delegatecall"; break; case Kind::BareCall: id += "barecall"; break; case Kind::BareCallCode: id += "barecallcode"; break; case Kind::BareDelegateCall: id += "baredelegatecall"; break; + case Kind::BareStaticCall: id += "barestaticcall"; break; case Kind::Creation: id += "creation"; break; case Kind::Send: id += "send"; break; case Kind::Transfer: id += "transfer"; break; - case Kind::SHA3: id += "sha3"; break; + case Kind::KECCAK256: id += "keccak256"; break; case Kind::Selfdestruct: id += "selfdestruct"; break; case Kind::Revert: id += "revert"; break; case Kind::ECRecover: id += "ecrecover"; break; @@ -2495,6 +2605,7 @@ string FunctionType::richIdentifier() const case Kind::AddMod: id += "addmod"; break; case Kind::MulMod: id += "mulmod"; break; case Kind::ArrayPush: id += "arraypush"; break; + case Kind::ArrayPop: id += "arraypop"; break; case Kind::ByteArrayPush: id += "bytearraypush"; break; case Kind::ObjectCreation: id += "objectcreation"; break; case Kind::Assert: id += "assert"; break; @@ -2503,7 +2614,7 @@ string FunctionType::richIdentifier() const case Kind::ABIEncodePacked: id += "abiencodepacked"; break; case Kind::ABIEncodeWithSelector: id += "abiencodewithselector"; break; case Kind::ABIEncodeWithSignature: id += "abiencodewithsignature"; break; - default: solAssert(false, "Unknown function location."); break; + case Kind::ABIDecode: id += "abidecode"; break; } id += "_" + stateMutabilityToString(m_stateMutability); id += identifierList(m_parameterTypes) + "returns" + identifierList(m_returnParameterTypes); @@ -2520,51 +2631,54 @@ bool FunctionType::operator==(Type const& _other) const { if (_other.category() != category()) return false; - FunctionType const& other = dynamic_cast<FunctionType const&>(_other); - if ( - m_kind != other.m_kind || - m_stateMutability != other.stateMutability() || - m_parameterTypes.size() != other.m_parameterTypes.size() || - m_returnParameterTypes.size() != other.m_returnParameterTypes.size() - ) + if (!equalExcludingStateMutability(other)) return false; - - auto typeCompare = [](TypePointer const& _a, TypePointer const& _b) -> bool { return *_a == *_b; }; - if ( - !equal(m_parameterTypes.cbegin(), m_parameterTypes.cend(), other.m_parameterTypes.cbegin(), typeCompare) || - !equal(m_returnParameterTypes.cbegin(), m_returnParameterTypes.cend(), other.m_returnParameterTypes.cbegin(), typeCompare) - ) - return false; - //@todo this is ugly, but cannot be prevented right now - if (m_gasSet != other.m_gasSet || m_valueSet != other.m_valueSet) - return false; - if (bound() != other.bound()) - return false; - if (bound() && *selfType() != *other.selfType()) + if (m_stateMutability != other.stateMutability()) return false; return true; } bool FunctionType::isExplicitlyConvertibleTo(Type const& _convertTo) const { - if (m_kind == Kind::External && _convertTo.category() == Category::Integer) - { - IntegerType const& convertTo = dynamic_cast<IntegerType const&>(_convertTo); - if (convertTo.isAddress()) - return true; - } + if (m_kind == Kind::External && _convertTo == AddressType::address()) + return true; return _convertTo.category() == category(); } -TypePointer FunctionType::unaryOperatorResult(Token::Value _operator) const +bool FunctionType::isImplicitlyConvertibleTo(Type const& _convertTo) const +{ + if (_convertTo.category() != category()) + return false; + + FunctionType const& convertTo = dynamic_cast<FunctionType const&>(_convertTo); + + if (!equalExcludingStateMutability(convertTo)) + return false; + + // non-payable should not be convertible to payable + if (m_stateMutability != StateMutability::Payable && convertTo.stateMutability() == StateMutability::Payable) + return false; + + // payable should be convertible to non-payable, because you are free to pay 0 ether + if (m_stateMutability == StateMutability::Payable && convertTo.stateMutability() == StateMutability::NonPayable) + return true; + + // e.g. pure should be convertible to view, but not the other way around. + if (m_stateMutability > convertTo.stateMutability()) + return false; + + return true; +} + +TypePointer FunctionType::unaryOperatorResult(Token _operator) const { - if (_operator == Token::Value::Delete) + if (_operator == Token::Delete) return make_shared<TupleType>(); return TypePointer(); } -TypePointer FunctionType::binaryOperatorResult(Token::Value _operator, TypePointer const& _other) const +TypePointer FunctionType::binaryOperatorResult(Token _operator, TypePointer const& _other) const { if (_other->category() != category() || !(_operator == Token::Equal || _operator == Token::NotEqual)) return TypePointer(); @@ -2640,15 +2754,16 @@ unsigned FunctionType::sizeOnStack() const switch(kind) { case Kind::External: - case Kind::CallCode: case Kind::DelegateCall: size = 2; break; case Kind::BareCall: case Kind::BareCallCode: case Kind::BareDelegateCall: + case Kind::BareStaticCall: case Kind::Internal: case Kind::ArrayPush: + case Kind::ArrayPop: case Kind::ByteArrayPush: size = 1; break; @@ -2713,6 +2828,7 @@ MemberList::MemberMap FunctionType::nativeMembers(ContractDefinition const*) con case Kind::BareCall: case Kind::BareCallCode: case Kind::BareDelegateCall: + case Kind::BareStaticCall: { MemberList::MemberMap members; if (m_kind == Kind::External) @@ -2801,7 +2917,7 @@ bool FunctionType::canTakeArguments(TypePointers const& _argumentTypes, TypePoin ); } -bool FunctionType::hasEqualArgumentTypes(FunctionType const& _other) const +bool FunctionType::hasEqualParameterTypes(FunctionType const& _other) const { if (m_parameterTypes.size() != _other.m_parameterTypes.size()) return false; @@ -2813,6 +2929,38 @@ bool FunctionType::hasEqualArgumentTypes(FunctionType const& _other) const ); } +bool FunctionType::hasEqualReturnTypes(FunctionType const& _other) const +{ + if (m_returnParameterTypes.size() != _other.m_returnParameterTypes.size()) + return false; + return equal( + m_returnParameterTypes.cbegin(), + m_returnParameterTypes.cend(), + _other.m_returnParameterTypes.cbegin(), + [](TypePointer const& _a, TypePointer const& _b) -> bool { return *_a == *_b; } + ); +} + +bool FunctionType::equalExcludingStateMutability(FunctionType const& _other) const +{ + if (m_kind != _other.m_kind) + return false; + + if (!hasEqualParameterTypes(_other) || !hasEqualReturnTypes(_other)) + return false; + + //@todo this is ugly, but cannot be prevented right now + if (m_gasSet != _other.m_gasSet || m_valueSet != _other.m_valueSet) + return false; + + if (bound() != _other.bound()) + return false; + + solAssert(!bound() || *selfType() == *_other.selfType(), ""); + + return true; +} + bool FunctionType::isBareCall() const { switch (m_kind) @@ -2820,6 +2968,7 @@ bool FunctionType::isBareCall() const case Kind::BareCall: case Kind::BareCallCode: case Kind::BareDelegateCall: + case Kind::BareStaticCall: case Kind::ECRecover: case Kind::SHA256: case Kind::RIPEMD160: @@ -2833,6 +2982,16 @@ string FunctionType::externalSignature() const { solAssert(m_declaration != nullptr, "External signature of function needs declaration"); solAssert(!m_declaration->name().empty(), "Fallback function has no signature."); + switch (kind()) + { + case Kind::Internal: + case Kind::External: + case Kind::DelegateCall: + case Kind::Event: + break; + default: + solAssert(false, "Invalid function type for requesting external signature."); + } bool const inLibrary = dynamic_cast<ContractDefinition const&>(*m_declaration->scope()).isLibrary(); FunctionTypePointer external = interfaceFunctionType(); @@ -2859,7 +3018,7 @@ bool FunctionType::isPure() const // FIXME: replace this with m_stateMutability == StateMutability::Pure once // the callgraph analyzer is in place return - m_kind == Kind::SHA3 || + m_kind == Kind::KECCAK256 || m_kind == Kind::ECRecover || m_kind == Kind::SHA256 || m_kind == Kind::RIPEMD160 || @@ -2869,7 +3028,8 @@ bool FunctionType::isPure() const m_kind == Kind::ABIEncode || m_kind == Kind::ABIEncodePacked || m_kind == Kind::ABIEncodeWithSelector || - m_kind == Kind::ABIEncodeWithSignature; + m_kind == Kind::ABIEncodeWithSignature || + m_kind == Kind::ABIDecode; } TypePointers FunctionType::parseElementaryTypeVector(strings const& _types) @@ -2954,6 +3114,26 @@ ASTPointer<ASTString> FunctionType::documentation() const return ASTPointer<ASTString>(); } +bool FunctionType::padArguments() const +{ + // No padding only for hash functions, low-level calls and the packed encoding function. + switch (m_kind) + { + case Kind::BareCall: + case Kind::BareCallCode: + case Kind::BareDelegateCall: + case Kind::BareStaticCall: + case Kind::SHA256: + case Kind::RIPEMD160: + case Kind::KECCAK256: + case Kind::ABIEncodePacked: + return false; + default: + return true; + } + return true; +} + string MappingType::richIdentifier() const { return "t_mapping" + identifierList(m_keyType, m_valueType); @@ -3093,7 +3273,7 @@ string ModifierType::toString(bool _short) const string ModuleType::richIdentifier() const { - return "t_module_" + std::to_string(m_sourceUnit.id()); + return "t_module_" + to_string(m_sourceUnit.id()); } bool ModuleType::operator==(Type const& _other) const @@ -3129,8 +3309,6 @@ string MagicType::richIdentifier() const return "t_magic_transaction"; case Kind::ABI: return "t_magic_abi"; - default: - solAssert(false, "Unknown kind of magic"); } return ""; } @@ -3149,7 +3327,7 @@ MemberList::MemberMap MagicType::nativeMembers(ContractDefinition const*) const { case Kind::Block: return MemberList::MemberMap({ - {"coinbase", make_shared<IntegerType>(160, IntegerType::Modifier::Address)}, + {"coinbase", make_shared<AddressType>(StateMutability::Payable)}, {"timestamp", make_shared<IntegerType>(256)}, {"blockhash", make_shared<FunctionType>(strings{"uint"}, strings{"bytes32"}, FunctionType::Kind::BlockHash, false, StateMutability::View)}, {"difficulty", make_shared<IntegerType>(256)}, @@ -3158,7 +3336,7 @@ MemberList::MemberMap MagicType::nativeMembers(ContractDefinition const*) const }); case Kind::Message: return MemberList::MemberMap({ - {"sender", make_shared<IntegerType>(160, IntegerType::Modifier::Address)}, + {"sender", make_shared<AddressType>(StateMutability::Payable)}, {"gas", make_shared<IntegerType>(256)}, {"value", make_shared<IntegerType>(256)}, {"data", make_shared<ArrayType>(DataLocation::CallData)}, @@ -3166,7 +3344,7 @@ MemberList::MemberMap MagicType::nativeMembers(ContractDefinition const*) const }); case Kind::Transaction: return MemberList::MemberMap({ - {"origin", make_shared<IntegerType>(160, IntegerType::Modifier::Address)}, + {"origin", make_shared<AddressType>(StateMutability::Payable)}, {"gasprice", make_shared<IntegerType>(256)} }); case Kind::ABI: @@ -3206,6 +3384,15 @@ MemberList::MemberMap MagicType::nativeMembers(ContractDefinition const*) const FunctionType::Kind::ABIEncodeWithSignature, true, StateMutability::Pure + )}, + {"decode", make_shared<FunctionType>( + TypePointers(), + TypePointers(), + strings{}, + strings{}, + FunctionType::Kind::ABIDecode, + true, + StateMutability::Pure )} }); default: diff --git a/libsolidity/ast/Types.h b/libsolidity/ast/Types.h index 95821634..24ace447 100644 --- a/libsolidity/ast/Types.h +++ b/libsolidity/ast/Types.h @@ -51,6 +51,15 @@ using FunctionTypePointer = std::shared_ptr<FunctionType const>; using TypePointers = std::vector<TypePointer>; using rational = boost::rational<dev::bigint>; +inline rational makeRational(bigint const& _numerator, bigint const& _denominator) +{ + solAssert(_denominator != 0, "division by zero"); + // due to a bug in certain versions of boost the denominator has to be positive + if (_denominator < 0) + return rational(-_numerator, -_denominator); + else + return rational(_numerator, _denominator); +} enum class DataLocation { Storage, CallData, Memory }; @@ -95,9 +104,7 @@ public: using MemberMap = std::vector<Member>; - MemberList() {} explicit MemberList(MemberMap const& _members): m_memberTypes(_members) {} - MemberList& operator=(MemberList&& _other); void combine(MemberList const& _other); TypePointer memberType(std::string const& _name) const { @@ -132,6 +139,8 @@ private: mutable std::unique_ptr<StorageOffsets> m_storageOffsets; }; +static_assert(std::is_nothrow_move_constructible<MemberList>::value, "MemberList should be noexcept move constructible"); + /** * Abstract base class that forms the root of the type hierarchy. */ @@ -141,7 +150,7 @@ public: virtual ~Type() = default; enum class Category { - Integer, RationalNumber, StringLiteral, Bool, FixedPoint, Array, + Address, Integer, RationalNumber, StringLiteral, Bool, FixedPoint, Array, FixedBytes, Contract, Struct, Function, Enum, Tuple, Mapping, TypeType, Modifier, Magic, Module, InaccessibleDynamic @@ -151,7 +160,8 @@ public: /// @name Factory functions /// Factory functions that convert an AST @ref TypeName to a Type. static TypePointer fromElementaryTypeName(ElementaryTypeNameToken const& _type); - /// Converts a given elementary type name with optional suffix " memory" to a type pointer. + /// Converts a given elementary type name with optional data location + /// suffix " storage", " calldata" or " memory" to a type pointer. If suffix not given, defaults to " storage". static TypePointer fromElementaryTypeName(std::string const& _name); /// @} @@ -171,9 +181,9 @@ public: /// only if they have the same identifier. /// The identifier should start with "t_". /// Will not contain any character which would be invalid as an identifier. - std::string identifier() const { return escapeIdentifier(richIdentifier()); } + std::string identifier() const; - /// More complex identifier strings use "parentheses", where $_ is interpreted as as + /// More complex identifier strings use "parentheses", where $_ is interpreted as /// "opening parenthesis", _$ as "closing parenthesis", _$_ as "comma" and any $ that /// appears as part of a user-supplied identifier is escaped as _$$$_. /// @returns an escaped identifier (will not contain any parenthesis or commas) @@ -187,13 +197,13 @@ public: /// @returns the resulting type of applying the given unary operator or an empty pointer if /// this is not possible. /// The default implementation does not allow any unary operator. - virtual TypePointer unaryOperatorResult(Token::Value) const { return TypePointer(); } + virtual TypePointer unaryOperatorResult(Token) const { return TypePointer(); } /// @returns the resulting type of applying the given binary operator or an empty pointer if /// this is not possible. /// The default implementation allows comparison operators if a common type exists - virtual TypePointer binaryOperatorResult(Token::Value _operator, TypePointer const& _other) const + virtual TypePointer binaryOperatorResult(Token _operator, TypePointer const& _other) const { - return Token::isCompareOp(_operator) ? commonType(shared_from_this(), _other) : TypePointer(); + return TokenTraits::isCompareOp(_operator) ? commonType(shared_from_this(), _other) : TypePointer(); } virtual bool operator==(Type const& _other) const { return category() == _other.category(); } @@ -279,6 +289,11 @@ public: /// This for example returns address for contract types. /// If there is no such type, returns an empty shared pointer. virtual TypePointer encodingType() const { return TypePointer(); } + /// @returns the encoding type used under the given circumstances for the type of an expression + /// when used for e.g. abi.encode(...) or the empty pointer if the object + /// cannot be encoded. + /// This is different from encodingType since it takes implicit conversions into account. + TypePointer fullEncodingType(bool _inLibraryCall, bool _encoderV2, bool _packed) const; /// @returns a (simpler) type that is used when decoding this type in calldata. virtual TypePointer decodingType() const { return encodingType(); } /// @returns a type that will be used outside of Solidity for e.g. function signatures. @@ -308,15 +323,57 @@ protected: }; /** - * Any kind of integer type (signed, unsigned, address). + * Type for addresses. + */ +class AddressType: public Type +{ +public: + static AddressType& address() { static std::shared_ptr<AddressType> addr(std::make_shared<AddressType>(StateMutability::NonPayable)); return *addr; } + static AddressType& addressPayable() { static std::shared_ptr<AddressType> addr(std::make_shared<AddressType>(StateMutability::Payable)); return *addr; } + + virtual Category category() const override { return Category::Address; } + + explicit AddressType(StateMutability _stateMutability); + + virtual std::string richIdentifier() const override; + virtual bool isImplicitlyConvertibleTo(Type const& _other) const override; + virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const override; + virtual TypePointer unaryOperatorResult(Token _operator) const override; + virtual TypePointer binaryOperatorResult(Token _operator, TypePointer const& _other) const override; + + virtual bool operator==(Type const& _other) const override; + + virtual unsigned calldataEncodedSize(bool _padded = true) const override { return _padded ? 32 : 160 / 8; } + virtual unsigned storageBytes() const override { return 160 / 8; } + virtual bool isValueType() const override { return true; } + + virtual MemberList::MemberMap nativeMembers(ContractDefinition const*) const override; + + virtual std::string toString(bool _short) const override; + virtual std::string canonicalName() const override; + + virtual u256 literalValue(Literal const* _literal) const override; + + virtual TypePointer encodingType() const override { return shared_from_this(); } + virtual TypePointer interfaceType(bool) const override { return shared_from_this(); } + + StateMutability stateMutability(void) const { return m_stateMutability; } + +private: + StateMutability m_stateMutability; +}; + +/** + * Any kind of integer type (signed, unsigned). */ class IntegerType: public Type { public: enum class Modifier { - Unsigned, Signed, Address + Unsigned, Signed }; + virtual Category category() const override { return Category::Integer; } explicit IntegerType(unsigned _bits, Modifier _modifier = Modifier::Unsigned); @@ -324,8 +381,8 @@ public: virtual std::string richIdentifier() const override; virtual bool isImplicitlyConvertibleTo(Type const& _convertTo) const override; virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const override; - virtual TypePointer unaryOperatorResult(Token::Value _operator) const override; - virtual TypePointer binaryOperatorResult(Token::Value _operator, TypePointer const& _other) const override; + virtual TypePointer unaryOperatorResult(Token _operator) const override; + virtual TypePointer binaryOperatorResult(Token _operator, TypePointer const& _other) const override; virtual bool operator==(Type const& _other) const override; @@ -333,17 +390,12 @@ public: virtual unsigned storageBytes() const override { return m_bits / 8; } virtual bool isValueType() const override { return true; } - virtual MemberList::MemberMap nativeMembers(ContractDefinition const*) const override; - virtual std::string toString(bool _short) const override; - virtual u256 literalValue(Literal const* _literal) const override; - virtual TypePointer encodingType() const override { return shared_from_this(); } virtual TypePointer interfaceType(bool) const override { return shared_from_this(); } unsigned numBits() const { return m_bits; } - bool isAddress() const { return m_modifier == Modifier::Address; } bool isSigned() const { return m_modifier == Modifier::Signed; } bigint minValue() const; @@ -371,8 +423,8 @@ public: virtual std::string richIdentifier() const override; virtual bool isImplicitlyConvertibleTo(Type const& _convertTo) const override; virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const override; - virtual TypePointer unaryOperatorResult(Token::Value _operator) const override; - virtual TypePointer binaryOperatorResult(Token::Value _operator, TypePointer const& _other) const override; + virtual TypePointer unaryOperatorResult(Token _operator) const override; + virtual TypePointer binaryOperatorResult(Token _operator, TypePointer const& _other) const override; virtual bool operator==(Type const& _other) const override; @@ -417,16 +469,16 @@ public: virtual Category category() const override { return Category::RationalNumber; } - /// @returns true if the literal is a valid integer. - static std::tuple<bool, rational> isValidLiteral(Literal const& _literal); + static TypePointer forLiteral(Literal const& _literal); - explicit RationalNumberType(rational const& _value): - m_value(_value) + explicit RationalNumberType(rational const& _value, TypePointer const& _compatibleBytesType = TypePointer()): + m_value(_value), m_compatibleBytesType(_compatibleBytesType) {} + virtual bool isImplicitlyConvertibleTo(Type const& _convertTo) const override; virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const override; - virtual TypePointer unaryOperatorResult(Token::Value _operator) const override; - virtual TypePointer binaryOperatorResult(Token::Value _operator, TypePointer const& _other) const override; + virtual TypePointer unaryOperatorResult(Token _operator) const override; + virtual TypePointer binaryOperatorResult(Token _operator, TypePointer const& _other) const override; virtual std::string richIdentifier() const override; virtual bool operator==(Type const& _other) const override; @@ -440,7 +492,8 @@ public: /// @returns the smallest integer type that can hold the value or an empty pointer if not possible. std::shared_ptr<IntegerType const> integerType() const; - /// @returns the smallest fixed type that can hold the value or incurs the least precision loss. + /// @returns the smallest fixed type that can hold the value or incurs the least precision loss, + /// unless the value was truncated, then a suitable type will be chosen to indicate such event. /// If the integer part does not fit, returns an empty pointer. std::shared_ptr<FixedPointType const> fixedPointType() const; @@ -456,6 +509,13 @@ public: private: rational m_value; + /// Bytes type to which the rational can be explicitly converted. + /// Empty for all rationals that are not directly parsed from hex literals. + TypePointer m_compatibleBytesType; + + /// @returns true if the literal is a valid integer. + static std::tuple<bool, rational> isValidLiteral(Literal const& _literal); + /// @returns true if the literal is a valid rational number. static std::tuple<bool, rational> parseRational(std::string const& _value); @@ -475,7 +535,7 @@ public: explicit StringLiteralType(Literal const& _literal); virtual bool isImplicitlyConvertibleTo(Type const& _convertTo) const override; - virtual TypePointer binaryOperatorResult(Token::Value, TypePointer const&) const override + virtual TypePointer binaryOperatorResult(Token, TypePointer const&) const override { return TypePointer(); } @@ -512,8 +572,8 @@ public: virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const override; virtual std::string richIdentifier() const override; virtual bool operator==(Type const& _other) const override; - virtual TypePointer unaryOperatorResult(Token::Value _operator) const override; - virtual TypePointer binaryOperatorResult(Token::Value _operator, TypePointer const& _other) const override; + virtual TypePointer unaryOperatorResult(Token _operator) const override; + virtual TypePointer binaryOperatorResult(Token _operator, TypePointer const& _other) const override; virtual unsigned calldataEncodedSize(bool _padded) const override { return _padded && m_bytes > 0 ? 32 : m_bytes; } virtual unsigned storageBytes() const override { return m_bytes; } @@ -539,8 +599,8 @@ public: BoolType() {} virtual Category category() const override { return Category::Bool; } virtual std::string richIdentifier() const override { return "t_bool"; } - virtual TypePointer unaryOperatorResult(Token::Value _operator) const override; - virtual TypePointer binaryOperatorResult(Token::Value _operator, TypePointer const& _other) const override; + virtual TypePointer unaryOperatorResult(Token _operator) const override; + virtual TypePointer binaryOperatorResult(Token _operator, TypePointer const& _other) const override; virtual unsigned calldataEncodedSize(bool _padded) const override{ return _padded ? 32 : 1; } virtual unsigned storageBytes() const override { return 1; } @@ -562,8 +622,8 @@ public: explicit ReferenceType(DataLocation _location): m_location(_location) {} DataLocation location() const { return m_location; } - virtual TypePointer unaryOperatorResult(Token::Value _operator) const override; - virtual TypePointer binaryOperatorResult(Token::Value, TypePointer const&) const override + virtual TypePointer unaryOperatorResult(Token _operator) const override; + virtual TypePointer binaryOperatorResult(Token, TypePointer const&) const override { return TypePointer(); } @@ -614,6 +674,9 @@ protected: class ArrayType: public ReferenceType { public: + static ArrayType& bytesMemory() { static std::shared_ptr<ArrayType> addr(std::make_shared<ArrayType>(DataLocation::Memory)); return *addr; } + static ArrayType& stringMemory() { static std::shared_ptr<ArrayType> addr(std::make_shared<ArrayType>(DataLocation::Memory, true)); return *addr; } + virtual Category category() const override { return Category::Array; } /// Constructor for a byte array ("bytes") and string. @@ -691,11 +754,11 @@ public: virtual Category category() const override { return Category::Contract; } explicit ContractType(ContractDefinition const& _contract, bool _super = false): m_contract(_contract), m_super(_super) {} - /// Contracts can be implicitly converted to super classes and to addresses. + /// Contracts can be implicitly converted only to base contracts. virtual bool isImplicitlyConvertibleTo(Type const& _convertTo) const override; - /// Contracts can be converted to themselves and to integers. + /// Contracts can only be explicitly converted to address types and base contracts. virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const override; - virtual TypePointer unaryOperatorResult(Token::Value _operator) const override; + virtual TypePointer unaryOperatorResult(Token _operator) const override; virtual std::string richIdentifier() const override; virtual bool operator==(Type const& _other) const override; virtual unsigned calldataEncodedSize(bool _padded ) const override @@ -715,7 +778,7 @@ public: { if (isSuper()) return TypePointer{}; - return std::make_shared<IntegerType>(160, IntegerType::Modifier::Address); + return std::make_shared<AddressType>(isPayable() ? StateMutability::Payable : StateMutability::NonPayable); } virtual TypePointer interfaceType(bool _inLibrary) const override { @@ -739,8 +802,6 @@ public: std::vector<std::tuple<VariableDeclaration const*, u256, unsigned>> stateVariables() const; private: - static void addNonConflictingAddressMembers(MemberList::MemberMap& _members); - ContractDefinition const& m_contract; /// If true, it is the "super" type of the current contract, i.e. it contains only inherited /// members. @@ -813,7 +874,7 @@ class EnumType: public Type public: virtual Category category() const override { return Category::Enum; } explicit EnumType(EnumDefinition const& _enum): m_enum(_enum) {} - virtual TypePointer unaryOperatorResult(Token::Value _operator) const override; + virtual TypePointer unaryOperatorResult(Token _operator) const override; virtual std::string richIdentifier() const override; virtual bool operator==(Type const& _other) const override; virtual unsigned calldataEncodedSize(bool _padded) const override @@ -857,7 +918,7 @@ public: virtual bool isImplicitlyConvertibleTo(Type const& _other) const override; virtual std::string richIdentifier() const override; virtual bool operator==(Type const& _other) const override; - virtual TypePointer binaryOperatorResult(Token::Value, TypePointer const&) const override { return TypePointer(); } + virtual TypePointer binaryOperatorResult(Token, TypePointer const&) const override { return TypePointer(); } virtual std::string toString(bool) const override; virtual bool canBeStored() const override { return false; } virtual u256 storageSize() const override; @@ -887,15 +948,15 @@ public: { Internal, ///< stack-call using plain JUMP External, ///< external call using CALL - CallCode, ///< external call using CALLCODE, i.e. not exchanging the storage DelegateCall, ///< external call using DELEGATECALL, i.e. not exchanging the storage BareCall, ///< CALL without function hash BareCallCode, ///< CALLCODE without function hash BareDelegateCall, ///< DELEGATECALL without function hash + BareStaticCall, ///< STATICCALL without function hash Creation, ///< external call using CREATE Send, ///< CALL, but without data and gas Transfer, ///< CALL, but without data and throws on error - SHA3, ///< SHA3 + KECCAK256, ///< KECCAK256 Selfdestruct, ///< SELFDESTRUCT Revert, ///< REVERT ECRecover, ///< CALL to special contract for ecrecover @@ -913,6 +974,7 @@ public: AddMod, ///< ADDMOD MulMod, ///< MULMOD ArrayPush, ///< .push() to a dynamically sized array in storage + ArrayPop, ///< .pop() from a dynamically sized array in storage ByteArrayPush, ///< .push() to a dynamically sized byte array in storage ObjectCreation, ///< array creation using new Assert, ///< assert() @@ -921,7 +983,8 @@ public: ABIEncodePacked, ABIEncodeWithSelector, ABIEncodeWithSignature, - GasLeft ///< gasleft() + ABIDecode, + GasLeft, ///< gasleft() }; virtual Category category() const override { return Category::Function; } @@ -1000,9 +1063,10 @@ public: virtual std::string richIdentifier() const override; virtual bool operator==(Type const& _other) const override; + virtual bool isImplicitlyConvertibleTo(Type const& _convertTo) const override; virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const override; - virtual TypePointer unaryOperatorResult(Token::Value _operator) const override; - virtual TypePointer binaryOperatorResult(Token::Value, TypePointer const&) const override; + virtual TypePointer unaryOperatorResult(Token _operator) const override; + virtual TypePointer binaryOperatorResult(Token, TypePointer const&) const override; virtual std::string canonicalName() const override; virtual std::string toString(bool _short) const override; virtual unsigned calldataEncodedSize(bool _padded) const override; @@ -1029,10 +1093,14 @@ public: /// @param _selfType if the function is bound, this has to be supplied and is the type of the /// expression the function is called on. bool canTakeArguments(TypePointers const& _arguments, TypePointer const& _selfType = TypePointer()) const; - /// @returns true if the types of parameters are equal (does't check return parameter types) - bool hasEqualArgumentTypes(FunctionType const& _other) const; - - /// @returns true if the ABI is used for this call (only meaningful for external calls) + /// @returns true if the types of parameters are equal (does not check return parameter types) + bool hasEqualParameterTypes(FunctionType const& _other) const; + /// @returns true iff the return types are equal (does not check parameter types) + bool hasEqualReturnTypes(FunctionType const& _other) const; + /// @returns true iff the function type is equal to the given type, ignoring state mutability differences. + bool equalExcludingStateMutability(FunctionType const& _other) const; + + /// @returns true if the ABI is NOT used for this call (only meaningful for external calls) bool isBareCall() const; Kind const& kind() const { return m_kind; } StateMutability stateMutability() const { return m_stateMutability; } @@ -1056,18 +1124,22 @@ public: ASTPointer<ASTString> documentation() const; /// true iff arguments are to be padded to multiples of 32 bytes for external calls - bool padArguments() const { return !(m_kind == Kind::SHA3 || m_kind == Kind::SHA256 || m_kind == Kind::RIPEMD160 || m_kind == Kind::ABIEncodePacked); } + /// The only functions that do not pad are hash functions, the low-level call functions + /// and abi.encodePacked. + bool padArguments() const; bool takesArbitraryParameters() const { return m_arbitraryParameters; } /// true iff the function takes a single bytes parameter and it is passed on without padding. - /// @todo until 0.5.0, this is just a "recommendation". bool takesSinglePackedBytesParameter() const { - // @todo add the call kinds here with 0.5.0 and perhaps also log0. switch (m_kind) { - case FunctionType::Kind::SHA3: + case FunctionType::Kind::KECCAK256: case FunctionType::Kind::SHA256: case FunctionType::Kind::RIPEMD160: + case FunctionType::Kind::BareCall: + case FunctionType::Kind::BareCallCode: + case FunctionType::Kind::BareDelegateCall: + case FunctionType::Kind::BareStaticCall: return true; default: return false; @@ -1124,7 +1196,7 @@ public: virtual std::string toString(bool _short) const override; virtual std::string canonicalName() const override; virtual bool canLiveOutsideStorage() const override { return false; } - virtual TypePointer binaryOperatorResult(Token::Value, TypePointer const&) const override { return TypePointer(); } + virtual TypePointer binaryOperatorResult(Token, TypePointer const&) const override { return TypePointer(); } virtual TypePointer encodingType() const override { return std::make_shared<IntegerType>(256); @@ -1157,7 +1229,7 @@ public: explicit TypeType(TypePointer const& _actualType): m_actualType(_actualType) {} TypePointer const& actualType() const { return m_actualType; } - virtual TypePointer binaryOperatorResult(Token::Value, TypePointer const&) const override { return TypePointer(); } + virtual TypePointer binaryOperatorResult(Token, TypePointer const&) const override { return TypePointer(); } virtual std::string richIdentifier() const override; virtual bool operator==(Type const& _other) const override; virtual bool canBeStored() const override { return false; } @@ -1182,7 +1254,7 @@ public: virtual Category category() const override { return Category::Modifier; } explicit ModifierType(ModifierDefinition const& _modifier); - virtual TypePointer binaryOperatorResult(Token::Value, TypePointer const&) const override { return TypePointer(); } + virtual TypePointer binaryOperatorResult(Token, TypePointer const&) const override { return TypePointer(); } virtual bool canBeStored() const override { return false; } virtual u256 storageSize() const override; virtual bool canLiveOutsideStorage() const override { return false; } @@ -1208,7 +1280,7 @@ public: explicit ModuleType(SourceUnit const& _source): m_sourceUnit(_source) {} - virtual TypePointer binaryOperatorResult(Token::Value, TypePointer const&) const override { return TypePointer(); } + virtual TypePointer binaryOperatorResult(Token, TypePointer const&) const override { return TypePointer(); } virtual std::string richIdentifier() const override; virtual bool operator==(Type const& _other) const override; virtual bool canBeStored() const override { return false; } @@ -1235,7 +1307,7 @@ public: explicit MagicType(Kind _kind): m_kind(_kind) {} - virtual TypePointer binaryOperatorResult(Token::Value, TypePointer const&) const override + virtual TypePointer binaryOperatorResult(Token, TypePointer const&) const override { return TypePointer(); } @@ -1268,7 +1340,7 @@ public: virtual std::string richIdentifier() const override { return "t_inaccessible"; } virtual bool isImplicitlyConvertibleTo(Type const&) const override { return false; } virtual bool isExplicitlyConvertibleTo(Type const&) const override { return false; } - virtual TypePointer binaryOperatorResult(Token::Value, TypePointer const&) const override { return TypePointer(); } + virtual TypePointer binaryOperatorResult(Token, TypePointer const&) const override { return TypePointer(); } virtual unsigned calldataEncodedSize(bool _padded) const override { (void)_padded; return 32; } virtual bool canBeStored() const override { return false; } virtual bool canLiveOutsideStorage() const override { return false; } diff --git a/libsolidity/codegen/ABIFunctions.cpp b/libsolidity/codegen/ABIFunctions.cpp index 3e3aa0ae..bd29b382 100644 --- a/libsolidity/codegen/ABIFunctions.cpp +++ b/libsolidity/codegen/ABIFunctions.cpp @@ -17,7 +17,7 @@ /** * @author Christian <chris@ethereum.org> * @date 2017 - * Routines that generate JULIA code related to ABI encoding, decoding and type conversions. + * Routines that generate Yul code related to ABI encoding, decoding and type conversions. */ #include <libsolidity/codegen/ABIFunctions.h> @@ -49,7 +49,7 @@ string ABIFunctions::tupleEncoder( if (_encodeAsLibraryTypes) functionName += "_library"; - return createFunction(functionName, [&]() { + return createExternallyUsedFunction(functionName, [&]() { solAssert(!_givenTypes.empty(), ""); // Note that the values are in reverse due to the difference in calling semantics. @@ -113,7 +113,7 @@ string ABIFunctions::tupleDecoder(TypePointers const& _types, bool _fromMemory) solAssert(!_types.empty(), ""); - return createFunction(functionName, [&]() { + return createExternallyUsedFunction(functionName, [&]() { TypePointers decodingTypes; for (auto const& t: _types) decodingTypes.emplace_back(t->decodingType()); @@ -176,13 +176,13 @@ string ABIFunctions::tupleDecoder(TypePointers const& _types, bool _fromMemory) }); } -string ABIFunctions::requestedFunctions() +pair<string, set<string>> ABIFunctions::requestedFunctions() { string result; for (auto const& f: m_requestedFunctions) result += f.second; m_requestedFunctions.clear(); - return result; + return make_pair(result, std::move(m_externallyUsedFunctions)); } string ABIFunctions::cleanupFunction(Type const& _type, bool _revertOnFailure) @@ -197,6 +197,9 @@ string ABIFunctions::cleanupFunction(Type const& _type, bool _revertOnFailure) templ("functionName", functionName); switch (_type.category()) { + case Type::Category::Address: + templ("body", "cleaned := " + cleanupFunction(IntegerType(160)) + "(value)"); + break; case Type::Category::Integer: { IntegerType const& type = dynamic_cast<IntegerType const&>(_type); @@ -228,7 +231,8 @@ string ABIFunctions::cleanupFunction(Type const& _type, bool _revertOnFailure) if (type.numBytes() == 32) templ("body", "cleaned := value"); else if (type.numBytes() == 0) - templ("body", "cleaned := 0"); + // This is disallowed in the type system. + solAssert(false, ""); else { size_t numBits = type.numBytes() * 8; @@ -238,8 +242,14 @@ string ABIFunctions::cleanupFunction(Type const& _type, bool _revertOnFailure) break; } case Type::Category::Contract: - templ("body", "cleaned := " + cleanupFunction(IntegerType(160, IntegerType::Modifier::Address)) + "(value)"); + { + AddressType addressType(dynamic_cast<ContractType const&>(_type).isPayable() ? + StateMutability::Payable : + StateMutability::NonPayable + ); + templ("body", "cleaned := " + cleanupFunction(addressType) + "(value)"); break; + } case Type::Category::Enum: { size_t members = dynamic_cast<EnumType const&>(_type).numberOfMembers(); @@ -283,6 +293,12 @@ string ABIFunctions::conversionFunction(Type const& _from, Type const& _to) auto fromCategory = _from.category(); switch (fromCategory) { + case Type::Category::Address: + body = + Whiskers("converted := <convert>(value)") + ("convert", conversionFunction(IntegerType(160), _to)) + .render(); + break; case Type::Category::Integer: case Type::Category::RationalNumber: case Type::Category::Contract: @@ -313,16 +329,19 @@ string ABIFunctions::conversionFunction(Type const& _from, Type const& _to) .render(); } else if (toCategory == Type::Category::FixedPoint) - { solUnimplemented("Not yet implemented - FixedPointType."); - } + else if (toCategory == Type::Category::Address) + body = + Whiskers("converted := <convert>(value)") + ("convert", conversionFunction(_from, IntegerType(160))) + .render(); else { solAssert( toCategory == Type::Category::Integer || toCategory == Type::Category::Contract, ""); - IntegerType const addressType(160, IntegerType::Modifier::Address); + IntegerType const addressType(160); IntegerType const& to = toCategory == Type::Category::Integer ? dynamic_cast<IntegerType const&>(_to) : @@ -374,6 +393,11 @@ string ABIFunctions::conversionFunction(Type const& _from, Type const& _to) ("shift", shiftRightFunction(256 - from.numBytes() * 8)) ("convert", conversionFunction(IntegerType(from.numBytes() * 8), _to)) .render(); + else if (toCategory == Type::Category::Address) + body = + Whiskers("converted := <convert>(value)") + ("convert", conversionFunction(_from, IntegerType(160))) + .render(); else { // clear for conversion to longer bytes @@ -409,7 +433,7 @@ string ABIFunctions::conversionFunction(Type const& _from, Type const& _to) solAssert(false, ""); } - solAssert(!body.empty(), ""); + solAssert(!body.empty(), _from.canonicalName() + " to " + _to.canonicalName()); templ("body", body); return templ.render(); }); @@ -471,13 +495,8 @@ string ABIFunctions::abiEncodingFunction( bool _fromStack ) { - solUnimplementedAssert( - _to.mobileType() && - _to.mobileType()->interfaceType(_encodeAsLibraryTypes) && - _to.mobileType()->interfaceType(_encodeAsLibraryTypes)->encodingType(), - "Encoding type \"" + _to.toString() + "\" not yet implemented." - ); - TypePointer toInterface = _to.mobileType()->interfaceType(_encodeAsLibraryTypes)->encodingType(); + TypePointer toInterface = _to.fullEncodingType(_encodeAsLibraryTypes, true, false); + solUnimplementedAssert(toInterface, "Encoding type \"" + _to.toString() + "\" not yet implemented."); Type const& to = *toInterface; if (_from.category() == Type::Category::StringLiteral) @@ -886,13 +905,8 @@ string ABIFunctions::abiEncodingFunctionStruct( solAssert(member.type, ""); if (!member.type->canLiveOutsideStorage()) continue; - solUnimplementedAssert( - member.type->mobileType() && - member.type->mobileType()->interfaceType(_encodeAsLibraryTypes) && - member.type->mobileType()->interfaceType(_encodeAsLibraryTypes)->encodingType(), - "Encoding type \"" + member.type->toString() + "\" not yet implemented." - ); - auto memberTypeTo = member.type->mobileType()->interfaceType(_encodeAsLibraryTypes)->encodingType(); + TypePointer memberTypeTo = member.type->fullEncodingType(_encodeAsLibraryTypes, true, false); + solUnimplementedAssert(memberTypeTo, "Encoding type \"" + member.type->toString() + "\" not yet implemented."); auto memberTypeFrom = _from.memberType(member.name); solAssert(memberTypeFrom, ""); bool dynamicMember = memberTypeTo->isDynamicallyEncoded(); @@ -989,7 +1003,7 @@ string ABIFunctions::abiEncodingFunctionStringLiteral( )"); templ("functionName", functionName); - // TODO this can make use of CODECOPY for large strings once we have that in JULIA + // TODO this can make use of CODECOPY for large strings once we have that in Yul size_t words = (value.size() + 31) / 32; templ("overallSize", to_string(32 + words * 32)); templ("length", to_string(value.size())); @@ -1188,7 +1202,8 @@ string ABIFunctions::abiDecodingFunctionCalldataArray(ArrayType const& _type) solAssert(_type.dataStoredIn(DataLocation::CallData), ""); if (!_type.isDynamicallySized()) solAssert(_type.length() < u256("0xffffffffffffffff"), ""); - solAssert(!_type.baseType()->isDynamicallyEncoded(), ""); + if (_type.baseType()->isDynamicallyEncoded()) + solUnimplemented("Calldata arrays with non-value base types are not yet supported by Solidity."); solAssert(_type.baseType()->calldataEncodedSize() < u256("0xffffffffffffffff"), ""); string functionName = @@ -1682,6 +1697,13 @@ string ABIFunctions::createFunction(string const& _name, function<string ()> con return _name; } +string ABIFunctions::createExternallyUsedFunction(string const& _name, function<string ()> const& _creator) +{ + string name = createFunction(_name, _creator); + m_externallyUsedFunctions.insert(name); + return name; +} + size_t ABIFunctions::headSize(TypePointers const& _targetTypes) { size_t headSize = 0; diff --git a/libsolidity/codegen/ABIFunctions.h b/libsolidity/codegen/ABIFunctions.h index db4d40f5..e9ffe4fb 100644 --- a/libsolidity/codegen/ABIFunctions.h +++ b/libsolidity/codegen/ABIFunctions.h @@ -17,7 +17,7 @@ /** * @author Christian <chris@ethereum.org> * @date 2017 - * Routines that generate JULIA code related to ABI encoding, decoding and type conversions. + * Routines that generate Yul code related to ABI encoding, decoding and type conversions. */ #pragma once @@ -28,6 +28,7 @@ #include <vector> #include <functional> +#include <set> #include <map> namespace dev { @@ -80,8 +81,11 @@ public: /// stack slot, it takes exactly that number of values. std::string tupleDecoder(TypePointers const& _types, bool _fromMemory = false); - /// @returns concatenation of all generated functions. - std::string requestedFunctions(); + /// @returns concatenation of all generated functions and a set of the + /// externally used functions. + /// Clears the internal list, i.e. calling it again will result in an + /// empty return value. + std::pair<std::string, std::set<std::string>> requestedFunctions(); private: /// @returns the name of the cleanup function for the given type and @@ -203,7 +207,7 @@ private: std::string arrayLengthFunction(ArrayType const& _type); /// @returns the name of a function that computes the number of bytes required /// to store an array in memory given its length (internally encoded, not ABI encoded). - /// The function reverts for too large lengthes. + /// The function reverts for too large lengths. std::string arrayAllocationSizeFunction(ArrayType const& _type); /// @returns the name of a function that converts a storage slot number /// or a memory pointer to the slot number / memory pointer for the data position of an array @@ -224,12 +228,17 @@ private: /// cases. std::string createFunction(std::string const& _name, std::function<std::string()> const& _creator); + /// Helper function that uses @a _creator to create a function and add it to + /// @a m_requestedFunctions if it has not been created yet and returns @a _name in both + /// cases. Also adds it to the list of externally used functions. + std::string createExternallyUsedFunction(std::string const& _name, std::function<std::string()> const& _creator); + /// @returns the size of the static part of the encoding of the given types. static size_t headSize(TypePointers const& _targetTypes); /// Map from function name to code for a multi-use function. std::map<std::string, std::string> m_requestedFunctions; - + std::set<std::string> m_externallyUsedFunctions; EVMVersion m_evmVersion; }; diff --git a/libsolidity/codegen/ArrayUtils.cpp b/libsolidity/codegen/ArrayUtils.cpp index 0fe66d2d..d33f749c 100644 --- a/libsolidity/codegen/ArrayUtils.cpp +++ b/libsolidity/codegen/ArrayUtils.cpp @@ -303,12 +303,17 @@ void ArrayUtils::copyArrayToMemory(ArrayType const& _sourceType, bool _padToWord m_context << _sourceType.length(); if (baseSize > 1) m_context << u256(baseSize) << Instruction::MUL; - // stack: target source_offset source_len - m_context << Instruction::DUP1 << Instruction::DUP3 << Instruction::DUP5; - // stack: target source_offset source_len source_len source_offset target - m_context << Instruction::CALLDATACOPY; - m_context << Instruction::DUP3 << Instruction::ADD; - m_context << Instruction::SWAP2 << Instruction::POP << Instruction::POP; + + string routine = "calldatacopy(target, source, len)\n"; + if (_padToWordBoundaries) + routine += R"( + // Set padding suffix to zero + mstore(add(target, len), 0) + len := and(add(len, 0x1f), not(0x1f)) + )"; + routine += "target := add(target, len)\n"; + m_context.appendInlineAssembly("{" + routine + "}", {"target", "source", "len"}); + m_context << Instruction::POP << Instruction::POP; } else if (_sourceType.location() == DataLocation::Memory) { @@ -823,6 +828,85 @@ void ArrayUtils::incrementDynamicArraySize(ArrayType const& _type) const })", {"ref"}); } +void ArrayUtils::popStorageArrayElement(ArrayType const& _type) const +{ + solAssert(_type.location() == DataLocation::Storage, ""); + solAssert(_type.isDynamicallySized(), ""); + if (!_type.isByteArray() && _type.baseType()->storageBytes() < 32) + solAssert(_type.baseType()->isValueType(), "Invalid storage size for non-value type."); + + if (_type.isByteArray()) + { + m_context.appendInlineAssembly(R"({ + let slot_value := sload(ref) + switch and(slot_value, 1) + case 0 { + // short byte array + let length := and(div(slot_value, 2), 0x1f) + if iszero(length) { invalid() } + + // Zero-out the suffix including the least significant byte. + let mask := sub(exp(0x100, sub(33, length)), 1) + length := sub(length, 1) + slot_value := or(and(not(mask), slot_value), mul(length, 2)) + sstore(ref, slot_value) + } + case 1 { + // long byte array + mstore(0, ref) + let length := div(slot_value, 2) + let slot := keccak256(0, 0x20) + switch length + case 32 + { + let data := sload(slot) + sstore(slot, 0) + data := and(data, not(0xff)) + sstore(ref, or(data, 62)) + } + default + { + let offset_inside_slot := and(sub(length, 1), 0x1f) + slot := add(slot, div(sub(length, 1), 32)) + let data := sload(slot) + + // Zero-out the suffix of the byte array by masking it. + // ((1<<(8 * (32 - offset))) - 1) + let mask := sub(exp(0x100, sub(32, offset_inside_slot)), 1) + data := and(not(mask), data) + sstore(slot, data) + + // Reduce the length by 1 + slot_value := sub(slot_value, 2) + sstore(ref, slot_value) + } + } + })", {"ref"}); + m_context << Instruction::POP; + } + else + { + // stack: ArrayReference + retrieveLength(_type); + // stack: ArrayReference oldLength + m_context << Instruction::DUP1; + // stack: ArrayReference oldLength oldLength + m_context << Instruction::ISZERO; + m_context.appendConditionalInvalid(); + + // Stack: ArrayReference oldLength + m_context << u256(1) << Instruction::SWAP1 << Instruction::SUB; + // Stack ArrayReference newLength + m_context << Instruction::DUP2 << Instruction::DUP2; + // Stack ArrayReference newLength ArrayReference newLength; + accessIndex(_type, false); + // Stack: ArrayReference newLength storage_slot byte_offset + StorageItem(m_context, *_type.baseType()).setToZero(SourceLocation(), true); + // Stack: ArrayReference newLength + m_context << Instruction::SWAP1 << Instruction::SSTORE; + } +} + void ArrayUtils::clearStorageLoop(TypePointer const& _type) const { m_context.callLowLevelFunction( @@ -1024,8 +1108,6 @@ void ArrayUtils::accessIndex(ArrayType const& _arrayType, bool _doBoundsCheck) c m_context << endTag; break; } - default: - solAssert(false, ""); } } diff --git a/libsolidity/codegen/ArrayUtils.h b/libsolidity/codegen/ArrayUtils.h index 99786397..daf50bf5 100644 --- a/libsolidity/codegen/ArrayUtils.h +++ b/libsolidity/codegen/ArrayUtils.h @@ -73,6 +73,11 @@ public: /// Stack pre: reference (excludes byte offset) /// Stack post: new_length void incrementDynamicArraySize(ArrayType const& _type) const; + /// Decrements the size of a dynamic array by one if length is nonzero. Causes an invalid instruction otherwise. + /// Clears the removed data element. In case of a byte array, this might move the data. + /// Stack pre: reference + /// Stack post: + void popStorageArrayElement(ArrayType const& _type) const; /// Appends a loop that clears a sequence of storage slots of the given type (excluding end). /// Stack pre: end_ref start_ref /// Stack post: end_ref diff --git a/libsolidity/codegen/Compiler.cpp b/libsolidity/codegen/Compiler.cpp index d3afada5..55f1d252 100644 --- a/libsolidity/codegen/Compiler.cpp +++ b/libsolidity/codegen/Compiler.cpp @@ -46,19 +46,6 @@ void Compiler::compileContract( m_context.optimise(m_optimize, m_optimizeRuns); } -void Compiler::compileClone( - ContractDefinition const& _contract, - map<ContractDefinition const*, eth::Assembly const*> const& _contracts -) -{ - solAssert(!_contract.isLibrary(), ""); - ContractCompiler runtimeCompiler(nullptr, m_runtimeContext, m_optimize); - ContractCompiler cloneCompiler(&runtimeCompiler, m_context, m_optimize); - m_runtimeSub = cloneCompiler.compileClone(_contract, _contracts); - - m_context.optimise(m_optimize, m_optimizeRuns); -} - eth::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 f6865d75..4028ae63 100644 --- a/libsolidity/codegen/Compiler.h +++ b/libsolidity/codegen/Compiler.h @@ -50,12 +50,6 @@ public: std::map<ContractDefinition const*, eth::Assembly const*> const& _contracts, bytes const& _metadata ); - /// Compiles a contract that uses DELEGATECALL to call into a pre-deployed version of the given - /// contract at runtime, but contains the full creation-time code. - void compileClone( - ContractDefinition const& _contract, - std::map<ContractDefinition const*, eth::Assembly const*> const& _contracts - ); /// @returns Entire assembly. eth::Assembly const& assembly() const { return m_context.assembly(); } /// @returns The entire assembled object (with constructor). diff --git a/libsolidity/codegen/CompilerContext.cpp b/libsolidity/codegen/CompilerContext.cpp index a35eea73..6e14d68a 100644 --- a/libsolidity/codegen/CompilerContext.cpp +++ b/libsolidity/codegen/CompilerContext.cpp @@ -32,6 +32,7 @@ #include <libsolidity/inlineasm/AsmCodeGen.h> #include <libsolidity/inlineasm/AsmAnalysis.h> #include <libsolidity/inlineasm/AsmAnalysisInfo.h> +#include <libyul/YulString.h> #include <boost/algorithm/string/replace.hpp> @@ -127,10 +128,14 @@ void CompilerContext::addVariable(VariableDeclaration const& _declaration, unsigned _offsetToCurrent) { solAssert(m_asm->deposit() >= 0 && unsigned(m_asm->deposit()) >= _offsetToCurrent, ""); + unsigned sizeOnStack = _declaration.annotation().type->sizeOnStack(); + // Variables should not have stack size other than [1, 2], + // but that might change when new types are introduced. + solAssert(sizeOnStack == 1 || sizeOnStack == 2, ""); m_localVariables[&_declaration].push_back(unsigned(m_asm->deposit()) - _offsetToCurrent); } -void CompilerContext::removeVariable(VariableDeclaration const& _declaration) +void CompilerContext::removeVariable(Declaration const& _declaration) { solAssert(m_localVariables.count(&_declaration) && !m_localVariables[&_declaration].empty(), ""); m_localVariables[&_declaration].pop_back(); @@ -138,6 +143,25 @@ void CompilerContext::removeVariable(VariableDeclaration const& _declaration) m_localVariables.erase(&_declaration); } +void CompilerContext::removeVariablesAboveStackHeight(unsigned _stackHeight) +{ + vector<Declaration const*> toRemove; + for (auto _var: m_localVariables) + { + solAssert(!_var.second.empty(), ""); + solAssert(_var.second.back() <= stackHeight(), ""); + if (_var.second.back() >= _stackHeight) + toRemove.push_back(_var.first); + } + for (auto _var: toRemove) + removeVariable(*_var); +} + +unsigned CompilerContext::numberOfLocalVariables() const +{ + return m_localVariables.size(); +} + eth::Assembly const& CompilerContext::compiledContract(const ContractDefinition& _contract) const { auto ret = m_compiledContracts.find(&_contract); @@ -290,32 +314,33 @@ void CompilerContext::resetVisitedNodes(ASTNode const* _node) void CompilerContext::appendInlineAssembly( string const& _assembly, vector<string> const& _localVariables, + set<string> const&, bool _system ) { int startStackHeight = stackHeight(); - julia::ExternalIdentifierAccess identifierAccess; + yul::ExternalIdentifierAccess identifierAccess; identifierAccess.resolve = [&]( assembly::Identifier const& _identifier, - julia::IdentifierContext, + yul::IdentifierContext, bool ) { - auto it = std::find(_localVariables.begin(), _localVariables.end(), _identifier.name); + auto it = std::find(_localVariables.begin(), _localVariables.end(), _identifier.name.str()); return it == _localVariables.end() ? size_t(-1) : 1; }; identifierAccess.generateCode = [&]( assembly::Identifier const& _identifier, - julia::IdentifierContext _context, - julia::AbstractAssembly& _assembly + yul::IdentifierContext _context, + yul::AbstractAssembly& _assembly ) { - auto it = std::find(_localVariables.begin(), _localVariables.end(), _identifier.name); + auto it = std::find(_localVariables.begin(), _localVariables.end(), _identifier.name.str()); solAssert(it != _localVariables.end(), ""); int stackDepth = _localVariables.end() - it; int stackDiff = _assembly.stackHeight() - startStackHeight + stackDepth; - if (_context == julia::IdentifierContext::LValue) + if (_context == yul::IdentifierContext::LValue) stackDiff -= 1; if (stackDiff < 1 || stackDiff > 16) BOOST_THROW_EXCEPTION( @@ -323,7 +348,7 @@ void CompilerContext::appendInlineAssembly( errinfo_sourceLocation(_identifier.location) << errinfo_comment("Stack too deep (" + to_string(stackDiff) + "), try removing local variables.") ); - if (_context == julia::IdentifierContext::RValue) + if (_context == yul::IdentifierContext::RValue) _assembly.appendInstruction(dupInstruction(stackDiff)); else { @@ -388,7 +413,7 @@ FunctionDefinition const& CompilerContext::resolveVirtualFunction( if ( function->name() == name && !function->isConstructor() && - FunctionType(*function).hasEqualArgumentTypes(functionType) + FunctionType(*function).hasEqualParameterTypes(functionType) ) return *function; solAssert(false, "Super function " + name + " not found."); diff --git a/libsolidity/codegen/CompilerContext.h b/libsolidity/codegen/CompilerContext.h index 5776b5d1..5bdc1d19 100644 --- a/libsolidity/codegen/CompilerContext.h +++ b/libsolidity/codegen/CompilerContext.h @@ -71,7 +71,11 @@ public: void addStateVariable(VariableDeclaration const& _declaration, u256 const& _storageOffset, unsigned _byteOffset); void addVariable(VariableDeclaration const& _declaration, unsigned _offsetToCurrent = 0); - void removeVariable(VariableDeclaration const& _declaration); + void removeVariable(Declaration const& _declaration); + /// Removes all local variables currently allocated above _stackHeight. + void removeVariablesAboveStackHeight(unsigned _stackHeight); + /// Returns the number of currently allocated local variables. + unsigned numberOfLocalVariables() const; void setCompiledContracts(std::map<ContractDefinition const*, eth::Assembly const*> const& _contracts) { m_compiledContracts = _contracts; } eth::Assembly const& compiledContract(ContractDefinition const& _contract) const; @@ -202,10 +206,12 @@ public: /// Appends inline assembly (strict mode). /// @a _replacements are string-matching replacements that are performed prior to parsing the inline assembly. /// @param _localVariables assigns stack positions to variables with the last one being the stack top + /// @param _externallyUsedFunctions a set of function names that are not to be renamed or removed. /// @param _system if true, this is a "system-level" assembly where all functions use named labels. void appendInlineAssembly( std::string const& _assembly, std::vector<std::string> const& _localVariables = std::vector<std::string>(), + std::set<std::string> const& _externallyUsedFunctions = std::set<std::string>(), bool _system = false ); diff --git a/libsolidity/codegen/CompilerUtils.cpp b/libsolidity/codegen/CompilerUtils.cpp index d9f17263..90eb74fe 100644 --- a/libsolidity/codegen/CompilerUtils.cpp +++ b/libsolidity/codegen/CompilerUtils.cpp @@ -142,9 +142,13 @@ void CompilerUtils::storeInMemory(unsigned _offset) void CompilerUtils::storeInMemoryDynamic(Type const& _type, bool _padToWordBoundaries) { + // process special types (Reference, StringLiteral, Function) if (auto ref = dynamic_cast<ReferenceType const*>(&_type)) { - solUnimplementedAssert(ref->location() == DataLocation::Memory, "Only in-memory reference type can be stored."); + solUnimplementedAssert( + ref->location() == DataLocation::Memory, + "Only in-memory reference type can be stored." + ); storeInMemoryDynamic(IntegerType(256), _padToWordBoundaries); } else if (auto str = dynamic_cast<StringLiteralType const*>(&_type)) @@ -166,27 +170,27 @@ void CompilerUtils::storeInMemoryDynamic(Type const& _type, bool _padToWordBound m_context << Instruction::DUP2 << Instruction::MSTORE; m_context << u256(_padToWordBoundaries ? 32 : 24) << Instruction::ADD; } - else + else if (_type.isValueType()) { unsigned numBytes = prepareMemoryStore(_type, _padToWordBoundaries); - if (numBytes > 0) - { - solUnimplementedAssert( - _type.sizeOnStack() == 1, - "Memory store of types with stack size != 1 not implemented." - ); - m_context << Instruction::DUP2 << Instruction::MSTORE; - m_context << u256(numBytes) << Instruction::ADD; - } + m_context << Instruction::DUP2 << Instruction::MSTORE; + m_context << u256(numBytes) << Instruction::ADD; + } + else // Should never happen + { + solAssert( + false, + "Memory store of type " + _type.toString(true) + " not allowed." + ); } } -void CompilerUtils::abiDecode(TypePointers const& _typeParameters, bool _fromMemory, bool _revertOnOutOfBounds) +void CompilerUtils::abiDecode(TypePointers const& _typeParameters, bool _fromMemory) { /// Stack: <source_offset> <length> if (m_context.experimentalFeatureActive(ExperimentalFeature::ABIEncoderV2)) { - // Use the new JULIA-based decoding function + // Use the new Yul-based decoding function auto stackHeightBefore = m_context.stackHeight(); abiDecodeV2(_typeParameters, _fromMemory); solAssert(m_context.stackHeight() - stackHeightBefore == sizeOnStack(_typeParameters) - 2, ""); @@ -194,14 +198,10 @@ void CompilerUtils::abiDecode(TypePointers const& _typeParameters, bool _fromMem } //@todo this does not yet support nested dynamic arrays - - if (_revertOnOutOfBounds) - { - size_t encodedSize = 0; - for (auto const& t: _typeParameters) - encodedSize += t->decodingType()->calldataEncodedSize(true); - m_context.appendInlineAssembly("{ if lt(len, " + to_string(encodedSize) + ") { revert(0, 0) } }", {"len"}); - } + size_t encodedSize = 0; + for (auto const& t: _typeParameters) + encodedSize += t->decodingType()->calldataEncodedSize(true); + m_context.appendInlineAssembly("{ if lt(len, " + to_string(encodedSize) + ") { revert(0, 0) } }", {"len"}); m_context << Instruction::DUP2 << Instruction::ADD; m_context << Instruction::SWAP1; @@ -231,26 +231,21 @@ void CompilerUtils::abiDecode(TypePointers const& _typeParameters, bool _fromMem { // compute data pointer m_context << Instruction::DUP1 << Instruction::MLOAD; - if (_revertOnOutOfBounds) - { - // Check that the data pointer is valid and that length times - // item size is still inside the range. - Whiskers templ(R"({ - if gt(ptr, 0x100000000) { revert(0, 0) } - ptr := add(ptr, base_offset) - let array_data_start := add(ptr, 0x20) - if gt(array_data_start, input_end) { revert(0, 0) } - let array_length := mload(ptr) - if or( - gt(array_length, 0x100000000), - gt(add(array_data_start, mul(array_length, <item_size>)), input_end) - ) { revert(0, 0) } - })"); - templ("item_size", to_string(arrayType.isByteArray() ? 1 : arrayType.baseType()->calldataEncodedSize(true))); - m_context.appendInlineAssembly(templ.render(), {"input_end", "base_offset", "offset", "ptr"}); - } - else - m_context << Instruction::DUP3 << Instruction::ADD; + // Check that the data pointer is valid and that length times + // item size is still inside the range. + Whiskers templ(R"({ + if gt(ptr, 0x100000000) { revert(0, 0) } + ptr := add(ptr, base_offset) + let array_data_start := add(ptr, 0x20) + if gt(array_data_start, input_end) { revert(0, 0) } + let array_length := mload(ptr) + if or( + gt(array_length, 0x100000000), + gt(add(array_data_start, mul(array_length, <item_size>)), input_end) + ) { revert(0, 0) } + })"); + templ("item_size", to_string(arrayType.isByteArray() ? 1 : arrayType.baseType()->calldataEncodedSize(true))); + m_context.appendInlineAssembly(templ.render(), {"input_end", "base_offset", "offset", "ptr"}); // stack: v1 v2 ... v(k-1) input_end base_offset current_offset v(k) moveIntoStack(3); m_context << u256(0x20) << Instruction::ADD; @@ -273,30 +268,25 @@ void CompilerUtils::abiDecode(TypePointers const& _typeParameters, bool _fromMem loadFromMemoryDynamic(IntegerType(256), !_fromMemory); m_context << Instruction::SWAP1; // stack: input_end base_offset next_pointer data_offset - if (_revertOnOutOfBounds) - m_context.appendInlineAssembly("{ if gt(data_offset, 0x100000000) { revert(0, 0) } }", {"data_offset"}); + m_context.appendInlineAssembly("{ if gt(data_offset, 0x100000000) { revert(0, 0) } }", {"data_offset"}); m_context << Instruction::DUP3 << Instruction::ADD; // stack: input_end base_offset next_pointer array_head_ptr - if (_revertOnOutOfBounds) - m_context.appendInlineAssembly( - "{ if gt(add(array_head_ptr, 0x20), input_end) { revert(0, 0) } }", - {"input_end", "base_offset", "next_ptr", "array_head_ptr"} - ); + m_context.appendInlineAssembly( + "{ if gt(add(array_head_ptr, 0x20), input_end) { revert(0, 0) } }", + {"input_end", "base_offset", "next_ptr", "array_head_ptr"} + ); // retrieve length loadFromMemoryDynamic(IntegerType(256), !_fromMemory, true); // stack: input_end base_offset next_pointer array_length data_pointer m_context << Instruction::SWAP2; // stack: input_end base_offset data_pointer array_length next_pointer - if (_revertOnOutOfBounds) - { - unsigned itemSize = arrayType.isByteArray() ? 1 : arrayType.baseType()->calldataEncodedSize(true); - m_context.appendInlineAssembly(R"({ - if or( - gt(array_length, 0x100000000), - gt(add(data_ptr, mul(array_length, )" + to_string(itemSize) + R"()), input_end) - ) { revert(0, 0) } - })", {"input_end", "base_offset", "data_ptr", "array_length", "next_ptr"}); - } + unsigned itemSize = arrayType.isByteArray() ? 1 : arrayType.baseType()->calldataEncodedSize(true); + m_context.appendInlineAssembly(R"({ + if or( + gt(array_length, 0x100000000), + gt(add(data_ptr, mul(array_length, )" + to_string(itemSize) + R"()), input_end) + ) { revert(0, 0) } + })", {"input_end", "base_offset", "data_ptr", "array_length", "next_ptr"}); } else { @@ -347,28 +337,21 @@ void CompilerUtils::encodeToMemory( ) { // stack: <v1> <v2> ... <vn> <mem> + bool const encoderV2 = m_context.experimentalFeatureActive(ExperimentalFeature::ABIEncoderV2); TypePointers targetTypes = _targetTypes.empty() ? _givenTypes : _targetTypes; solAssert(targetTypes.size() == _givenTypes.size(), ""); for (TypePointer& t: targetTypes) { - solUnimplementedAssert( - t->mobileType() && - t->mobileType()->interfaceType(_encodeAsLibraryTypes) && - t->mobileType()->interfaceType(_encodeAsLibraryTypes)->encodingType(), - "Encoding type \"" + t->toString() + "\" not yet implemented." - ); - t = t->mobileType()->interfaceType(_encodeAsLibraryTypes)->encodingType(); + TypePointer tEncoding = t->fullEncodingType(_encodeAsLibraryTypes, encoderV2, !_padToWordBoundaries); + solUnimplementedAssert(tEncoding, "Encoding type \"" + t->toString() + "\" not yet implemented."); + t = std::move(tEncoding); } if (_givenTypes.empty()) return; - else if ( - _padToWordBoundaries && - !_copyDynamicDataInPlace && - m_context.experimentalFeatureActive(ExperimentalFeature::ABIEncoderV2) - ) + else if (_padToWordBoundaries && !_copyDynamicDataInPlace && encoderV2) { - // Use the new JULIA-based encoding function + // Use the new Yul-based encoding function auto stackHeightBefore = m_context.stackHeight(); abiEncodeV2(_givenTypes, targetTypes, _encodeAsLibraryTypes); solAssert(stackHeightBefore - m_context.stackHeight() == sizeOnStack(_givenTypes), ""); @@ -377,8 +360,8 @@ void CompilerUtils::encodeToMemory( // Stack during operation: // <v1> <v2> ... <vn> <mem_start> <dyn_head_1> ... <dyn_head_r> <end_of_mem> - // The values dyn_head_i are added during the first loop and they point to the head part - // of the ith dynamic parameter, which is filled once the dynamic parts are processed. + // The values dyn_head_n are added during the first loop and they point to the head part + // of the nth dynamic parameter, which is filled once the dynamic parts are processed. // store memory start pointer m_context << Instruction::DUP1; @@ -524,7 +507,7 @@ void CompilerUtils::zeroInitialiseMemoryArray(ArrayType const& _type) codecopy(memptr, codesize(), size) memptr := add(memptr, size) })"); - templ("element_size", to_string(_type.baseType()->memoryHeadSize())); + templ("element_size", to_string(_type.isByteArray() ? 1 : _type.baseType()->memoryHeadSize())); m_context.appendInlineAssembly(templ.render(), {"length", "memptr"}); } else @@ -678,6 +661,11 @@ void CompilerUtils::convertType( if (targetIntegerType.numBits() < typeOnStack.numBytes() * 8) convertType(IntegerType(typeOnStack.numBytes() * 8), _targetType, _cleanupNeeded); } + else if (targetTypeCategory == Type::Category::Address) + { + solAssert(typeOnStack.numBytes() * 8 == 160, ""); + rightShiftNumberOnStack(256 - 160); + } else { // clear for conversion to longer bytes @@ -711,23 +699,33 @@ void CompilerUtils::convertType( break; case Type::Category::FixedPoint: solUnimplemented("Not yet implemented - FixedPointType."); + case Type::Category::Address: case Type::Category::Integer: case Type::Category::Contract: case Type::Category::RationalNumber: if (targetTypeCategory == Type::Category::FixedBytes) { - solAssert(stackTypeCategory == Type::Category::Integer || stackTypeCategory == Type::Category::RationalNumber, - "Invalid conversion to FixedBytesType requested."); + solAssert( + stackTypeCategory == Type::Category::Address || + stackTypeCategory == Type::Category::Integer || + stackTypeCategory == Type::Category::RationalNumber, + "Invalid conversion to FixedBytesType requested." + ); // conversion from bytes to string. no need to clean the high bit // only to shift left because of opposite alignment FixedBytesType const& targetBytesType = dynamic_cast<FixedBytesType const&>(_targetType); if (auto typeOnStack = dynamic_cast<IntegerType const*>(&_typeOnStack)) + { if (targetBytesType.numBytes() * 8 > typeOnStack->numBits()) cleanHigherOrderBits(*typeOnStack); + } + else if (stackTypeCategory == Type::Category::Address) + solAssert(targetBytesType.numBytes() * 8 == 160, ""); leftShiftNumberOnStack(256 - targetBytesType.numBytes() * 8); } else if (targetTypeCategory == Type::Category::Enum) { + solAssert(stackTypeCategory != Type::Category::Address, "Invalid conversion to EnumType requested."); solAssert(_typeOnStack.mobileType(), ""); // just clean convertType(_typeOnStack, *_typeOnStack.mobileType(), true); @@ -754,8 +752,8 @@ void CompilerUtils::convertType( } else { - solAssert(targetTypeCategory == Type::Category::Integer || targetTypeCategory == Type::Category::Contract, ""); - IntegerType addressType(160, IntegerType::Modifier::Address); + solAssert(targetTypeCategory == Type::Category::Integer || targetTypeCategory == Type::Category::Contract || targetTypeCategory == Type::Category::Address, ""); + IntegerType addressType(160); IntegerType const& targetType = targetTypeCategory == Type::Category::Integer ? dynamic_cast<IntegerType const&>(_targetType) : addressType; if (stackTypeCategory == Type::Category::RationalNumber) @@ -901,15 +899,6 @@ void CompilerUtils::convertType( typeOnStack.location() == DataLocation::CallData, "Invalid conversion to calldata type."); break; - default: - solAssert( - false, - "Invalid type conversion " + - _typeOnStack.toString(false) + - " to " + - _targetType.toString(false) + - " requested." - ); } break; } @@ -968,20 +957,12 @@ void CompilerUtils::convertType( { TupleType const& sourceTuple = dynamic_cast<TupleType const&>(_typeOnStack); TupleType const& targetTuple = dynamic_cast<TupleType const&>(_targetType); - // fillRight: remove excess values at right side, !fillRight: remove eccess values at left side - bool fillRight = !targetTuple.components().empty() && ( - !targetTuple.components().back() || - targetTuple.components().front() - ); + solAssert(targetTuple.components().size() == sourceTuple.components().size(), ""); unsigned depth = sourceTuple.sizeOnStack(); for (size_t i = 0; i < sourceTuple.components().size(); ++i) { TypePointer sourceType = sourceTuple.components()[i]; - TypePointer targetType; - if (fillRight && i < targetTuple.components().size()) - targetType = targetTuple.components()[i]; - else if (!fillRight && targetTuple.components().size() + i >= sourceTuple.components().size()) - targetType = targetTuple.components()[targetTuple.components().size() - (sourceTuple.components().size() - i)]; + TypePointer targetType = targetTuple.components()[i]; if (!sourceType) { solAssert(!targetType, ""); @@ -1025,10 +1006,8 @@ void CompilerUtils::convertType( m_context << Instruction::ISZERO << Instruction::ISZERO; break; default: - if (stackTypeCategory == Type::Category::Function && targetTypeCategory == Type::Category::Integer) + if (stackTypeCategory == Type::Category::Function && targetTypeCategory == Type::Category::Address) { - IntegerType const& targetType = dynamic_cast<IntegerType const&>(_targetType); - solAssert(targetType.isAddress(), "Function type can only be converted to address."); FunctionType const& typeOnStack = dynamic_cast<FunctionType const&>(_typeOnStack); solAssert(typeOnStack.kind() == FunctionType::Kind::External, "Only external function type can be converted."); @@ -1037,8 +1016,22 @@ void CompilerUtils::convertType( } else { - // All other types should not be convertible to non-equal types. - solAssert(_typeOnStack == _targetType, "Invalid type conversion requested."); + if (stackTypeCategory == Type::Category::Function && targetTypeCategory == Type::Category::Function) + { + FunctionType const& typeOnStack = dynamic_cast<FunctionType const&>(_typeOnStack); + FunctionType const& targetType = dynamic_cast<FunctionType const&>(_targetType); + solAssert( + typeOnStack.isImplicitlyConvertibleTo(targetType) && + typeOnStack.sizeOnStack() == targetType.sizeOnStack() && + (typeOnStack.kind() == FunctionType::Kind::Internal || typeOnStack.kind() == FunctionType::Kind::External) && + typeOnStack.kind() == targetType.kind(), + "Invalid function type conversion requested." + ); + } + else + // All other types should not be convertible to non-equal types. + solAssert(_typeOnStack == _targetType, "Invalid type conversion requested."); + if (_cleanupNeeded && _targetType.canBeStored() && _targetType.storageBytes() < 32) m_context << ((u256(1) << (8 * _targetType.storageBytes())) - 1) @@ -1184,6 +1177,15 @@ void CompilerUtils::popStackSlots(size_t _amount) m_context << Instruction::POP; } +void CompilerUtils::popAndJump(unsigned _toHeight, eth::AssemblyItem const& _jumpTo) +{ + solAssert(m_context.stackHeight() >= _toHeight, ""); + unsigned amount = m_context.stackHeight() - _toHeight; + popStackSlots(amount); + m_context.appendJumpTo(_jumpTo); + m_context.adjustStackOffset(amount); +} + unsigned CompilerUtils::sizeOnStack(vector<shared_ptr<Type const>> const& _variableTypes) { unsigned size = 0; @@ -1282,18 +1284,30 @@ void CompilerUtils::rightShiftNumberOnStack(unsigned _bits) unsigned CompilerUtils::prepareMemoryStore(Type const& _type, bool _padToWords) { + solAssert( + _type.sizeOnStack() == 1, + "Memory store of types with stack size != 1 not allowed (Type: " + _type.toString(true) + ")." + ); + unsigned numBytes = _type.calldataEncodedSize(_padToWords); + + solAssert( + numBytes > 0, + "Memory store of 0 bytes requested (Type: " + _type.toString(true) + ")." + ); + + solAssert( + numBytes <= 32, + "Memory store of more than 32 bytes requested (Type: " + _type.toString(true) + ")." + ); + bool leftAligned = _type.category() == Type::Category::FixedBytes; - if (numBytes == 0) - m_context << Instruction::POP; - else - { - solAssert(numBytes <= 32, "Memory store of more than 32 bytes requested."); - convertType(_type, _type, true); - if (numBytes != 32 && !leftAligned && !_padToWords) - // shift the value accordingly before storing - leftShiftNumberOnStack((32 - numBytes) * 8); - } + + convertType(_type, _type, true); + if (numBytes != 32 && !leftAligned && !_padToWords) + // shift the value accordingly before storing + leftShiftNumberOnStack((32 - numBytes) * 8); + return numBytes; } diff --git a/libsolidity/codegen/CompilerUtils.h b/libsolidity/codegen/CompilerUtils.h index 8e3a8a5d..bd8170ad 100644 --- a/libsolidity/codegen/CompilerUtils.h +++ b/libsolidity/codegen/CompilerUtils.h @@ -97,12 +97,12 @@ public: /// Creates code that unpacks the arguments according to their types specified by a vector of TypePointers. /// From memory if @a _fromMemory is true, otherwise from call data. - /// Calls revert if @a _revertOnOutOfBounds is true and the supplied size is shorter - /// than the static data requirements or if dynamic data pointers reach outside of the - /// area. Also has a hard cap of 0x100000000 for any given length/offset field. + /// Calls revert if the supplied size is shorter than the static data requirements + /// or if dynamic data pointers reach outside of the area. + /// Also has a hard cap of 0x100000000 for any given length/offset field. /// Stack pre: <source_offset> <length> /// Stack post: <value0> <value1> ... <valuen> - void abiDecode(TypePointers const& _typeParameters, bool _fromMemory = false, bool _revertOnOutOfBounds = false); + void abiDecode(TypePointers const& _typeParameters, bool _fromMemory = false); /// Copies values (of types @a _givenTypes) given on the stack to a location in memory given /// at the stack top, encoding them according to the ABI as the given types @a _targetTypes. @@ -241,6 +241,10 @@ public: void popStackElement(Type const& _type); /// Removes element from the top of the stack _amount times. void popStackSlots(size_t _amount); + /// Pops slots from the stack such that its height is _toHeight. + /// Adds jump to _jumpTo. + /// Readjusts the stack offset to the original value. + void popAndJump(unsigned _toHeight, eth::AssemblyItem const& _jumpTo); template <class T> static unsigned sizeOnStack(std::vector<T> const& _variables); @@ -256,7 +260,7 @@ public: /// Stack post: <shifted_value> void rightShiftNumberOnStack(unsigned _bits); - /// Appends code that computes tha Keccak-256 hash of the topmost stack element of 32 byte type. + /// Appends code that computes the Keccak-256 hash of the topmost stack element of 32 byte type. void computeHashStatic(); /// Bytes we need to the start of call data. diff --git a/libsolidity/codegen/ContractCompiler.cpp b/libsolidity/codegen/ContractCompiler.cpp index 0889ac7c..1fdf3483 100644 --- a/libsolidity/codegen/ContractCompiler.cpp +++ b/libsolidity/codegen/ContractCompiler.cpp @@ -50,7 +50,7 @@ class StackHeightChecker public: explicit StackHeightChecker(CompilerContext const& _context): m_context(_context), stackHeight(m_context.stackHeight()) {} - void check() { solAssert(m_context.stackHeight() == stackHeight, std::string("I sense a disturbance in the stack: ") + std::to_string(m_context.stackHeight()) + " vs " + std::to_string(stackHeight)); } + void check() { solAssert(m_context.stackHeight() == stackHeight, std::string("I sense a disturbance in the stack: ") + to_string(m_context.stackHeight()) + " vs " + to_string(stackHeight)); } private: CompilerContext const& m_context; unsigned stackHeight; @@ -71,7 +71,11 @@ void ContractCompiler::compileContract( appendDelegatecallCheck(); initializeContext(_contract, _contracts); + // This generates the dispatch function for externally visible functions + // and adds the function to the compilation queue. Additionally internal functions, + // which are referenced directly or indirectly will be added. appendFunctionSelector(_contract); + // This processes the above populated queue until it is empty. appendMissingFunctions(); } @@ -90,27 +94,6 @@ size_t ContractCompiler::compileConstructor( } } -size_t ContractCompiler::compileClone( - ContractDefinition const& _contract, - map<ContractDefinition const*, eth::Assembly const*> const& _contracts -) -{ - initializeContext(_contract, _contracts); - - appendInitAndConstructorCode(_contract); - - //@todo determine largest return size of all runtime functions - auto runtimeSub = m_context.addSubroutine(cloneRuntime()); - - // stack contains sub size - m_context << Instruction::DUP1 << runtimeSub << u256(0) << Instruction::CODECOPY; - m_context << u256(0) << Instruction::RETURN; - - appendMissingFunctions(); - - return size_t(runtimeSub.data()); -} - void ContractCompiler::initializeContext( ContractDefinition const& _contract, map<ContractDefinition const*, eth::Assembly const*> const& _compiledContracts @@ -427,7 +410,7 @@ bool ContractCompiler::visit(FunctionDefinition const& _function) m_context.startFunction(_function); // stack upon entry: [return address] [arg0] [arg1] ... [argn] - // reserve additional slots: [retarg0] ... [retargm] [localvar0] ... [localvarp] + // reserve additional slots: [retarg0] ... [retargm] unsigned parametersSize = CompilerUtils::sizeOnStack(_function.parameters()); if (!_function.isConstructor()) @@ -441,8 +424,6 @@ bool ContractCompiler::visit(FunctionDefinition const& _function) for (ASTPointer<VariableDeclaration const> const& variable: _function.returnParameters()) appendStackVariableInitialisation(*variable); - for (VariableDeclaration const* localVariable: _function.localVariables()) - appendStackVariableInitialisation(*localVariable); if (_function.isConstructor()) if (auto c = m_context.nextConstructor(dynamic_cast<ContractDefinition const&>(*_function.scope()))) @@ -451,12 +432,11 @@ bool ContractCompiler::visit(FunctionDefinition const& _function) solAssert(m_returnTags.empty(), ""); m_breakTags.clear(); m_continueTags.clear(); - m_stackCleanupForReturn = 0; m_currentFunction = &_function; m_modifierDepth = -1; + m_scopeStackHeight.clear(); appendModifierOrFunctionCode(); - solAssert(m_returnTags.empty(), ""); // Now we need to re-shuffle the stack. For this we keep a record of the stack layout @@ -467,14 +447,12 @@ bool ContractCompiler::visit(FunctionDefinition const& _function) unsigned const c_argumentsSize = CompilerUtils::sizeOnStack(_function.parameters()); unsigned const c_returnValuesSize = CompilerUtils::sizeOnStack(_function.returnParameters()); - unsigned const c_localVariablesSize = CompilerUtils::sizeOnStack(_function.localVariables()); vector<int> stackLayout; stackLayout.push_back(c_returnValuesSize); // target of return address stackLayout += vector<int>(c_argumentsSize, -1); // discard all arguments for (unsigned i = 0; i < c_returnValuesSize; ++i) stackLayout.push_back(i); - stackLayout += vector<int>(c_localVariablesSize, -1); if (stackLayout.size() > 17) BOOST_THROW_EXCEPTION( @@ -493,39 +471,44 @@ bool ContractCompiler::visit(FunctionDefinition const& _function) m_context << swapInstruction(stackLayout.size() - stackLayout.back() - 1); swap(stackLayout[stackLayout.back()], stackLayout.back()); } - //@todo assert that everything is in place now + for (int i = 0; i < int(stackLayout.size()); ++i) + if (stackLayout[i] != i) + solAssert(false, "Invalid stack layout on cleanup."); for (ASTPointer<VariableDeclaration const> const& variable: _function.parameters() + _function.returnParameters()) m_context.removeVariable(*variable); - for (VariableDeclaration const* localVariable: _function.localVariables()) - m_context.removeVariable(*localVariable); m_context.adjustStackOffset(-(int)c_returnValuesSize); /// The constructor and the fallback function doesn't to jump out. - if (!_function.isConstructor() && !_function.isFallback()) - m_context.appendJump(eth::AssemblyItem::JumpType::OutOfFunction); + if (!_function.isConstructor()) + { + solAssert(m_context.numberOfLocalVariables() == 0, ""); + if (!_function.isFallback()) + m_context.appendJump(eth::AssemblyItem::JumpType::OutOfFunction); + } + return false; } bool ContractCompiler::visit(InlineAssembly const& _inlineAssembly) { unsigned startStackHeight = m_context.stackHeight(); - julia::ExternalIdentifierAccess identifierAccess; - identifierAccess.resolve = [&](assembly::Identifier const& _identifier, julia::IdentifierContext, bool) + yul::ExternalIdentifierAccess identifierAccess; + identifierAccess.resolve = [&](assembly::Identifier const& _identifier, yul::IdentifierContext, bool) { auto ref = _inlineAssembly.annotation().externalReferences.find(&_identifier); if (ref == _inlineAssembly.annotation().externalReferences.end()) return size_t(-1); return ref->second.valueSize; }; - identifierAccess.generateCode = [&](assembly::Identifier const& _identifier, julia::IdentifierContext _context, julia::AbstractAssembly& _assembly) + identifierAccess.generateCode = [&](assembly::Identifier const& _identifier, yul::IdentifierContext _context, yul::AbstractAssembly& _assembly) { auto ref = _inlineAssembly.annotation().externalReferences.find(&_identifier); solAssert(ref != _inlineAssembly.annotation().externalReferences.end(), ""); Declaration const* decl = ref->second.declaration; solAssert(!!decl, ""); - if (_context == julia::IdentifierContext::RValue) + if (_context == yul::IdentifierContext::RValue) { int const depositBefore = _assembly.stackHeight(); solAssert(!!decl->type(), "Type of declaration required but not yet determined."); @@ -666,32 +649,36 @@ bool ContractCompiler::visit(WhileStatement const& _whileStatement) { StackHeightChecker checker(m_context); CompilerContext::LocationSetter locationSetter(m_context, _whileStatement); + eth::AssemblyItem loopStart = m_context.newTag(); eth::AssemblyItem loopEnd = m_context.newTag(); - m_continueTags.push_back(loopStart); - m_breakTags.push_back(loopEnd); + m_breakTags.push_back({loopEnd, m_context.stackHeight()}); m_context << loopStart; - // While loops have the condition prepended - if (!_whileStatement.isDoWhile()) + if (_whileStatement.isDoWhile()) { - compileExpression(_whileStatement.condition()); - m_context << Instruction::ISZERO; - m_context.appendConditionalJumpTo(loopEnd); - } + eth::AssemblyItem condition = m_context.newTag(); + m_continueTags.push_back({condition, m_context.stackHeight()}); - _whileStatement.body().accept(*this); + _whileStatement.body().accept(*this); - // Do-while loops have the condition appended - if (_whileStatement.isDoWhile()) + m_context << condition; + compileExpression(_whileStatement.condition()); + m_context << Instruction::ISZERO << Instruction::ISZERO; + m_context.appendConditionalJumpTo(loopStart); + } + else { + m_continueTags.push_back({loopStart, m_context.stackHeight()}); compileExpression(_whileStatement.condition()); m_context << Instruction::ISZERO; m_context.appendConditionalJumpTo(loopEnd); - } - m_context.appendJumpTo(loopStart); + _whileStatement.body().accept(*this); + + m_context.appendJumpTo(loopStart); + } m_context << loopEnd; m_continueTags.pop_back(); @@ -708,12 +695,14 @@ bool ContractCompiler::visit(ForStatement const& _forStatement) eth::AssemblyItem loopStart = m_context.newTag(); eth::AssemblyItem loopEnd = m_context.newTag(); eth::AssemblyItem loopNext = m_context.newTag(); - m_continueTags.push_back(loopNext); - m_breakTags.push_back(loopEnd); + + storeStackHeight(&_forStatement); if (_forStatement.initializationExpression()) _forStatement.initializationExpression()->accept(*this); + m_breakTags.push_back({loopEnd, m_context.stackHeight()}); + m_continueTags.push_back({loopNext, m_context.stackHeight()}); m_context << loopStart; // if there is no terminating condition in for, default is to always be true @@ -733,11 +722,16 @@ bool ContractCompiler::visit(ForStatement const& _forStatement) _forStatement.loopExpression()->accept(*this); m_context.appendJumpTo(loopStart); + m_context << loopEnd; m_continueTags.pop_back(); m_breakTags.pop_back(); + // For the case where no break/return is executed: + // loop initialization variables have to be freed + popScopedVariables(&_forStatement); + checker.check(); return false; } @@ -745,16 +739,16 @@ bool ContractCompiler::visit(ForStatement const& _forStatement) bool ContractCompiler::visit(Continue const& _continueStatement) { CompilerContext::LocationSetter locationSetter(m_context, _continueStatement); - if (!m_continueTags.empty()) - m_context.appendJumpTo(m_continueTags.back()); + solAssert(!m_continueTags.empty(), ""); + CompilerUtils(m_context).popAndJump(m_continueTags.back().second, m_continueTags.back().first); return false; } bool ContractCompiler::visit(Break const& _breakStatement) { CompilerContext::LocationSetter locationSetter(m_context, _breakStatement); - if (!m_breakTags.empty()) - m_context.appendJumpTo(m_breakTags.back()); + solAssert(!m_breakTags.empty(), ""); + CompilerUtils(m_context).popAndJump(m_breakTags.back().second, m_breakTags.back().first); return false; } @@ -780,18 +774,14 @@ bool ContractCompiler::visit(Return const& _return) for (auto const& retVariable: boost::adaptors::reverse(returnParameters)) CompilerUtils(m_context).moveToStackVariable(*retVariable); } - for (unsigned i = 0; i < m_stackCleanupForReturn; ++i) - m_context << Instruction::POP; - m_context.appendJumpTo(m_returnTags.back()); - m_context.adjustStackOffset(m_stackCleanupForReturn); + + CompilerUtils(m_context).popAndJump(m_returnTags.back().second, m_returnTags.back().first); return false; } -bool ContractCompiler::visit(Throw const& _throw) +bool ContractCompiler::visit(Throw const&) { - CompilerContext::LocationSetter locationSetter(m_context, _throw); - // Do not send back an error detail. - m_context.appendRevert(); + solAssert(false, "Throw statement is disallowed."); return false; } @@ -806,8 +796,15 @@ bool ContractCompiler::visit(EmitStatement const& _emit) bool ContractCompiler::visit(VariableDeclarationStatement const& _variableDeclarationStatement) { - StackHeightChecker checker(m_context); CompilerContext::LocationSetter locationSetter(m_context, _variableDeclarationStatement); + + // Local variable slots are reserved when their declaration is visited, + // and freed in the end of their scope. + for (auto _decl: _variableDeclarationStatement.declarations()) + if (_decl) + appendStackVariableInitialisation(*_decl); + + StackHeightChecker checker(m_context); if (Expression const* expression = _variableDeclarationStatement.initialValue()) { CompilerUtils utils(m_context); @@ -817,20 +814,19 @@ bool ContractCompiler::visit(VariableDeclarationStatement const& _variableDeclar valueTypes = tupleType->components(); else valueTypes = TypePointers{expression->annotation().type}; - auto const& assignments = _variableDeclarationStatement.annotation().assignments; - solAssert(assignments.size() == valueTypes.size(), ""); - for (size_t i = 0; i < assignments.size(); ++i) + auto const& declarations = _variableDeclarationStatement.declarations(); + solAssert(declarations.size() == valueTypes.size(), ""); + for (size_t i = 0; i < declarations.size(); ++i) { - size_t j = assignments.size() - i - 1; + size_t j = declarations.size() - i - 1; solAssert(!!valueTypes[j], ""); - VariableDeclaration const* varDecl = assignments[j]; - if (!varDecl) - utils.popStackElement(*valueTypes[j]); - else + if (VariableDeclaration const* varDecl = declarations[j].get()) { utils.convertType(*valueTypes[j], *varDecl->annotation().type); utils.moveToStackVariable(*varDecl); } + else + utils.popStackElement(*valueTypes[j]); } } checker.check(); @@ -857,6 +853,18 @@ bool ContractCompiler::visit(PlaceholderStatement const& _placeholderStatement) return true; } +bool ContractCompiler::visit(Block const& _block) +{ + storeStackHeight(&_block); + return true; +} + +void ContractCompiler::endVisit(Block const& _block) +{ + // Frees local variables declared in the scope of this block. + popScopedVariables(&_block); +} + void ContractCompiler::appendMissingFunctions() { while (Declaration const* function = m_context.nextFunctionToCompile()) @@ -866,9 +874,9 @@ void ContractCompiler::appendMissingFunctions() solAssert(m_context.nextFunctionToCompile() != function, "Compiled the wrong function?"); } m_context.appendMissingLowLevelFunctions(); - string abiFunctions = m_context.abiFunctions().requestedFunctions(); - if (!abiFunctions.empty()) - m_context.appendInlineAssembly("{" + move(abiFunctions) + "}", {}, true); + auto abiFunctions = m_context.abiFunctions().requestedFunctions(); + if (!abiFunctions.first.empty()) + m_context.appendInlineAssembly("{" + move(abiFunctions.first) + "}", {}, abiFunctions.second, true); } void ContractCompiler::appendModifierOrFunctionCode() @@ -912,27 +920,19 @@ void ContractCompiler::appendModifierOrFunctionCode() modifier.parameters()[i]->annotation().type ); } - for (VariableDeclaration const* localVariable: modifier.localVariables()) - { - addedVariables.push_back(localVariable); - appendStackVariableInitialisation(*localVariable); - } - stackSurplus = - CompilerUtils::sizeOnStack(modifier.parameters()) + - CompilerUtils::sizeOnStack(modifier.localVariables()); + stackSurplus = CompilerUtils::sizeOnStack(modifier.parameters()); codeBlock = &modifier.body(); } } if (codeBlock) { - m_returnTags.push_back(m_context.newTag()); - + m_returnTags.push_back({m_context.newTag(), m_context.stackHeight()}); codeBlock->accept(*this); solAssert(!m_returnTags.empty(), ""); - m_context << m_returnTags.back(); + m_context << m_returnTags.back().first; m_returnTags.pop_back(); CompilerUtils(m_context).popStackSlots(stackSurplus); @@ -957,25 +957,19 @@ void ContractCompiler::compileExpression(Expression const& _expression, TypePoin CompilerUtils(m_context).convertType(*_expression.annotation().type, *_targetType); } -eth::AssemblyPointer ContractCompiler::cloneRuntime() const -{ - eth::Assembly a; - a << Instruction::CALLDATASIZE; - a << u256(0) << Instruction::DUP1 << Instruction::CALLDATACOPY; - //@todo adjust for larger return values, make this dynamic. - a << u256(0x20) << u256(0) << Instruction::CALLDATASIZE; - a << u256(0); - // this is the address which has to be substituted by the linker. - //@todo implement as special "marker" AssemblyItem. - a << u256("0xcafecafecafecafecafecafecafecafecafecafe"); - a << u256(eth::GasCosts::callGas(m_context.evmVersion()) + 10) << Instruction::GAS << Instruction::SUB; - a << Instruction::DELEGATECALL; - //Propagate error condition (if DELEGATECALL pushes 0 on stack). - a << Instruction::ISZERO; - a << Instruction::ISZERO; - eth::AssemblyItem afterTag = a.appendJumpI().tag(); - a << Instruction::INVALID << afterTag; - //@todo adjust for larger return values, make this dynamic. - a << u256(0x20) << u256(0) << Instruction::RETURN; - return make_shared<eth::Assembly>(a); +void ContractCompiler::popScopedVariables(ASTNode const* _node) +{ + unsigned blockHeight = m_scopeStackHeight.at(m_modifierDepth).at(_node); + m_context.removeVariablesAboveStackHeight(blockHeight); + solAssert(m_context.stackHeight() >= blockHeight, ""); + unsigned stackDiff = m_context.stackHeight() - blockHeight; + CompilerUtils(m_context).popStackSlots(stackDiff); + m_scopeStackHeight[m_modifierDepth].erase(_node); + if (m_scopeStackHeight[m_modifierDepth].empty()) + m_scopeStackHeight.erase(m_modifierDepth); +} + +void ContractCompiler::storeStackHeight(ASTNode const* _node) +{ + m_scopeStackHeight[m_modifierDepth][_node] = m_context.stackHeight(); } diff --git a/libsolidity/codegen/ContractCompiler.h b/libsolidity/codegen/ContractCompiler.h index 02a3452f..5fa650b1 100644 --- a/libsolidity/codegen/ContractCompiler.h +++ b/libsolidity/codegen/ContractCompiler.h @@ -56,13 +56,6 @@ public: ContractDefinition const& _contract, std::map<ContractDefinition const*, eth::Assembly const*> const& _contracts ); - /// Compiles a contract that uses DELEGATECALL to call into a pre-deployed version of the given - /// contract at runtime, but contains the full creation-time code. - /// @returns the identifier of the runtime sub-assembly. - size_t compileClone( - ContractDefinition const& _contract, - std::map<ContractDefinition const*, eth::Assembly const*> const& _contracts - ); private: /// Registers the non-function objects inside the contract with the context and stores the basic @@ -109,6 +102,8 @@ private: virtual bool visit(VariableDeclarationStatement const& _variableDeclarationStatement) override; virtual bool visit(ExpressionStatement const& _expressionStatement) override; virtual bool visit(PlaceholderStatement const&) override; + virtual bool visit(Block const& _block) override; + virtual void endVisit(Block const& _block) override; /// Repeatedly visits all function which are referenced but which are not compiled yet. void appendMissingFunctions(); @@ -120,22 +115,31 @@ private: void appendStackVariableInitialisation(VariableDeclaration const& _variable); void compileExpression(Expression const& _expression, TypePointer const& _targetType = TypePointer()); - /// @returns the runtime assembly for clone contracts. - eth::AssemblyPointer cloneRuntime() const; + /// Frees the variables of a certain scope (to be used when leaving). + void popScopedVariables(ASTNode const* _node); + + /// Sets the stack height for the visited loop. + void storeStackHeight(ASTNode const* _node); bool const m_optimise; /// Pointer to the runtime compiler in case this is a creation compiler. ContractCompiler* m_runtimeCompiler = nullptr; CompilerContext& m_context; - std::vector<eth::AssemblyItem> m_breakTags; ///< tag to jump to for a "break" statement - std::vector<eth::AssemblyItem> m_continueTags; ///< tag to jump to for a "continue" statement - /// Tag to jump to for a "return" statement, needs to be stacked because of modifiers. - std::vector<eth::AssemblyItem> m_returnTags; + /// Tag to jump to for a "break" statement and the stack height after freeing the local loop variables. + std::vector<std::pair<eth::AssemblyItem, unsigned>> m_breakTags; + /// Tag to jump to for a "continue" statement and the stack height after freeing the local loop variables. + std::vector<std::pair<eth::AssemblyItem, unsigned>> m_continueTags; + /// Tag to jump to for a "return" statement and the stack height after freeing the local function or modifier variables. + /// Needs to be stacked because of modifiers. + std::vector<std::pair<eth::AssemblyItem, unsigned>> m_returnTags; unsigned m_modifierDepth = 0; FunctionDefinition const* m_currentFunction = nullptr; - unsigned m_stackCleanupForReturn = 0; ///< this number of stack elements need to be removed before jump to m_returnTag + // arguments for base constructors, filled in derived-to-base order std::map<FunctionDefinition const*, ASTNode const*> const* m_baseArguments; + + /// Stores the variables that were declared inside a specific scope, for each modifier depth. + std::map<unsigned, std::map<ASTNode const*, unsigned>> m_scopeStackHeight; }; } diff --git a/libsolidity/codegen/ExpressionCompiler.cpp b/libsolidity/codegen/ExpressionCompiler.cpp index f38c1e67..bdf91fbf 100644 --- a/libsolidity/codegen/ExpressionCompiler.cpp +++ b/libsolidity/codegen/ExpressionCompiler.cpp @@ -25,7 +25,7 @@ #include <boost/range/adaptor/reversed.hpp> #include <boost/algorithm/string/replace.hpp> #include <libdevcore/Common.h> -#include <libdevcore/SHA3.h> +#include <libdevcore/Keccak256.h> #include <libsolidity/ast/AST.h> #include <libsolidity/codegen/ExpressionCompiler.h> #include <libsolidity/codegen/CompilerContext.h> @@ -206,8 +206,8 @@ bool ExpressionCompiler::visit(Conditional const& _condition) bool ExpressionCompiler::visit(Assignment const& _assignment) { CompilerContext::LocationSetter locationSetter(m_context, _assignment); - Token::Value op = _assignment.assignmentOperator(); - Token::Value binOp = op == Token::Assign ? op : Token::AssignmentToBinaryOp(op); + Token op = _assignment.assignmentOperator(); + Token binOp = op == Token::Assign ? op : TokenTraits::AssignmentToBinaryOp(op); Type const& leftType = *_assignment.leftHandSide().annotation().type; if (leftType.category() == Type::Category::Tuple) { @@ -223,7 +223,7 @@ bool ExpressionCompiler::visit(Assignment const& _assignment) // Perform some conversion already. This will convert storage types to memory and literals // to their actual type, but will not convert e.g. memory to storage. TypePointer rightIntermediateType; - if (op != Token::Assign && Token::isShiftOp(binOp)) + if (op != Token::Assign && TokenTraits::isShiftOp(binOp)) rightIntermediateType = _assignment.rightHandSide().annotation().type->mobileType(); else rightIntermediateType = _assignment.rightHandSide().annotation().type->closestTemporaryType( @@ -251,7 +251,7 @@ bool ExpressionCompiler::visit(Assignment const& _assignment) m_currentLValue->retrieveValue(_assignment.location(), true); utils().convertType(leftType, leftType, cleanupNeeded); - if (Token::isShiftOp(binOp)) + if (TokenTraits::isShiftOp(binOp)) appendShiftOperatorCode(binOp, leftType, *rightIntermediateType); else { @@ -281,19 +281,19 @@ bool ExpressionCompiler::visit(TupleExpression const& _tuple) if (_tuple.isInlineArray()) { ArrayType const& arrayType = dynamic_cast<ArrayType const&>(*_tuple.annotation().type); - + solAssert(!arrayType.isDynamicallySized(), "Cannot create dynamically sized inline array."); m_context << max(u256(32u), arrayType.memorySize()); utils().allocateMemory(); m_context << Instruction::DUP1; - + for (auto const& component: _tuple.components()) { component->accept(*this); utils().convertType(*component->annotation().type, *arrayType.baseType(), true); - utils().storeInMemoryDynamic(*arrayType.baseType(), true); + utils().storeInMemoryDynamic(*arrayType.baseType(), true); } - + m_context << Instruction::POP; } else @@ -349,6 +349,10 @@ bool ExpressionCompiler::visit(UnaryOperation const& _unaryOperation) case Token::Inc: // ++ (pre- or postfix) case Token::Dec: // -- (pre- or postfix) solAssert(!!m_currentLValue, "LValue not retrieved."); + solUnimplementedAssert( + _unaryOperation.annotation().type->category() != Type::Category::FixedPoint, + "Not yet implemented - FixedPointType." + ); m_currentLValue->retrieveValue(_unaryOperation.location()); if (!_unaryOperation.isPrefixOperation()) { @@ -380,7 +384,7 @@ bool ExpressionCompiler::visit(UnaryOperation const& _unaryOperation) m_context << u256(0) << Instruction::SUB; break; default: - solAssert(false, "Invalid unary operator: " + string(Token::toString(_unaryOperation.getOperator()))); + solAssert(false, "Invalid unary operator: " + string(TokenTraits::toString(_unaryOperation.getOperator()))); } return false; } @@ -392,7 +396,7 @@ bool ExpressionCompiler::visit(BinaryOperation const& _binaryOperation) Expression const& rightExpression = _binaryOperation.rightExpression(); solAssert(!!_binaryOperation.annotation().commonType, ""); TypePointer const& commonType = _binaryOperation.annotation().commonType; - Token::Value const c_op = _binaryOperation.getOperator(); + Token const c_op = _binaryOperation.getOperator(); if (c_op == Token::And || c_op == Token::Or) // special case: short-circuiting appendAndOrOperatorCode(_binaryOperation); @@ -403,7 +407,7 @@ bool ExpressionCompiler::visit(BinaryOperation const& _binaryOperation) bool cleanupNeeded = cleanupNeededForOp(commonType->category(), c_op); TypePointer leftTargetType = commonType; - TypePointer rightTargetType = Token::isShiftOp(c_op) ? rightExpression.annotation().type->mobileType() : commonType; + TypePointer rightTargetType = TokenTraits::isShiftOp(c_op) ? rightExpression.annotation().type->mobileType() : commonType; solAssert(rightTargetType, ""); // for commutative operators, push the literal as late as possible to allow improved optimization @@ -411,7 +415,7 @@ bool ExpressionCompiler::visit(BinaryOperation const& _binaryOperation) { return dynamic_cast<Literal const*>(&_e) || _e.annotation().type->category() == Type::Category::RationalNumber; }; - bool swap = m_optimize && Token::isCommutativeOp(c_op) && isLiteral(rightExpression) && !isLiteral(leftExpression); + bool swap = m_optimize && TokenTraits::isCommutativeOp(c_op) && isLiteral(rightExpression) && !isLiteral(leftExpression); if (swap) { leftExpression.accept(*this); @@ -426,10 +430,10 @@ bool ExpressionCompiler::visit(BinaryOperation const& _binaryOperation) leftExpression.accept(*this); utils().convertType(*leftExpression.annotation().type, *leftTargetType, cleanupNeeded); } - if (Token::isShiftOp(c_op)) + if (TokenTraits::isShiftOp(c_op)) // shift only cares about the signedness of both sides appendShiftOperatorCode(c_op, *leftTargetType, *rightTargetType); - else if (Token::isCompareOp(c_op)) + else if (TokenTraits::isCompareOp(c_op)) appendCompareOperatorCode(c_op, *commonType); else appendOrdinaryBinaryOperatorCode(c_op, *commonType); @@ -562,14 +566,15 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) break; } case FunctionType::Kind::External: - case FunctionType::Kind::CallCode: case FunctionType::Kind::DelegateCall: case FunctionType::Kind::BareCall: - case FunctionType::Kind::BareCallCode: case FunctionType::Kind::BareDelegateCall: + case FunctionType::Kind::BareStaticCall: _functionCall.expression().accept(*this); appendExternalFunctionCall(function, arguments); break; + case FunctionType::Kind::BareCallCode: + solAssert(false, "Callcode has been removed."); case FunctionType::Kind::Creation: { _functionCall.expression().accept(*this); @@ -697,18 +702,26 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) m_context.appendRevert(); break; } - case FunctionType::Kind::SHA3: + case FunctionType::Kind::KECCAK256: { - TypePointers argumentTypes; - for (auto const& arg: arguments) + solAssert(arguments.size() == 1, ""); + solAssert(!function.padArguments(), ""); + TypePointer const& argType = arguments.front()->annotation().type; + solAssert(argType, ""); + arguments.front()->accept(*this); + // Optimization: If type is bytes or string, then do not encode, + // but directly compute keccak256 on memory. + if (*argType == ArrayType::bytesMemory() || *argType == ArrayType::stringMemory()) { - arg->accept(*this); - argumentTypes.push_back(arg->annotation().type); + ArrayUtils(m_context).retrieveLength(ArrayType::bytesMemory()); + m_context << Instruction::SWAP1 << u256(0x20) << Instruction::ADD; + } + else + { + utils().fetchFreeMemoryPointer(); + utils().packedEncode({argType}, TypePointers()); + utils().toSizeAfterFreeMemoryPointer(); } - utils().fetchFreeMemoryPointer(); - solAssert(!function.padArguments(), ""); - utils().packedEncode(argumentTypes, TypePointers()); - utils().toSizeAfterFreeMemoryPointer(); m_context << Instruction::KECCAK256; break; } @@ -866,6 +879,19 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) StorageByteArrayElement(m_context).storeValue(*type, _functionCall.location(), true); break; } + case FunctionType::Kind::ArrayPop: + { + _functionCall.expression().accept(*this); + solAssert(function.parameterTypes().empty(), ""); + + ArrayType const& arrayType = dynamic_cast<ArrayType const&>( + *dynamic_cast<MemberAccess const&>(_functionCall.expression()).expression().annotation().type + ); + solAssert(arrayType.dataStoredIn(DataLocation::Storage), ""); + + ArrayUtils(m_context).popStorageArrayElement(arrayType); + break; + } case FunctionType::Kind::ObjectCreation: { ArrayType const& arrayType = dynamic_cast<ArrayType const&>(*_functionCall.annotation().type); @@ -1045,11 +1071,34 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) // stack now: <memory pointer> break; } + case FunctionType::Kind::ABIDecode: + { + arguments.front()->accept(*this); + TypePointer firstArgType = arguments.front()->annotation().type; + TypePointers targetTypes; + if (TupleType const* targetTupleType = dynamic_cast<TupleType const*>(_functionCall.annotation().type.get())) + targetTypes = targetTupleType->components(); + else + targetTypes = TypePointers{_functionCall.annotation().type}; + if ( + *firstArgType == ArrayType(DataLocation::CallData) || + *firstArgType == ArrayType(DataLocation::CallData, true) + ) + utils().abiDecode(targetTypes, false); + else + { + utils().convertType(*firstArgType, ArrayType::bytesMemory()); + m_context << Instruction::DUP1 << u256(32) << Instruction::ADD; + m_context << Instruction::SWAP1 << Instruction::MLOAD; + // stack now: <mem_pos> <length> + + utils().abiDecode(targetTypes, true); + } + break; + } case FunctionType::Kind::GasLeft: m_context << Instruction::GAS; break; - default: - solAssert(false, "Invalid function type."); } } return false; @@ -1118,18 +1167,18 @@ bool ExpressionCompiler::visit(MemberAccess const& _memberAccess) solAssert(false, "event not found"); // no-op, because the parent node will do the job break; + case FunctionType::Kind::DelegateCall: + _memberAccess.expression().accept(*this); + m_context << funType->externalIdentifier(); + break; case FunctionType::Kind::External: case FunctionType::Kind::Creation: - case FunctionType::Kind::DelegateCall: - case FunctionType::Kind::CallCode: case FunctionType::Kind::Send: case FunctionType::Kind::BareCall: case FunctionType::Kind::BareCallCode: case FunctionType::Kind::BareDelegateCall: + case FunctionType::Kind::BareStaticCall: case FunctionType::Kind::Transfer: - _memberAccess.expression().accept(*this); - m_context << funType->externalIdentifier(); - break; case FunctionType::Kind::Log0: case FunctionType::Kind::Log1: case FunctionType::Kind::Log2: @@ -1180,7 +1229,7 @@ bool ExpressionCompiler::visit(MemberAccess const& _memberAccess) else solAssert(false, "Contract member is neither variable nor function."); m_context << identifier; - /// need to store store it as bytes4 + /// need to store it as bytes4 utils().leftShiftNumberOnStack(224); return false; } @@ -1189,70 +1238,73 @@ bool ExpressionCompiler::visit(MemberAccess const& _memberAccess) switch (_memberAccess.expression().annotation().type->category()) { case Type::Category::Contract: - case Type::Category::Integer: { - bool alsoSearchInteger = false; - if (_memberAccess.expression().annotation().type->category() == Type::Category::Contract) + ContractType const& type = dynamic_cast<ContractType const&>(*_memberAccess.expression().annotation().type); + if (type.isSuper()) { - ContractType const& type = dynamic_cast<ContractType const&>(*_memberAccess.expression().annotation().type); - if (type.isSuper()) - { - solAssert(!!_memberAccess.annotation().referencedDeclaration, "Referenced declaration not resolved."); - utils().pushCombinedFunctionEntryLabel(m_context.superFunction( - dynamic_cast<FunctionDefinition const&>(*_memberAccess.annotation().referencedDeclaration), - type.contractDefinition() - )); - } + solAssert(!!_memberAccess.annotation().referencedDeclaration, "Referenced declaration not resolved."); + utils().pushCombinedFunctionEntryLabel(m_context.superFunction( + dynamic_cast<FunctionDefinition const&>(*_memberAccess.annotation().referencedDeclaration), + type.contractDefinition() + )); + } + // ordinary contract type + else if (Declaration const* declaration = _memberAccess.annotation().referencedDeclaration) + { + u256 identifier; + if (auto const* variable = dynamic_cast<VariableDeclaration const*>(declaration)) + identifier = FunctionType(*variable).externalIdentifier(); + else if (auto const* function = dynamic_cast<FunctionDefinition const*>(declaration)) + identifier = FunctionType(*function).externalIdentifier(); else - { - // ordinary contract type - if (Declaration const* declaration = _memberAccess.annotation().referencedDeclaration) - { - u256 identifier; - if (auto const* variable = dynamic_cast<VariableDeclaration const*>(declaration)) - identifier = FunctionType(*variable).externalIdentifier(); - else if (auto const* function = dynamic_cast<FunctionDefinition const*>(declaration)) - identifier = FunctionType(*function).externalIdentifier(); - else - solAssert(false, "Contract member is neither variable nor function."); - utils().convertType(type, IntegerType(160, IntegerType::Modifier::Address), true); - m_context << identifier; - } - else - // not found in contract, search in members inherited from address - alsoSearchInteger = true; - } + solAssert(false, "Contract member is neither variable nor function."); + utils().convertType(type, type.isPayable() ? AddressType::addressPayable() : AddressType::address(), true); + m_context << identifier; } else - alsoSearchInteger = true; - - if (alsoSearchInteger) + solAssert(false, "Invalid member access in contract"); + break; + } + case Type::Category::Integer: + { + solAssert(false, "Invalid member access to integer"); + break; + } + case Type::Category::Address: + { + if (member == "balance") { - if (member == "balance") - { - utils().convertType( - *_memberAccess.expression().annotation().type, - IntegerType(160, IntegerType::Modifier::Address), - true - ); - m_context << Instruction::BALANCE; - } - else if ((set<string>{"send", "transfer", "call", "callcode", "delegatecall"}).count(member)) - utils().convertType( - *_memberAccess.expression().annotation().type, - IntegerType(160, IntegerType::Modifier::Address), - true - ); - else - solAssert(false, "Invalid member access to integer"); + utils().convertType( + *_memberAccess.expression().annotation().type, + AddressType::address(), + true + ); + m_context << Instruction::BALANCE; + } + else if ((set<string>{"send", "transfer"}).count(member)) + { + solAssert(dynamic_cast<AddressType const&>(*_memberAccess.expression().annotation().type).stateMutability() == StateMutability::Payable, ""); + utils().convertType( + *_memberAccess.expression().annotation().type, + AddressType(StateMutability::Payable), + true + ); } + else if ((set<string>{"call", "callcode", "delegatecall", "staticcall"}).count(member)) + utils().convertType( + *_memberAccess.expression().annotation().type, + AddressType::address(), + true + ); + else + solAssert(false, "Invalid member access to address"); break; } case Type::Category::Function: if (member == "selector") { m_context << Instruction::SWAP1 << Instruction::POP; - /// need to store store it as bytes4 + /// need to store it as bytes4 utils().leftShiftNumberOnStack(224); } else @@ -1277,8 +1329,6 @@ bool ExpressionCompiler::visit(MemberAccess const& _memberAccess) m_context << Instruction::CALLVALUE; else if (member == "origin") m_context << Instruction::ORIGIN; - else if (member == "gas") - m_context << Instruction::GAS; else if (member == "gasprice") m_context << Instruction::GASPRICE; else if (member == "data") @@ -1286,9 +1336,10 @@ bool ExpressionCompiler::visit(MemberAccess const& _memberAccess) else if (member == "sig") m_context << u256(0) << Instruction::CALLDATALOAD << (u256(0xffffffff) << (256 - 32)) << Instruction::AND; + else if (member == "gas") + solAssert(false, "Gas has been removed."); else if (member == "blockhash") - { - } + solAssert(false, "Blockhash has been removed."); else solAssert(false, "Unknown magic member."); break; @@ -1345,11 +1396,13 @@ bool ExpressionCompiler::visit(MemberAccess const& _memberAccess) break; } } - else if (member == "push") + else if (member == "push" || member == "pop") { solAssert( - type.isDynamicallySized() && type.location() == DataLocation::Storage, - "Tried to use .push() on a non-dynamically sized array" + type.isDynamicallySized() && + type.location() == DataLocation::Storage && + type.category() == Type::Category::Array, + "Tried to use ." + member + "() on a non-dynamically sized array" ); } else @@ -1532,12 +1585,12 @@ void ExpressionCompiler::endVisit(Literal const& _literal) { CompilerContext::LocationSetter locationSetter(m_context, _literal); TypePointer type = _literal.annotation().type; - + switch (type->category()) { case Type::Category::RationalNumber: case Type::Category::Bool: - case Type::Category::Integer: + case Type::Category::Address: m_context << type->literalValue(&_literal); break; case Type::Category::StringLiteral: @@ -1549,7 +1602,7 @@ void ExpressionCompiler::endVisit(Literal const& _literal) void ExpressionCompiler::appendAndOrOperatorCode(BinaryOperation const& _binaryOperation) { - Token::Value const c_op = _binaryOperation.getOperator(); + Token const c_op = _binaryOperation.getOperator(); solAssert(c_op == Token::Or || c_op == Token::And, ""); _binaryOperation.leftExpression().accept(*this); @@ -1562,7 +1615,7 @@ void ExpressionCompiler::appendAndOrOperatorCode(BinaryOperation const& _binaryO m_context << endLabel; } -void ExpressionCompiler::appendCompareOperatorCode(Token::Value _operator, Type const& _type) +void ExpressionCompiler::appendCompareOperatorCode(Token _operator, Type const& _type) { solAssert(_type.sizeOnStack() == 1, "Comparison of multi-slot types."); if (_operator == Token::Equal || _operator == Token::NotEqual) @@ -1612,24 +1665,24 @@ void ExpressionCompiler::appendCompareOperatorCode(Token::Value _operator, Type } } -void ExpressionCompiler::appendOrdinaryBinaryOperatorCode(Token::Value _operator, Type const& _type) +void ExpressionCompiler::appendOrdinaryBinaryOperatorCode(Token _operator, Type const& _type) { - if (Token::isArithmeticOp(_operator)) + if (TokenTraits::isArithmeticOp(_operator)) appendArithmeticOperatorCode(_operator, _type); - else if (Token::isBitOp(_operator)) + else if (TokenTraits::isBitOp(_operator)) appendBitOperatorCode(_operator); else solAssert(false, "Unknown binary operator."); } -void ExpressionCompiler::appendArithmeticOperatorCode(Token::Value _operator, Type const& _type) +void ExpressionCompiler::appendArithmeticOperatorCode(Token _operator, Type const& _type) { - IntegerType const& type = dynamic_cast<IntegerType const&>(_type); - bool const c_isSigned = type.isSigned(); - if (_type.category() == Type::Category::FixedPoint) solUnimplemented("Not yet implemented - FixedPointType."); + IntegerType const& type = dynamic_cast<IntegerType const&>(_type); + bool const c_isSigned = type.isSigned(); + switch (_operator) { case Token::Add: @@ -1662,7 +1715,7 @@ void ExpressionCompiler::appendArithmeticOperatorCode(Token::Value _operator, Ty } } -void ExpressionCompiler::appendBitOperatorCode(Token::Value _operator) +void ExpressionCompiler::appendBitOperatorCode(Token _operator) { switch (_operator) { @@ -1680,7 +1733,7 @@ void ExpressionCompiler::appendBitOperatorCode(Token::Value _operator) } } -void ExpressionCompiler::appendShiftOperatorCode(Token::Value _operator, Type const& _valueType, Type const& _shiftAmountType) +void ExpressionCompiler::appendShiftOperatorCode(Token _operator, Type const& _valueType, Type const& _shiftAmountType) { // stack: shift_amount value_to_shift @@ -1722,11 +1775,36 @@ void ExpressionCompiler::appendShiftOperatorCode(Token::Value _operator, Type co m_context << u256(2) << Instruction::EXP << Instruction::MUL; break; case Token::SAR: - // NOTE: SAR rounds differently than SDIV - if (m_context.evmVersion().hasBitwiseShifting() && !c_valueSigned) - m_context << Instruction::SHR; + if (m_context.evmVersion().hasBitwiseShifting()) + m_context << (c_valueSigned ? Instruction::SAR : Instruction::SHR); else - m_context << u256(2) << Instruction::EXP << Instruction::SWAP1 << (c_valueSigned ? Instruction::SDIV : Instruction::DIV); + { + if (c_valueSigned) + // In the following assembly snippet, xor_mask will be zero, if value_to_shift is positive. + // Therefore xor'ing with xor_mask is the identity and the computation reduces to + // div(value_to_shift, exp(2, shift_amount)), which is correct, since for positive values + // arithmetic right shift is dividing by a power of two (which, as a bitwise operation, results + // in discarding bits on the right and filling with zeros from the left). + // For negative values arithmetic right shift, viewed as a bitwise operation, discards bits to the + // right and fills in ones from the left. This is achieved as follows: + // If value_to_shift is negative, then xor_mask will have all bits set, so xor'ing with xor_mask + // will flip all bits. First all bits in value_to_shift are flipped. As for the positive case, + // dividing by a power of two using integer arithmetic results in discarding bits to the right + // and filling with zeros from the left. Flipping all bits in the result again, turns all zeros + // on the left to ones and restores the non-discarded, shifted bits to their original value (they + // have now been flipped twice). In summary we now have discarded bits to the right and filled with + // ones from the left, i.e. we have performed an arithmetic right shift. + m_context.appendInlineAssembly(R"({ + let xor_mask := sub(0, slt(value_to_shift, 0)) + value_to_shift := xor(div(xor(value_to_shift, xor_mask), exp(2, shift_amount)), xor_mask) + })", {"value_to_shift", "shift_amount"}); + else + m_context.appendInlineAssembly(R"({ + value_to_shift := div(value_to_shift, exp(2, shift_amount)) + })", {"value_to_shift", "shift_amount"}); + m_context << Instruction::POP; + + } break; case Token::SHR: default: @@ -1763,71 +1841,47 @@ void ExpressionCompiler::appendExternalFunctionCall( utils().moveToStackTop(gasValueSize, _functionType.selfType()->sizeOnStack()); auto funKind = _functionType.kind(); - bool returnSuccessCondition = funKind == FunctionType::Kind::BareCall || funKind == FunctionType::Kind::BareCallCode || funKind == FunctionType::Kind::BareDelegateCall; - bool isCallCode = funKind == FunctionType::Kind::BareCallCode || funKind == FunctionType::Kind::CallCode; + + solAssert(funKind != FunctionType::Kind::BareStaticCall || m_context.evmVersion().hasStaticCall(), ""); + + solAssert(funKind != FunctionType::Kind::BareCallCode, "Callcode has been removed."); + + bool returnSuccessConditionAndReturndata = funKind == FunctionType::Kind::BareCall || funKind == FunctionType::Kind::BareDelegateCall || funKind == FunctionType::Kind::BareStaticCall; bool isDelegateCall = funKind == FunctionType::Kind::BareDelegateCall || funKind == FunctionType::Kind::DelegateCall; - bool useStaticCall = - _functionType.stateMutability() <= StateMutability::View && - m_context.experimentalFeatureActive(ExperimentalFeature::V050) && - m_context.evmVersion().hasStaticCall(); + bool useStaticCall = funKind == FunctionType::Kind::BareStaticCall || (_functionType.stateMutability() <= StateMutability::View && m_context.evmVersion().hasStaticCall()); bool haveReturndatacopy = m_context.evmVersion().supportsReturndata(); unsigned retSize = 0; - TypePointers returnTypes; - if (returnSuccessCondition) - retSize = 0; // return value actually is success condition - else if (haveReturndatacopy) - returnTypes = _functionType.returnParameterTypes(); - else - returnTypes = _functionType.returnParameterTypesWithoutDynamicTypes(); - bool dynamicReturnSize = false; - for (auto const& retType: returnTypes) - if (retType->isDynamicallyEncoded()) - { - solAssert(haveReturndatacopy, ""); - dynamicReturnSize = true; - retSize = 0; - break; - } + TypePointers returnTypes; + if (!returnSuccessConditionAndReturndata) + { + if (haveReturndatacopy) + returnTypes = _functionType.returnParameterTypes(); else - retSize += retType->calldataEncodedSize(); + returnTypes = _functionType.returnParameterTypesWithoutDynamicTypes(); + + for (auto const& retType: returnTypes) + if (retType->isDynamicallyEncoded()) + { + solAssert(haveReturndatacopy, ""); + dynamicReturnSize = true; + retSize = 0; + break; + } + else + retSize += retType->calldataEncodedSize(); + } // Evaluate arguments. TypePointers argumentTypes; TypePointers parameterTypes = _functionType.parameterTypes(); - bool manualFunctionId = false; - if ( - (funKind == FunctionType::Kind::BareCall || funKind == FunctionType::Kind::BareCallCode || funKind == FunctionType::Kind::BareDelegateCall) && - !_arguments.empty() - ) - { - solAssert(_arguments.front()->annotation().type->mobileType(), ""); - manualFunctionId = - _arguments.front()->annotation().type->mobileType()->calldataEncodedSize(false) == - CompilerUtils::dataStartOffset; - } - if (manualFunctionId) - { - // If we have a Bare* and the first type has exactly 4 bytes, use it as - // function identifier. - _arguments.front()->accept(*this); - utils().convertType( - *_arguments.front()->annotation().type, - IntegerType(8 * CompilerUtils::dataStartOffset), - true - ); - for (unsigned i = 0; i < gasValueSize; ++i) - m_context << swapInstruction(gasValueSize - i); - gasStackPos++; - valueStackPos++; - } if (_functionType.bound()) { argumentTypes.push_back(_functionType.selfType()); parameterTypes.insert(parameterTypes.begin(), _functionType.selfType()); } - for (size_t i = manualFunctionId ? 1 : 0; i < _arguments.size(); ++i) + for (size_t i = 0; i < _arguments.size(); ++i) { _arguments[i]->accept(*this); argumentTypes.push_back(_arguments[i]->annotation().type); @@ -1856,27 +1910,28 @@ void ExpressionCompiler::appendExternalFunctionCall( { m_context << u256(0); utils().fetchFreeMemoryPointer(); - // This touches too much, but that way we save some rounding arithmetics + // This touches too much, but that way we save some rounding arithmetic m_context << u256(retSize) << Instruction::ADD << Instruction::MSTORE; } } // Copy function identifier to memory. utils().fetchFreeMemoryPointer(); - if (!_functionType.isBareCall() || manualFunctionId) + if (!_functionType.isBareCall()) { m_context << dupInstruction(2 + gasValueSize + CompilerUtils::sizeOnStack(argumentTypes)); utils().storeInMemoryDynamic(IntegerType(8 * CompilerUtils::dataStartOffset), false); } - // If the function takes arbitrary parameters, copy dynamic length data in place. + + // If the function takes arbitrary parameters or is a bare call, copy dynamic length data in place. // Move arguments to memory, will not update the free memory pointer (but will update the memory // pointer on the stack). utils().encodeToMemory( argumentTypes, parameterTypes, _functionType.padArguments(), - _functionType.takesArbitraryParameters(), - isCallCode || isDelegateCall + _functionType.takesArbitraryParameters() || _functionType.isBareCall(), + isDelegateCall ); // Stack now: @@ -1919,8 +1974,8 @@ void ExpressionCompiler::appendExternalFunctionCall( m_context << dupInstruction(m_context.baseToCurrentStackOffset(contractStackPos)); bool existenceChecked = false; - // Check the the target contract exists (has code) for non-low-level calls. - if (funKind == FunctionType::Kind::External || funKind == FunctionType::Kind::CallCode || funKind == FunctionType::Kind::DelegateCall) + // Check the target contract exists (has code) for non-low-level calls. + if (funKind == FunctionType::Kind::External || funKind == FunctionType::Kind::DelegateCall) { m_context << Instruction::DUP1 << Instruction::EXTCODESIZE << Instruction::ISZERO; // TODO: error message? @@ -1947,8 +2002,6 @@ void ExpressionCompiler::appendExternalFunctionCall( // Order is important here, STATICCALL might overlap with DELEGATECALL. if (isDelegateCall) m_context << Instruction::DELEGATECALL; - else if (isCallCode) - m_context << Instruction::CALLCODE; else if (useStaticCall) m_context << Instruction::STATICCALL; else @@ -1956,11 +2009,11 @@ void ExpressionCompiler::appendExternalFunctionCall( unsigned remainsSize = 2 + // contract address, input_memory_end - _functionType.valueSet() + - _functionType.gasSet() + - (!_functionType.isBareCall() || manualFunctionId); + (_functionType.valueSet() ? 1 : 0) + + (_functionType.gasSet() ? 1 : 0) + + (!_functionType.isBareCall() ? 1 : 0); - if (returnSuccessCondition) + if (returnSuccessConditionAndReturndata) m_context << swapInstruction(remainsSize); else { @@ -1971,9 +2024,31 @@ void ExpressionCompiler::appendExternalFunctionCall( utils().popStackSlots(remainsSize); - if (returnSuccessCondition) + if (returnSuccessConditionAndReturndata) { - // already there + // success condition is already there + // The return parameter types can be empty, when this function is used as + // an internal helper function e.g. for ``send`` and ``transfer``. In that + // case we're only interested in the success condition, not the return data. + if (!_functionType.returnParameterTypes().empty()) + { + if (haveReturndatacopy) + { + m_context << Instruction::RETURNDATASIZE; + m_context.appendInlineAssembly(R"({ + switch v case 0 { + v := 0x60 + } default { + v := mload(0x40) + mstore(0x40, add(v, and(add(returndatasize(), 0x3f), not(0x1f)))) + mstore(v, returndatasize()) + returndatacopy(add(v, 0x20), 0, returndatasize()) + } + })", {"v"}); + } + else + utils().pushZeroPointer(); + } } else if (funKind == FunctionType::Kind::RIPEMD160) { @@ -2025,7 +2100,7 @@ void ExpressionCompiler::appendExternalFunctionCall( mstore(0x40, newMem) })", {"start", "size"}); - utils().abiDecode(returnTypes, true, true); + utils().abiDecode(returnTypes, true); } } @@ -2065,9 +2140,9 @@ void ExpressionCompiler::setLValueToStorageItem(Expression const& _expression) setLValue<StorageItem>(_expression, *_expression.annotation().type); } -bool ExpressionCompiler::cleanupNeededForOp(Type::Category _type, Token::Value _op) +bool ExpressionCompiler::cleanupNeededForOp(Type::Category _type, Token _op) { - if (Token::isCompareOp(_op) || Token::isShiftOp(_op)) + if (TokenTraits::isCompareOp(_op) || TokenTraits::isShiftOp(_op)) return true; else if (_type == Type::Category::Integer && (_op == Token::Div || _op == Token::Mod || _op == Token::Exp)) // We need cleanup for EXP because 0**0 == 1, but 0**0x100 == 0 diff --git a/libsolidity/codegen/ExpressionCompiler.h b/libsolidity/codegen/ExpressionCompiler.h index cdfa096e..3d8e8682 100644 --- a/libsolidity/codegen/ExpressionCompiler.h +++ b/libsolidity/codegen/ExpressionCompiler.h @@ -86,12 +86,12 @@ private: ///@{ ///@name Append code for various operator types void appendAndOrOperatorCode(BinaryOperation const& _binaryOperation); - void appendCompareOperatorCode(Token::Value _operator, Type const& _type); - void appendOrdinaryBinaryOperatorCode(Token::Value _operator, Type const& _type); + void appendCompareOperatorCode(Token _operator, Type const& _type); + void appendOrdinaryBinaryOperatorCode(Token _operator, Type const& _type); - void appendArithmeticOperatorCode(Token::Value _operator, Type const& _type); - void appendBitOperatorCode(Token::Value _operator); - void appendShiftOperatorCode(Token::Value _operator, Type const& _valueType, Type const& _shiftAmountType); + void appendArithmeticOperatorCode(Token _operator, Type const& _type); + void appendBitOperatorCode(Token _operator); + void appendShiftOperatorCode(Token _operator, Type const& _valueType, Type const& _shiftAmountType); /// @} /// Appends code to call a function of the given type with the given arguments. @@ -119,7 +119,7 @@ private: /// @returns true if the operator applied to the given type requires a cleanup prior to the /// operation. - static bool cleanupNeededForOp(Type::Category _type, Token::Value _op); + static bool cleanupNeededForOp(Type::Category _type, Token _op); /// @returns the CompilerUtils object containing the current context. CompilerUtils utils(); diff --git a/libsolidity/codegen/LValue.cpp b/libsolidity/codegen/LValue.cpp index 77684683..790ab309 100644 --- a/libsolidity/codegen/LValue.cpp +++ b/libsolidity/codegen/LValue.cpp @@ -134,8 +134,7 @@ void MemoryItem::storeValue(Type const& _sourceType, SourceLocation const&, bool void MemoryItem::setToZero(SourceLocation const&, bool _removeReference) const { CompilerUtils utils(m_context); - if (!_removeReference) - m_context << Instruction::DUP1; + solAssert(_removeReference, ""); utils.pushZeroValue(*m_dataType); utils.storeInMemoryDynamic(*m_dataType, m_padded); m_context << Instruction::POP; @@ -460,8 +459,7 @@ void StorageByteArrayElement::storeValue(Type const&, SourceLocation const&, boo void StorageByteArrayElement::setToZero(SourceLocation const&, bool _removeReference) const { // stack: ref byte_number - if (!_removeReference) - m_context << Instruction::DUP2 << Instruction::DUP2; + solAssert(_removeReference, ""); m_context << u256(31) << Instruction::SUB << u256(0x100) << Instruction::EXP; // stack: ref (1<<(8*(31-byte_number))) m_context << Instruction::DUP2 << Instruction::SLOAD; @@ -498,8 +496,7 @@ void StorageArrayLength::storeValue(Type const&, SourceLocation const&, bool _mo void StorageArrayLength::setToZero(SourceLocation const&, bool _removeReference) const { - if (!_removeReference) - m_context << Instruction::DUP1; + solAssert(_removeReference, ""); ArrayUtils(m_context).clearDynamicArray(m_arrayType); } @@ -521,24 +518,9 @@ unsigned TupleObject::sizeOnStack() const return size; } -void TupleObject::retrieveValue(SourceLocation const& _location, bool _remove) const +void TupleObject::retrieveValue(SourceLocation const&, bool) const { - unsigned initialDepth = sizeOnStack(); - unsigned initialStack = m_context.stackHeight(); - for (auto const& lv: m_lvalues) - if (lv) - { - solAssert(initialDepth + m_context.stackHeight() >= initialStack, ""); - unsigned depth = initialDepth + m_context.stackHeight() - initialStack; - if (lv->sizeOnStack() > 0) - { - if (_remove && depth > lv->sizeOnStack()) - CompilerUtils(m_context).moveToStackTop(depth, depth - lv->sizeOnStack()); - else if (!_remove && depth > 0) - CompilerUtils(m_context).copyToStackTop(depth, lv->sizeOnStack()); - } - lv->retrieveValue(_location, true); - } + solAssert(false, "Tried to retrieve value of tuple."); } void TupleObject::storeValue(Type const& _sourceType, SourceLocation const& _location, bool) const @@ -569,24 +551,7 @@ void TupleObject::storeValue(Type const& _sourceType, SourceLocation const& _loc CompilerUtils(m_context).popStackElement(_sourceType); } -void TupleObject::setToZero(SourceLocation const& _location, bool _removeReference) const +void TupleObject::setToZero(SourceLocation const&, bool) const { - if (_removeReference) - { - for (size_t i = 0; i < m_lvalues.size(); ++i) - if (m_lvalues[m_lvalues.size() - i]) - m_lvalues[m_lvalues.size() - i]->setToZero(_location, true); - } - else - { - unsigned depth = sizeOnStack(); - for (auto const& val: m_lvalues) - if (val) - { - if (val->sizeOnStack() > 0) - CompilerUtils(m_context).copyToStackTop(depth, val->sizeOnStack()); - val->setToZero(_location, false); - depth -= val->sizeOnStack(); - } - } + solAssert(false, "Tried to delete tuple."); } diff --git a/libsolidity/formal/CVC4Interface.cpp b/libsolidity/formal/CVC4Interface.cpp index dba5823a..6cb91483 100644 --- a/libsolidity/formal/CVC4Interface.cpp +++ b/libsolidity/formal/CVC4Interface.cpp @@ -37,6 +37,7 @@ void CVC4Interface::reset() m_functions.clear(); m_solver.reset(); m_solver.setOption("produce-models", true); + m_solver.setTimeLimit(queryTimeout); } void CVC4Interface::push() @@ -49,23 +50,25 @@ void CVC4Interface::pop() m_solver.pop(); } -Expression CVC4Interface::newFunction(string _name, Sort _domain, Sort _codomain) +void CVC4Interface::declareFunction(string _name, Sort _domain, Sort _codomain) { - CVC4::Type fType = m_context.mkFunctionType(cvc4Sort(_domain), cvc4Sort(_codomain)); - m_functions.insert({_name, m_context.mkVar(_name.c_str(), fType)}); - return SolverInterface::newFunction(move(_name), _domain, _codomain); + if (!m_functions.count(_name)) + { + CVC4::Type fType = m_context.mkFunctionType(cvc4Sort(_domain), cvc4Sort(_codomain)); + m_functions.insert({_name, m_context.mkVar(_name.c_str(), fType)}); + } } -Expression CVC4Interface::newInteger(string _name) +void CVC4Interface::declareInteger(string _name) { - m_constants.insert({_name, m_context.mkVar(_name.c_str(), m_context.integerType())}); - return SolverInterface::newInteger(move(_name)); + if (!m_constants.count(_name)) + m_constants.insert({_name, m_context.mkVar(_name.c_str(), m_context.integerType())}); } -Expression CVC4Interface::newBool(string _name) +void CVC4Interface::declareBool(string _name) { - m_constants.insert({_name, m_context.mkVar(_name.c_str(), m_context.booleanType())}); - return SolverInterface::newBool(std::move(_name)); + if (!m_constants.count(_name)) + m_constants.insert({_name, m_context.mkVar(_name.c_str(), m_context.booleanType())}); } void CVC4Interface::addAssertion(Expression const& _expr) @@ -109,13 +112,13 @@ pair<CheckResult, vector<string>> CVC4Interface::check(vector<Expression> const& solAssert(false, ""); } - if (result != CheckResult::UNSATISFIABLE && !_expressionsToEvaluate.empty()) + if (result == CheckResult::SATISFIABLE && !_expressionsToEvaluate.empty()) { for (Expression const& e: _expressionsToEvaluate) values.push_back(toString(m_solver.getValue(toCVC4Expr(e)))); } } - catch (CVC4::Exception & e) + catch (CVC4::Exception const&) { result = CheckResult::ERROR; values.clear(); diff --git a/libsolidity/formal/CVC4Interface.h b/libsolidity/formal/CVC4Interface.h index cfaeb412..cd6d761d 100644 --- a/libsolidity/formal/CVC4Interface.h +++ b/libsolidity/formal/CVC4Interface.h @@ -21,8 +21,19 @@ #include <boost/noncopyable.hpp> +#if defined(__GLIBC__) +// The CVC4 headers includes the deprecated system headers <ext/hash_map> +// and <ext/hash_set>. These headers cause a warning that will break the +// build, unless _GLIBCXX_PERMIT_BACKWARD_HASH is set. +#define _GLIBCXX_PERMIT_BACKWARD_HASH +#endif + #include <cvc4/cvc4.h> +#if defined(__GLIBC__) +#undef _GLIBCXX_PERMIT_BACKWARD_HASH +#endif + namespace dev { namespace solidity @@ -40,9 +51,9 @@ public: void push() override; void pop() override; - Expression newFunction(std::string _name, Sort _domain, Sort _codomain) override; - Expression newInteger(std::string _name) override; - Expression newBool(std::string _name) override; + void declareFunction(std::string _name, Sort _domain, Sort _codomain) override; + void declareInteger(std::string _name) override; + void declareBool(std::string _name) override; void addAssertion(Expression const& _expr) override; std::pair<CheckResult, std::vector<std::string>> check(std::vector<Expression> const& _expressionsToEvaluate) override; diff --git a/libsolidity/formal/SMTChecker.cpp b/libsolidity/formal/SMTChecker.cpp index 425c5c1e..cc580021 100644 --- a/libsolidity/formal/SMTChecker.cpp +++ b/libsolidity/formal/SMTChecker.cpp @@ -17,17 +17,10 @@ #include <libsolidity/formal/SMTChecker.h> -#ifdef HAVE_Z3 -#include <libsolidity/formal/Z3Interface.h> -#elif HAVE_CVC4 -#include <libsolidity/formal/CVC4Interface.h> -#else -#include <libsolidity/formal/SMTLib2Interface.h> -#endif - -#include <libsolidity/formal/SSAVariable.h> -#include <libsolidity/formal/SymbolicIntVariable.h> +#include <libsolidity/formal/SMTPortfolio.h> + #include <libsolidity/formal/VariableUsage.h> +#include <libsolidity/formal/SymbolicTypes.h> #include <libsolidity/interface/ErrorReporter.h> @@ -39,21 +32,15 @@ using namespace dev; using namespace dev::solidity; SMTChecker::SMTChecker(ErrorReporter& _errorReporter, ReadCallback::Callback const& _readFileCallback): -#ifdef HAVE_Z3 - m_interface(make_shared<smt::Z3Interface>()), -#elif HAVE_CVC4 - m_interface(make_shared<smt::CVC4Interface>()), -#else - m_interface(make_shared<smt::SMTLib2Interface>(_readFileCallback)), -#endif + m_interface(make_shared<smt::SMTPortfolio>(_readFileCallback)), m_errorReporter(_errorReporter) { - (void)_readFileCallback; } -void SMTChecker::analyze(SourceUnit const& _source) +void SMTChecker::analyze(SourceUnit const& _source, shared_ptr<Scanner> const& _scanner) { m_variableUsage = make_shared<VariableUsage>(_source); + m_scanner = _scanner; if (_source.annotation().experimentalFeatures.count(ExperimentalFeature::SMTChecker)) _source.accept(*this); } @@ -68,7 +55,7 @@ bool SMTChecker::visit(ContractDefinition const& _contract) void SMTChecker::endVisit(ContractDefinition const&) { - m_stateVariables.clear(); + m_variables.clear(); } void SMTChecker::endVisit(VariableDeclaration const& _varDecl) @@ -84,45 +71,54 @@ bool SMTChecker::visit(FunctionDefinition const& _function) _function.location(), "Assertion checker does not yet support constructors and functions with modifiers." ); - m_currentFunction = &_function; - m_interface->reset(); - m_variables.clear(); - m_variables.insert(m_stateVariables.begin(), m_stateVariables.end()); - m_pathConditions.clear(); + m_functionPath.push_back(&_function); + // Not visited by a function call + if (isRootFunction()) + { + m_interface->reset(); + m_pathConditions.clear(); + m_expressions.clear(); + resetStateVariables(); + initializeLocalVariables(_function); + } + m_loopExecutionHappened = false; - initializeLocalVariables(_function); - resetStateVariables(); return true; } void SMTChecker::endVisit(FunctionDefinition const&) { - // TOOD we could check for "reachability", i.e. satisfiability here. - // We only handle local variables, so we clear at the beginning of the function. - // If we add storage variables, those should be cleared differently. - m_currentFunction = nullptr; + // If _function was visited from a function call we don't remove + // the local variables just yet, since we might need them for + // future calls. + // Otherwise we remove any local variables from the context and + // keep the state variables. + if (isRootFunction()) + removeLocalVariables(); + m_functionPath.pop_back(); } bool SMTChecker::visit(IfStatement const& _node) { _node.condition().accept(*this); - checkBooleanNotConstant(_node.condition(), "Condition is always $VALUE."); + // We ignore called functions here because they have + // specific input values. + if (isRootFunction()) + checkBooleanNotConstant(_node.condition(), "Condition is always $VALUE."); - auto countersEndTrue = visitBranch(_node.trueStatement(), expr(_node.condition())); - vector<Declaration const*> touchedVariables = m_variableUsage->touchedVariables(_node.trueStatement()); - decltype(countersEndTrue) countersEndFalse; + auto indicesEndTrue = visitBranch(_node.trueStatement(), expr(_node.condition())); + vector<VariableDeclaration const*> touchedVariables = m_variableUsage->touchedVariables(_node.trueStatement()); + decltype(indicesEndTrue) indicesEndFalse; if (_node.falseStatement()) { - countersEndFalse = visitBranch(*_node.falseStatement(), !expr(_node.condition())); + indicesEndFalse = visitBranch(*_node.falseStatement(), !expr(_node.condition())); touchedVariables += m_variableUsage->touchedVariables(*_node.falseStatement()); } else - { - countersEndFalse = m_variables; - } + indicesEndFalse = copyVariableIndices(); - mergeVariables(touchedVariables, expr(_node.condition()), countersEndTrue, countersEndFalse); + mergeVariables(touchedVariables, expr(_node.condition()), indicesEndTrue, indicesEndFalse); return false; } @@ -136,12 +132,14 @@ bool SMTChecker::visit(WhileStatement const& _node) visitBranch(_node.body()); // TODO the assertions generated in the body should still be active in the condition _node.condition().accept(*this); - checkBooleanNotConstant(_node.condition(), "Do-while loop condition is always $VALUE."); + if (isRootFunction()) + checkBooleanNotConstant(_node.condition(), "Do-while loop condition is always $VALUE."); } else { _node.condition().accept(*this); - checkBooleanNotConstant(_node.condition(), "While loop condition is always $VALUE."); + if (isRootFunction()) + checkBooleanNotConstant(_node.condition(), "While loop condition is always $VALUE."); visitBranch(_node.body(), expr(_node.condition())); } @@ -172,10 +170,10 @@ bool SMTChecker::visit(ForStatement const& _node) if (_node.condition()) { _node.condition()->accept(*this); - checkBooleanNotConstant(*_node.condition(), "For loop condition is always $VALUE."); + if (isRootFunction()) + checkBooleanNotConstant(*_node.condition(), "For loop condition is always $VALUE."); } - VariableSequenceCounters sequenceCountersStart = m_variables; m_interface->push(); if (_node.condition()) m_interface->addAssertion(expr(*_node.condition())); @@ -186,7 +184,6 @@ bool SMTChecker::visit(ForStatement const& _node) m_interface->pop(); m_loopExecutionHappened = true; - std::swap(sequenceCountersStart, m_variables); resetVariables(touchedVariables); @@ -212,28 +209,24 @@ void SMTChecker::endVisit(VariableDeclarationStatement const& _varDecl) ); } -void SMTChecker::endVisit(ExpressionStatement const&) -{ -} - void SMTChecker::endVisit(Assignment const& _assignment) { - if (_assignment.assignmentOperator() != Token::Value::Assign) + if (_assignment.assignmentOperator() != Token::Assign) m_errorReporter.warning( _assignment.location(), "Assertion checker does not yet implement compound assignment." ); - else if (!SSAVariable::isSupportedType(_assignment.annotation().type->category())) + else if (!isSupportedType(_assignment.annotation().type->category())) m_errorReporter.warning( _assignment.location(), "Assertion checker does not yet implement type " + _assignment.annotation().type->toString() ); else if (Identifier const* identifier = dynamic_cast<Identifier const*>(&_assignment.leftHandSide())) { - Declaration const* decl = identifier->annotation().referencedDeclaration; - if (knownVariable(*decl)) + VariableDeclaration const& decl = dynamic_cast<VariableDeclaration const&>(*identifier->annotation().referencedDeclaration); + if (knownVariable(decl)) { - assignment(*decl, _assignment.rightHandSide(), _assignment.location()); + assignment(decl, _assignment.rightHandSide(), _assignment.location()); defineExpr(_assignment, expr(_assignment.rightHandSide())); } else @@ -254,7 +247,7 @@ void SMTChecker::endVisit(TupleExpression const& _tuple) if (_tuple.isInlineArray() || _tuple.components().size() != 1) m_errorReporter.warning( _tuple.location(), - "Assertion checker does not yet implement tules and inline arrays." + "Assertion checker does not yet implement tuples and inline arrays." ); else defineExpr(_tuple, expr(*_tuple.components()[0])); @@ -263,17 +256,17 @@ void SMTChecker::endVisit(TupleExpression const& _tuple) void SMTChecker::checkUnderOverflow(smt::Expression _value, IntegerType const& _type, SourceLocation const& _location) { checkCondition( - _value < SymbolicIntVariable::minValue(_type), + _value < minValue(_type), _location, "Underflow (resulting value less than " + formatNumber(_type.minValue()) + ")", - "value", + "<result>", &_value ); checkCondition( - _value > SymbolicIntVariable::maxValue(_type), + _value > maxValue(_type), _location, "Overflow (resulting value larger than " + formatNumber(_type.maxValue()) + ")", - "value", + "<result>", &_value ); } @@ -284,7 +277,7 @@ void SMTChecker::endVisit(UnaryOperation const& _op) { case Token::Not: // ! { - solAssert(SSAVariable::isBool(_op.annotation().type->category()), ""); + solAssert(isBool(_op.annotation().type->category()), ""); defineExpr(_op, !expr(_op.subExpression())); break; } @@ -292,16 +285,16 @@ void SMTChecker::endVisit(UnaryOperation const& _op) case Token::Dec: // -- (pre- or postfix) { - solAssert(SSAVariable::isInteger(_op.annotation().type->category()), ""); + solAssert(isInteger(_op.annotation().type->category()), ""); solAssert(_op.subExpression().annotation().lValueRequested, ""); if (Identifier const* identifier = dynamic_cast<Identifier const*>(&_op.subExpression())) { - Declaration const* decl = identifier->annotation().referencedDeclaration; - if (knownVariable(*decl)) + VariableDeclaration const& decl = dynamic_cast<VariableDeclaration const&>(*identifier->annotation().referencedDeclaration); + if (knownVariable(decl)) { - auto innerValue = currentValue(*decl); + auto innerValue = currentValue(decl); auto newValue = _op.getOperator() == Token::Inc ? innerValue + 1 : innerValue - 1; - assignment(*decl, newValue, _op.location()); + assignment(decl, newValue, _op.location()); defineExpr(_op, _op.isPrefixOperation() ? newValue : innerValue); } else @@ -337,11 +330,11 @@ void SMTChecker::endVisit(UnaryOperation const& _op) void SMTChecker::endVisit(BinaryOperation const& _op) { - if (Token::isArithmeticOp(_op.getOperator())) + if (TokenTraits::isArithmeticOp(_op.getOperator())) arithmeticOperation(_op); - else if (Token::isCompareOp(_op.getOperator())) + else if (TokenTraits::isCompareOp(_op.getOperator())) compareOperation(_op); - else if (Token::isBooleanOp(_op.getOperator())) + else if (TokenTraits::isBooleanOp(_op.getOperator())) booleanOperation(_op); else m_errorReporter.warning( @@ -366,49 +359,162 @@ void SMTChecker::endVisit(FunctionCall const& _funCall) std::vector<ASTPointer<Expression const>> const args = _funCall.arguments(); if (funType.kind() == FunctionType::Kind::Assert) - { - solAssert(args.size() == 1, ""); - solAssert(args[0]->annotation().type->category() == Type::Category::Bool, ""); - checkCondition(!(expr(*args[0])), _funCall.location(), "Assertion violation"); - addPathImpliedExpression(expr(*args[0])); - } + visitAssert(_funCall); else if (funType.kind() == FunctionType::Kind::Require) + visitRequire(_funCall); + else if (funType.kind() == FunctionType::Kind::GasLeft) + visitGasLeft(_funCall); + else if (funType.kind() == FunctionType::Kind::BlockHash) + visitBlockHash(_funCall); + else if (funType.kind() == FunctionType::Kind::Internal) + inlineFunctionCall(_funCall); + else { - solAssert(args.size() == 1, ""); - solAssert(args[0]->annotation().type->category() == Type::Category::Bool, ""); + m_errorReporter.warning( + _funCall.location(), + "Assertion checker does not yet implement this type of function call." + ); + } +} + +void SMTChecker::visitAssert(FunctionCall const& _funCall) +{ + auto const& args = _funCall.arguments(); + solAssert(args.size() == 1, ""); + solAssert(args[0]->annotation().type->category() == Type::Category::Bool, ""); + checkCondition(!(expr(*args[0])), _funCall.location(), "Assertion violation"); + addPathImpliedExpression(expr(*args[0])); +} + +void SMTChecker::visitRequire(FunctionCall const& _funCall) +{ + auto const& args = _funCall.arguments(); + solAssert(args.size() == 1, ""); + solAssert(args[0]->annotation().type->category() == Type::Category::Bool, ""); + if (isRootFunction()) checkBooleanNotConstant(*args[0], "Condition is always $VALUE."); - addPathImpliedExpression(expr(*args[0])); + addPathImpliedExpression(expr(*args[0])); +} + +void SMTChecker::visitGasLeft(FunctionCall const& _funCall) +{ + string gasLeft = "gasleft()"; + // We increase the variable index since gasleft changes + // inside a tx. + defineSpecialVariable(gasLeft, _funCall, true); + auto const& symbolicVar = m_specialVariables.at(gasLeft); + unsigned index = symbolicVar->index(); + // We set the current value to unknown anyway to add type constraints. + symbolicVar->setUnknownValue(); + if (index > 0) + m_interface->addAssertion(symbolicVar->currentValue() <= symbolicVar->valueAtIndex(index - 1)); +} + +void SMTChecker::visitBlockHash(FunctionCall const& _funCall) +{ + string blockHash = "blockhash()"; + // TODO Define blockhash as an uninterpreted function + defineSpecialVariable(blockHash, _funCall); +} + +void SMTChecker::inlineFunctionCall(FunctionCall const& _funCall) +{ + FunctionDefinition const* _funDef = nullptr; + Expression const* _calledExpr = &_funCall.expression(); + + if (TupleExpression const* _fun = dynamic_cast<TupleExpression const*>(&_funCall.expression())) + { + solAssert(_fun->components().size() == 1, ""); + _calledExpr = _fun->components().at(0).get(); + } + + if (Identifier const* _fun = dynamic_cast<Identifier const*>(_calledExpr)) + _funDef = dynamic_cast<FunctionDefinition const*>(_fun->annotation().referencedDeclaration); + else if (MemberAccess const* _fun = dynamic_cast<MemberAccess const*>(_calledExpr)) + _funDef = dynamic_cast<FunctionDefinition const*>(_fun->annotation().referencedDeclaration); + else + { + m_errorReporter.warning( + _funCall.location(), + "Assertion checker does not yet implement this type of function call." + ); + return; + } + solAssert(_funDef, ""); + + if (visitedFunction(_funDef)) + m_errorReporter.warning( + _funCall.location(), + "Assertion checker does not support recursive function calls.", + SecondarySourceLocation().append("Starting from function:", _funDef->location()) + ); + else if (_funDef && _funDef->isImplemented()) + { + vector<smt::Expression> funArgs; + for (auto arg: _funCall.arguments()) + funArgs.push_back(expr(*arg)); + initializeFunctionCallParameters(*_funDef, funArgs); + _funDef->accept(*this); + auto const& returnParams = _funDef->returnParameters(); + if (_funDef->returnParameters().size()) + { + if (returnParams.size() > 1) + m_errorReporter.warning( + _funCall.location(), + "Assertion checker does not yet support calls to functions that return more than one value." + ); + else + defineExpr(_funCall, currentValue(*returnParams[0])); + } + } + else + { + m_errorReporter.warning( + _funCall.location(), + "Assertion checker does not support calls to functions without implementation." + ); } } void SMTChecker::endVisit(Identifier const& _identifier) { - Declaration const* decl = _identifier.annotation().referencedDeclaration; - solAssert(decl, ""); if (_identifier.annotation().lValueRequested) { // Will be translated as part of the node that requested the lvalue. } - else if (SSAVariable::isSupportedType(_identifier.annotation().type->category())) - defineExpr(_identifier, currentValue(*decl)); else if (FunctionType const* fun = dynamic_cast<FunctionType const*>(_identifier.annotation().type.get())) { - if (fun->kind() == FunctionType::Kind::Assert || fun->kind() == FunctionType::Kind::Require) + if ( + fun->kind() == FunctionType::Kind::Assert || + fun->kind() == FunctionType::Kind::Require || + fun->kind() == FunctionType::Kind::GasLeft || + fun->kind() == FunctionType::Kind::BlockHash + ) return; + createExpr(_identifier); + } + else if (isSupportedType(_identifier.annotation().type->category())) + { + if (VariableDeclaration const* decl = dynamic_cast<VariableDeclaration const*>(_identifier.annotation().referencedDeclaration)) + defineExpr(_identifier, currentValue(*decl)); + else if (_identifier.name() == "now") + defineSpecialVariable(_identifier.name(), _identifier); + else + // TODO: handle MagicVariableDeclaration here + m_errorReporter.warning( + _identifier.location(), + "Assertion checker does not yet support the type of this variable." + ); } } void SMTChecker::endVisit(Literal const& _literal) { Type const& type = *_literal.annotation().type; - if (type.category() == Type::Category::Integer || type.category() == Type::Category::RationalNumber) - { - if (RationalNumberType const* rational = dynamic_cast<RationalNumberType const*>(&type)) - solAssert(!rational->isFractional(), ""); + if (isNumber(type.category())) defineExpr(_literal, smt::Expression(type.literalValue(&_literal))); - } - else if (type.category() == Type::Category::Bool) + else if (isBool(type.category())) defineExpr(_literal, smt::Expression(_literal.token() == Token::TrueLiteral ? true : false)); else m_errorReporter.warning( @@ -419,6 +525,69 @@ void SMTChecker::endVisit(Literal const& _literal) ); } +void SMTChecker::endVisit(Return const& _return) +{ + if (knownExpr(*_return.expression())) + { + auto returnParams = m_functionPath.back()->returnParameters(); + if (returnParams.size() > 1) + m_errorReporter.warning( + _return.location(), + "Assertion checker does not yet support more than one return value." + ); + else if (returnParams.size() == 1) + m_interface->addAssertion(expr(*_return.expression()) == newValue(*returnParams[0])); + } +} + +bool SMTChecker::visit(MemberAccess const& _memberAccess) +{ + auto const& exprType = _memberAccess.expression().annotation().type; + solAssert(exprType, ""); + if (exprType->category() == Type::Category::Magic) + { + auto identifier = dynamic_cast<Identifier const*>(&_memberAccess.expression()); + string accessedName; + if (identifier) + accessedName = identifier->name(); + else + m_errorReporter.warning( + _memberAccess.location(), + "Assertion checker does not yet support this expression." + ); + defineSpecialVariable(accessedName + "." + _memberAccess.memberName(), _memberAccess); + return false; + } + else + m_errorReporter.warning( + _memberAccess.location(), + "Assertion checker does not yet support this expression." + ); + + return true; +} + +void SMTChecker::defineSpecialVariable(string const& _name, Expression const& _expr, bool _increaseIndex) +{ + if (!knownSpecialVariable(_name)) + { + auto result = newSymbolicVariable(*_expr.annotation().type, _name, *m_interface); + m_specialVariables.emplace(_name, result.second); + result.second->setUnknownValue(); + if (result.first) + m_errorReporter.warning( + _expr.location(), + "Assertion checker does not yet support this special variable." + ); + } + else if (_increaseIndex) + m_specialVariables.at(_name)->increaseIndex(); + // The default behavior is not to increase the index since + // most of the special values stay the same throughout a tx. + defineExpr(_expr, m_specialVariables.at(_name)->currentValue()); +} + + void SMTChecker::arithmeticOperation(BinaryOperation const& _op) { switch (_op.getOperator()) @@ -429,11 +598,18 @@ void SMTChecker::arithmeticOperation(BinaryOperation const& _op) case Token::Div: { solAssert(_op.annotation().commonType, ""); - solAssert(_op.annotation().commonType->category() == Type::Category::Integer, ""); + if (_op.annotation().commonType->category() != Type::Category::Integer) + { + m_errorReporter.warning( + _op.location(), + "Assertion checker does not yet implement this operator on non-integer types." + ); + break; + } auto const& intType = dynamic_cast<IntegerType const&>(*_op.annotation().commonType); smt::Expression left(expr(_op.leftExpression())); smt::Expression right(expr(_op.rightExpression())); - Token::Value op = _op.getOperator(); + Token op = _op.getOperator(); smt::Expression value( op == Token::Add ? left + right : op == Token::Sub ? left - right : @@ -443,7 +619,7 @@ void SMTChecker::arithmeticOperation(BinaryOperation const& _op) if (_op.getOperator() == Token::Div) { - checkCondition(right == 0, _op.location(), "Division by zero", "value", &right); + checkCondition(right == 0, _op.location(), "Division by zero", "<result>", &right); m_interface->addAssertion(right != 0); } @@ -463,13 +639,13 @@ void SMTChecker::arithmeticOperation(BinaryOperation const& _op) void SMTChecker::compareOperation(BinaryOperation const& _op) { solAssert(_op.annotation().commonType, ""); - if (SSAVariable::isSupportedType(_op.annotation().commonType->category())) + if (isSupportedType(_op.annotation().commonType->category())) { smt::Expression left(expr(_op.leftExpression())); smt::Expression right(expr(_op.rightExpression())); - Token::Value op = _op.getOperator(); + Token op = _op.getOperator(); shared_ptr<smt::Expression> value; - if (SSAVariable::isInteger(_op.annotation().commonType->category())) + if (isNumber(_op.annotation().commonType->category())) { value = make_shared<smt::Expression>( op == Token::Equal ? (left == right) : @@ -482,14 +658,10 @@ void SMTChecker::compareOperation(BinaryOperation const& _op) } else // Bool { - solUnimplementedAssert(SSAVariable::isBool(_op.annotation().commonType->category()), "Operation not yet supported"); + solUnimplementedAssert(isBool(_op.annotation().commonType->category()), "Operation not yet supported"); value = make_shared<smt::Expression>( op == Token::Equal ? (left == right) : - op == Token::NotEqual ? (left != right) : - op == Token::LessThan ? (!left && right) : - op == Token::LessThanOrEqual ? (!left || right) : - op == Token::GreaterThan ? (left && !right) : - /*op == Token::GreaterThanOrEqual*/ (left || !right) + /*op == Token::NotEqual*/ (left != right) ); } // TODO: check that other values for op are not possible. @@ -534,37 +706,37 @@ smt::Expression SMTChecker::division(smt::Expression _left, smt::Expression _rig return _left / _right; } -void SMTChecker::assignment(Declaration const& _variable, Expression const& _value, SourceLocation const& _location) +void SMTChecker::assignment(VariableDeclaration const& _variable, Expression const& _value, SourceLocation const& _location) { assignment(_variable, expr(_value), _location); } -void SMTChecker::assignment(Declaration const& _variable, smt::Expression const& _value, SourceLocation const& _location) +void SMTChecker::assignment(VariableDeclaration const& _variable, smt::Expression const& _value, SourceLocation const& _location) { TypePointer type = _variable.type(); if (auto const* intType = dynamic_cast<IntegerType const*>(type.get())) checkUnderOverflow(_value, *intType, _location); + else if (dynamic_cast<AddressType const*>(type.get())) + checkUnderOverflow(_value, IntegerType(160), _location); m_interface->addAssertion(newValue(_variable) == _value); } -SMTChecker::VariableSequenceCounters SMTChecker::visitBranch(Statement const& _statement, smt::Expression _condition) +SMTChecker::VariableIndices SMTChecker::visitBranch(Statement const& _statement, smt::Expression _condition) { return visitBranch(_statement, &_condition); } -SMTChecker::VariableSequenceCounters SMTChecker::visitBranch(Statement const& _statement, smt::Expression const* _condition) +SMTChecker::VariableIndices SMTChecker::visitBranch(Statement const& _statement, smt::Expression const* _condition) { - VariableSequenceCounters beforeVars = m_variables; - + auto indicesBeforeBranch = copyVariableIndices(); if (_condition) pushPathCondition(*_condition); _statement.accept(*this); if (_condition) popPathCondition(); - - std::swap(m_variables, beforeVars); - - return beforeVars; + auto indicesAfterBranch = copyVariableIndices(); + resetVariableIndices(indicesBeforeBranch); + return indicesAfterBranch; } void SMTChecker::checkCondition( @@ -580,31 +752,24 @@ void SMTChecker::checkCondition( vector<smt::Expression> expressionsToEvaluate; vector<string> expressionNames; - if (m_currentFunction) + if (m_functionPath.size()) { + solAssert(m_scanner, ""); if (_additionalValue) { expressionsToEvaluate.emplace_back(*_additionalValue); expressionNames.push_back(_additionalValueName); } - for (auto const& param: m_currentFunction->parameters()) - if (knownVariable(*param)) - { - expressionsToEvaluate.emplace_back(currentValue(*param)); - expressionNames.push_back(param->name()); - } - for (auto const& var: m_currentFunction->localVariables()) - if (knownVariable(*var)) - { - expressionsToEvaluate.emplace_back(currentValue(*var)); - expressionNames.push_back(var->name()); - } - for (auto const& var: m_stateVariables) - if (knownVariable(*var.first)) - { - expressionsToEvaluate.emplace_back(currentValue(*var.first)); - expressionNames.push_back(var.first->name()); - } + for (auto const& var: m_variables) + { + expressionsToEvaluate.emplace_back(currentValue(*var.first)); + expressionNames.push_back(var.first->name()); + } + for (auto const& var: m_specialVariables) + { + expressionsToEvaluate.emplace_back(var.second->currentValue()); + expressionNames.push_back(var.first); + } } smt::CheckResult result; vector<string> values; @@ -621,17 +786,25 @@ void SMTChecker::checkCondition( { std::ostringstream message; message << _description << " happens here"; - if (m_currentFunction) + if (m_functionPath.size()) { - message << " for:\n"; + std::ostringstream modelMessage; + modelMessage << " for:\n"; solAssert(values.size() == expressionNames.size(), ""); + map<string, string> sortedModel; for (size_t i = 0; i < values.size(); ++i) if (expressionsToEvaluate.at(i).name != values.at(i)) - message << " " << expressionNames.at(i) << " = " << values.at(i) << "\n"; + sortedModel[expressionNames.at(i)] = values.at(i); + + for (auto const& eval: sortedModel) + modelMessage << " " << eval.first << " = " << eval.second << "\n"; + m_errorReporter.warning(_location, message.str() + loopComment, SecondarySourceLocation().append(modelMessage.str(), SourceLocation())); } else + { message << "."; - m_errorReporter.warning(_location, message.str() + loopComment); + m_errorReporter.warning(_location, message.str() + loopComment); + } break; } case smt::CheckResult::UNSATISFIABLE: @@ -639,11 +812,12 @@ void SMTChecker::checkCondition( case smt::CheckResult::UNKNOWN: m_errorReporter.warning(_location, _description + " might happen here." + loopComment); break; + case smt::CheckResult::CONFLICTING: + m_errorReporter.warning(_location, "At least two SMT solvers provided conflicting answers. Results might not be sound."); + break; case smt::CheckResult::ERROR: m_errorReporter.warning(_location, "Error trying to invoke SMT solver."); break; - default: - solAssert(false, ""); } m_interface->pop(); } @@ -666,6 +840,8 @@ void SMTChecker::checkBooleanNotConstant(Expression const& _condition, string co if (positiveResult == smt::CheckResult::ERROR || negatedResult == smt::CheckResult::ERROR) m_errorReporter.warning(_condition.location(), "Error trying to invoke SMT solver."); + else if (positiveResult == smt::CheckResult::CONFLICTING || negatedResult == smt::CheckResult::CONFLICTING) + m_errorReporter.warning(_condition.location(), "At least two SMT solvers provided conflicting answers. Results might not be sound."); else if (positiveResult == smt::CheckResult::SATISFIABLE && negatedResult == smt::CheckResult::SATISFIABLE) { // everything fine. @@ -726,6 +902,30 @@ smt::CheckResult SMTChecker::checkSatisfiable() return checkSatisfiableAndGenerateModel({}).first; } +void SMTChecker::initializeFunctionCallParameters(FunctionDefinition const& _function, vector<smt::Expression> const& _callArgs) +{ + auto const& funParams = _function.parameters(); + solAssert(funParams.size() == _callArgs.size(), ""); + for (unsigned i = 0; i < funParams.size(); ++i) + if (createVariable(*funParams[i])) + m_interface->addAssertion(_callArgs[i] == newValue(*funParams[i])); + + for (auto const& variable: _function.localVariables()) + if (createVariable(*variable)) + { + newValue(*variable); + setZeroValue(*variable); + } + + if (_function.returnParameterList()) + for (auto const& retParam: _function.returnParameters()) + if (createVariable(*retParam)) + { + newValue(*retParam); + setZeroValue(*retParam); + } +} + void SMTChecker::initializeLocalVariables(FunctionDefinition const& _function) { for (auto const& variable: _function.localVariables()) @@ -742,16 +942,30 @@ void SMTChecker::initializeLocalVariables(FunctionDefinition const& _function) setZeroValue(*retParam); } +void SMTChecker::removeLocalVariables() +{ + for (auto it = m_variables.begin(); it != m_variables.end(); ) + { + if (it->first->isLocalVariable()) + it = m_variables.erase(it); + else + ++it; + } +} + void SMTChecker::resetStateVariables() { - for (auto const& variable: m_stateVariables) + for (auto const& variable: m_variables) { - newValue(*variable.first); - setUnknownValue(*variable.first); + if (variable.first->isStateVariable()) + { + newValue(*variable.first); + setUnknownValue(*variable.first); + } } } -void SMTChecker::resetVariables(vector<Declaration const*> _variables) +void SMTChecker::resetVariables(vector<VariableDeclaration const*> _variables) { for (auto const* decl: _variables) { @@ -760,38 +974,33 @@ void SMTChecker::resetVariables(vector<Declaration const*> _variables) } } -void SMTChecker::mergeVariables(vector<Declaration const*> const& _variables, smt::Expression const& _condition, VariableSequenceCounters const& _countersEndTrue, VariableSequenceCounters const& _countersEndFalse) +void SMTChecker::mergeVariables(vector<VariableDeclaration const*> const& _variables, smt::Expression const& _condition, VariableIndices const& _indicesEndTrue, VariableIndices const& _indicesEndFalse) { - set<Declaration const*> uniqueVars(_variables.begin(), _variables.end()); + set<VariableDeclaration const*> uniqueVars(_variables.begin(), _variables.end()); for (auto const* decl: uniqueVars) { - int trueCounter = _countersEndTrue.at(decl).index(); - int falseCounter = _countersEndFalse.at(decl).index(); - solAssert(trueCounter != falseCounter, ""); + solAssert(_indicesEndTrue.count(decl) && _indicesEndFalse.count(decl), ""); + int trueIndex = _indicesEndTrue.at(decl); + int falseIndex = _indicesEndFalse.at(decl); + solAssert(trueIndex != falseIndex, ""); m_interface->addAssertion(newValue(*decl) == smt::Expression::ite( _condition, - valueAtSequence(*decl, trueCounter), - valueAtSequence(*decl, falseCounter)) + valueAtIndex(*decl, trueIndex), + valueAtIndex(*decl, falseIndex)) ); } } bool SMTChecker::createVariable(VariableDeclaration const& _varDecl) { - if (SSAVariable::isSupportedType(_varDecl.type()->category())) - { - solAssert(m_variables.count(&_varDecl) == 0, ""); - solAssert(m_stateVariables.count(&_varDecl) == 0, ""); - if (_varDecl.isLocalVariable()) - m_variables.emplace(&_varDecl, SSAVariable(_varDecl, *m_interface)); - else - { - solAssert(_varDecl.isStateVariable(), ""); - m_stateVariables.emplace(&_varDecl, SSAVariable(_varDecl, *m_interface)); - } + // This might be the case for multiple calls to the same function. + if (knownVariable(_varDecl)) return true; - } - else + auto const& type = _varDecl.type(); + solAssert(m_variables.count(&_varDecl) == 0, ""); + auto result = newSymbolicVariable(*type, _varDecl.name() + "_" + to_string(_varDecl.id()), *m_interface); + m_variables.emplace(&_varDecl, result.second); + if (result.first) { m_errorReporter.warning( _varDecl.location(), @@ -799,84 +1008,78 @@ bool SMTChecker::createVariable(VariableDeclaration const& _varDecl) ); return false; } + return true; } -string SMTChecker::uniqueSymbol(Expression const& _expr) -{ - return "expr_" + to_string(_expr.id()); -} - -bool SMTChecker::knownVariable(Declaration const& _decl) +bool SMTChecker::knownVariable(VariableDeclaration const& _decl) { return m_variables.count(&_decl); } -smt::Expression SMTChecker::currentValue(Declaration const& _decl) +smt::Expression SMTChecker::currentValue(VariableDeclaration const& _decl) { solAssert(knownVariable(_decl), ""); - return m_variables.at(&_decl)(); + return m_variables.at(&_decl)->currentValue(); } -smt::Expression SMTChecker::valueAtSequence(Declaration const& _decl, int _sequence) +smt::Expression SMTChecker::valueAtIndex(VariableDeclaration const& _decl, int _index) { solAssert(knownVariable(_decl), ""); - return m_variables.at(&_decl)(_sequence); + return m_variables.at(&_decl)->valueAtIndex(_index); } -smt::Expression SMTChecker::newValue(Declaration const& _decl) +smt::Expression SMTChecker::newValue(VariableDeclaration const& _decl) { solAssert(knownVariable(_decl), ""); - ++m_variables.at(&_decl); - return m_variables.at(&_decl)(); + return m_variables.at(&_decl)->increaseIndex(); } -void SMTChecker::setZeroValue(Declaration const& _decl) +void SMTChecker::setZeroValue(VariableDeclaration const& _decl) { solAssert(knownVariable(_decl), ""); - m_variables.at(&_decl).setZeroValue(); + m_variables.at(&_decl)->setZeroValue(); } -void SMTChecker::setUnknownValue(Declaration const& _decl) +void SMTChecker::setUnknownValue(VariableDeclaration const& _decl) { solAssert(knownVariable(_decl), ""); - m_variables.at(&_decl).setUnknownValue(); + m_variables.at(&_decl)->setUnknownValue(); } smt::Expression SMTChecker::expr(Expression const& _e) { - if (!m_expressions.count(&_e)) + if (!knownExpr(_e)) { m_errorReporter.warning(_e.location(), "Internal error: Expression undefined for SMT solver." ); createExpr(_e); } - return m_expressions.at(&_e); + return m_expressions.at(&_e)->currentValue(); +} + +bool SMTChecker::knownExpr(Expression const& _e) const +{ + return m_expressions.count(&_e); +} + +bool SMTChecker::knownSpecialVariable(string const& _var) const +{ + return m_specialVariables.count(_var); } void SMTChecker::createExpr(Expression const& _e) { - if (m_expressions.count(&_e)) - m_errorReporter.warning(_e.location(), "Internal error: Expression created twice in SMT solver." ); + solAssert(_e.annotation().type, ""); + if (knownExpr(_e)) + m_expressions.at(&_e)->increaseIndex(); else { - solAssert(_e.annotation().type, ""); - switch (_e.annotation().type->category()) - { - case Type::Category::RationalNumber: - { - if (RationalNumberType const* rational = dynamic_cast<RationalNumberType const*>(_e.annotation().type.get())) - solAssert(!rational->isFractional(), ""); - m_expressions.emplace(&_e, m_interface->newInteger(uniqueSymbol(_e))); - break; - } - case Type::Category::Integer: - m_expressions.emplace(&_e, m_interface->newInteger(uniqueSymbol(_e))); - break; - case Type::Category::Bool: - m_expressions.emplace(&_e, m_interface->newBool(uniqueSymbol(_e))); - break; - default: - solUnimplementedAssert(false, "Type not implemented."); - } + auto result = newSymbolicVariable(*_e.annotation().type, "expr_" + to_string(_e.id()), *m_interface); + m_expressions.emplace(&_e, result.second); + if (result.first) + m_errorReporter.warning( + _e.location(), + "Assertion checker does not yet implement this type." + ); } } @@ -899,7 +1102,7 @@ void SMTChecker::pushPathCondition(smt::Expression const& _e) smt::Expression SMTChecker::currentPathConditions() { - if (m_pathConditions.size() == 0) + if (m_pathConditions.empty()) return smt::Expression(true); return m_pathConditions.back(); } @@ -913,3 +1116,27 @@ void SMTChecker::addPathImpliedExpression(smt::Expression const& _e) { m_interface->addAssertion(smt::Expression::implies(currentPathConditions(), _e)); } + +bool SMTChecker::isRootFunction() +{ + return m_functionPath.size() == 1; +} + +bool SMTChecker::visitedFunction(FunctionDefinition const* _funDef) +{ + return contains(m_functionPath, _funDef); +} + +SMTChecker::VariableIndices SMTChecker::copyVariableIndices() +{ + VariableIndices indices; + for (auto const& var: m_variables) + indices.emplace(var.first, var.second->index()); + return indices; +} + +void SMTChecker::resetVariableIndices(VariableIndices const& _indices) +{ + for (auto const& var: _indices) + m_variables.at(var.first)->index() = var.second; +} diff --git a/libsolidity/formal/SMTChecker.h b/libsolidity/formal/SMTChecker.h index 50d40ab9..a7f955dd 100644 --- a/libsolidity/formal/SMTChecker.h +++ b/libsolidity/formal/SMTChecker.h @@ -19,14 +19,15 @@ #include <libsolidity/formal/SolverInterface.h> - -#include <libsolidity/formal/SSAVariable.h> +#include <libsolidity/formal/SymbolicVariables.h> #include <libsolidity/ast/ASTVisitor.h> #include <libsolidity/interface/ReadFile.h> -#include <map> +#include <libsolidity/parsing/Scanner.h> + +#include <unordered_map> #include <string> #include <vector> @@ -43,7 +44,7 @@ class SMTChecker: private ASTConstVisitor public: SMTChecker(ErrorReporter& _errorReporter, ReadCallback::Callback const& _readCallback); - void analyze(SourceUnit const& _sources); + void analyze(SourceUnit const& _sources, std::shared_ptr<Scanner> const& _scanner); private: // TODO: Check that we do not have concurrent reads and writes to a variable, @@ -59,7 +60,6 @@ private: virtual bool visit(WhileStatement const& _node) override; virtual bool visit(ForStatement const& _node) override; virtual void endVisit(VariableDeclarationStatement const& _node) override; - virtual void endVisit(ExpressionStatement const& _node) override; virtual void endVisit(Assignment const& _node) override; virtual void endVisit(TupleExpression const& _node) override; virtual void endVisit(UnaryOperation const& _node) override; @@ -67,26 +67,38 @@ private: virtual void endVisit(FunctionCall const& _node) override; virtual void endVisit(Identifier const& _node) override; virtual void endVisit(Literal const& _node) override; + virtual void endVisit(Return const& _node) override; + virtual bool visit(MemberAccess const& _node) override; void arithmeticOperation(BinaryOperation const& _op); void compareOperation(BinaryOperation const& _op); void booleanOperation(BinaryOperation const& _op); + void visitAssert(FunctionCall const&); + void visitRequire(FunctionCall const&); + void visitGasLeft(FunctionCall const&); + void visitBlockHash(FunctionCall const&); + /// Visits the FunctionDefinition of the called function + /// if available and inlines the return value. + void inlineFunctionCall(FunctionCall const&); + + void defineSpecialVariable(std::string const& _name, Expression const& _expr, bool _increaseIndex = false); + /// Division expression in the given type. Requires special treatment because /// of rounding for signed division. smt::Expression division(smt::Expression _left, smt::Expression _right, IntegerType const& _type); - void assignment(Declaration const& _variable, Expression const& _value, SourceLocation const& _location); - void assignment(Declaration const& _variable, smt::Expression const& _value, SourceLocation const& _location); + void assignment(VariableDeclaration const& _variable, Expression const& _value, SourceLocation const& _location); + void assignment(VariableDeclaration const& _variable, smt::Expression const& _value, SourceLocation const& _location); /// Maps a variable to an SSA index. - using VariableSequenceCounters = std::map<Declaration const*, SSAVariable>; + using VariableIndices = std::unordered_map<VariableDeclaration const*, int>; /// Visits the branch given by the statement, pushes and pops the current path conditions. /// @param _condition if present, asserts that this condition is true within the branch. - /// @returns the variable sequence counter after visiting the branch. - VariableSequenceCounters visitBranch(Statement const& _statement, smt::Expression const* _condition = nullptr); - VariableSequenceCounters visitBranch(Statement const& _statement, smt::Expression _condition); + /// @returns the variable indices after visiting the branch. + VariableIndices visitBranch(Statement const& _statement, smt::Expression const* _condition = nullptr); + VariableIndices visitBranch(Statement const& _statement, smt::Expression _condition); /// Check that a condition can be satisfied. void checkCondition( @@ -113,43 +125,47 @@ private: smt::CheckResult checkSatisfiable(); void initializeLocalVariables(FunctionDefinition const& _function); + void initializeFunctionCallParameters(FunctionDefinition const& _function, std::vector<smt::Expression> const& _callArgs); void resetStateVariables(); - void resetVariables(std::vector<Declaration const*> _variables); + void resetVariables(std::vector<VariableDeclaration const*> _variables); /// Given two different branches and the touched variables, /// merge the touched variables into after-branch ite variables /// using the branch condition as guard. - void mergeVariables(std::vector<Declaration const*> const& _variables, smt::Expression const& _condition, VariableSequenceCounters const& _countersEndTrue, VariableSequenceCounters const& _countersEndFalse); + void mergeVariables(std::vector<VariableDeclaration const*> const& _variables, smt::Expression const& _condition, VariableIndices const& _indicesEndTrue, VariableIndices const& _indicesEndFalse); /// Tries to create an uninitialized variable and returns true on success. /// This fails if the type is not supported. bool createVariable(VariableDeclaration const& _varDecl); - static std::string uniqueSymbol(Expression const& _expr); - /// @returns true if _delc is a variable that is known at the current point, i.e. - /// has a valid sequence number - bool knownVariable(Declaration const& _decl); + /// has a valid index + bool knownVariable(VariableDeclaration const& _decl); /// @returns an expression denoting the value of the variable declared in @a _decl /// at the current point. - smt::Expression currentValue(Declaration const& _decl); + smt::Expression currentValue(VariableDeclaration const& _decl); /// @returns an expression denoting the value of the variable declared in @a _decl - /// at the given sequence point. Does not ensure that this sequence point exists. - smt::Expression valueAtSequence(Declaration const& _decl, int _sequence); - /// Allocates a new sequence number for the declaration, updates the current - /// sequence number to this value and returns the expression. - smt::Expression newValue(Declaration const& _decl); + /// at the given index. Does not ensure that this index exists. + smt::Expression valueAtIndex(VariableDeclaration const& _decl, int _index); + /// Allocates a new index for the declaration, updates the current + /// index to this value and returns the expression. + smt::Expression newValue(VariableDeclaration const& _decl); /// Sets the value of the declaration to zero. - void setZeroValue(Declaration const& _decl); + void setZeroValue(VariableDeclaration const& _decl); /// Resets the variable to an unknown value (in its range). - void setUnknownValue(Declaration const& decl); + void setUnknownValue(VariableDeclaration const& decl); /// Returns the expression corresponding to the AST node. Throws if the expression does not exist. smt::Expression expr(Expression const& _e); /// Creates the expression (value can be arbitrary) void createExpr(Expression const& _e); + /// Checks if expression was created + bool knownExpr(Expression const& _e) const; /// Creates the expression and sets its value. void defineExpr(Expression const& _e, smt::Expression _value); + /// Checks if special variable was seen. + bool knownSpecialVariable(std::string const& _var) const; + /// Adds a new path condition void pushPathCondition(smt::Expression const& _e); /// Remove the last path condition @@ -161,16 +177,33 @@ private: /// Add to the solver: the given expression implied by the current path conditions void addPathImpliedExpression(smt::Expression const& _e); + /// Removes local variables from the context. + void removeLocalVariables(); + + /// Copy the SSA indices of m_variables. + VariableIndices copyVariableIndices(); + /// Resets the variable indices. + void resetVariableIndices(VariableIndices const& _indices); + std::shared_ptr<smt::SolverInterface> m_interface; std::shared_ptr<VariableUsage> m_variableUsage; bool m_loopExecutionHappened = false; - std::map<Expression const*, smt::Expression> m_expressions; - std::map<Declaration const*, SSAVariable> m_variables; - std::map<Declaration const*, SSAVariable> m_stateVariables; + /// An Expression may have multiple smt::Expression due to + /// repeated calls to the same function. + std::unordered_map<Expression const*, std::shared_ptr<SymbolicVariable>> m_expressions; + std::unordered_map<VariableDeclaration const*, std::shared_ptr<SymbolicVariable>> m_variables; + std::unordered_map<std::string, std::shared_ptr<SymbolicVariable>> m_specialVariables; std::vector<smt::Expression> m_pathConditions; ErrorReporter& m_errorReporter; - - FunctionDefinition const* m_currentFunction = nullptr; + std::shared_ptr<Scanner> m_scanner; + + /// Stores the current path of function calls. + std::vector<FunctionDefinition const*> m_functionPath; + /// Returns true if the current function was not visited by + /// a function call. + bool isRootFunction(); + /// Returns true if _funDef was already visited. + bool visitedFunction(FunctionDefinition const* _funDef); }; } diff --git a/libsolidity/formal/SMTLib2Interface.cpp b/libsolidity/formal/SMTLib2Interface.cpp index 0e00665a..a6c1f87c 100644 --- a/libsolidity/formal/SMTLib2Interface.cpp +++ b/libsolidity/formal/SMTLib2Interface.cpp @@ -47,6 +47,8 @@ void SMTLib2Interface::reset() { m_accumulatedOutput.clear(); m_accumulatedOutput.emplace_back(); + m_constants.clear(); + m_functions.clear(); write("(set-option :produce-models true)"); write("(set-logic QF_UFLIA)"); } @@ -62,30 +64,40 @@ void SMTLib2Interface::pop() m_accumulatedOutput.pop_back(); } -Expression SMTLib2Interface::newFunction(string _name, Sort _domain, Sort _codomain) +void SMTLib2Interface::declareFunction(string _name, Sort _domain, Sort _codomain) { - write( - "(declare-fun |" + - _name + - "| (" + - (_domain == Sort::Int ? "Int" : "Bool") + - ") " + - (_codomain == Sort::Int ? "Int" : "Bool") + - ")" - ); - return SolverInterface::newFunction(move(_name), _domain, _codomain); + // TODO Use domain and codomain as key as well + if (!m_functions.count(_name)) + { + m_functions.insert(_name); + write( + "(declare-fun |" + + _name + + "| (" + + (_domain == Sort::Int ? "Int" : "Bool") + + ") " + + (_codomain == Sort::Int ? "Int" : "Bool") + + ")" + ); + } } -Expression SMTLib2Interface::newInteger(string _name) +void SMTLib2Interface::declareInteger(string _name) { - write("(declare-const |" + _name + "| Int)"); - return SolverInterface::newInteger(move(_name)); + if (!m_constants.count(_name)) + { + m_constants.insert(_name); + write("(declare-const |" + _name + "| Int)"); + } } -Expression SMTLib2Interface::newBool(string _name) +void SMTLib2Interface::declareBool(string _name) { - write("(declare-const |" + _name + "| Bool)"); - return SolverInterface::newBool(std::move(_name)); + if (!m_constants.count(_name)) + { + m_constants.insert(_name); + write("(declare-const |" + _name + "| Bool)"); + } } void SMTLib2Interface::addAssertion(Expression const& _expr) @@ -112,7 +124,7 @@ pair<CheckResult, vector<string>> SMTLib2Interface::check(vector<Expression> con result = CheckResult::ERROR; vector<string> values; - if (result != CheckResult::UNSATISFIABLE && result != CheckResult::ERROR) + if (result == CheckResult::SATISFIABLE && result != CheckResult::ERROR) values = parseValues(find(response.cbegin(), response.cend(), '\n'), response.cend()); return make_pair(result, values); } @@ -146,7 +158,7 @@ string SMTLib2Interface::checkSatAndGetValuesCommand(vector<Expression> const& _ { auto const& e = _expressionsToEvaluate.at(i); solAssert(e.sort == Sort::Int || e.sort == Sort::Bool, "Invalid sort for expression to evaluate."); - command += "(declare-const |EVALEXPR_" + to_string(i) + "| " + (e.sort == Sort::Int ? "Int" : "Bool") + "\n"; + command += "(declare-const |EVALEXPR_" + to_string(i) + "| " + (e.sort == Sort::Int ? "Int" : "Bool") + ")\n"; command += "(assert (= |EVALEXPR_" + to_string(i) + "| " + toSExpr(e) + "))\n"; } command += "(check-sat)\n"; diff --git a/libsolidity/formal/SMTLib2Interface.h b/libsolidity/formal/SMTLib2Interface.h index 63188acd..eb876a7f 100644 --- a/libsolidity/formal/SMTLib2Interface.h +++ b/libsolidity/formal/SMTLib2Interface.h @@ -30,6 +30,7 @@ #include <string> #include <vector> #include <cstdio> +#include <set> namespace dev { @@ -48,9 +49,9 @@ public: void push() override; void pop() override; - Expression newFunction(std::string _name, Sort _domain, Sort _codomain) override; - Expression newInteger(std::string _name) override; - Expression newBool(std::string _name) override; + void declareFunction(std::string _name, Sort _domain, Sort _codomain) override; + void declareInteger(std::string _name) override; + void declareBool(std::string _name) override; void addAssertion(Expression const& _expr) override; std::pair<CheckResult, std::vector<std::string>> check(std::vector<Expression> const& _expressionsToEvaluate) override; @@ -68,6 +69,8 @@ private: ReadCallback::Callback m_queryCallback; std::vector<std::string> m_accumulatedOutput; + std::set<std::string> m_constants; + std::set<std::string> m_functions; }; } diff --git a/libsolidity/formal/SMTPortfolio.cpp b/libsolidity/formal/SMTPortfolio.cpp new file mode 100644 index 00000000..8b9fe9ce --- /dev/null +++ b/libsolidity/formal/SMTPortfolio.cpp @@ -0,0 +1,152 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <libsolidity/formal/SMTPortfolio.h> + +#ifdef HAVE_Z3 +#include <libsolidity/formal/Z3Interface.h> +#endif +#ifdef HAVE_CVC4 +#include <libsolidity/formal/CVC4Interface.h> +#endif +#if !defined (HAVE_Z3) && !defined (HAVE_CVC4) +#include <libsolidity/formal/SMTLib2Interface.h> +#endif + +using namespace std; +using namespace dev; +using namespace dev::solidity; +using namespace dev::solidity::smt; + +SMTPortfolio::SMTPortfolio(ReadCallback::Callback const& _readCallback) +{ +#ifdef HAVE_Z3 + m_solvers.emplace_back(make_shared<smt::Z3Interface>()); +#endif +#ifdef HAVE_CVC4 + m_solvers.emplace_back(make_shared<smt::CVC4Interface>()); +#endif +#if !defined (HAVE_Z3) && !defined (HAVE_CVC4) + m_solvers.emplace_back(make_shared<smt::SMTLib2Interface>(_readCallback)), +#endif + (void)_readCallback; +} + +void SMTPortfolio::reset() +{ + for (auto s : m_solvers) + s->reset(); +} + +void SMTPortfolio::push() +{ + for (auto s : m_solvers) + s->push(); +} + +void SMTPortfolio::pop() +{ + for (auto s : m_solvers) + s->pop(); +} + +void SMTPortfolio::declareFunction(string _name, Sort _domain, Sort _codomain) +{ + for (auto s : m_solvers) + s->declareFunction(_name, _domain, _codomain); +} + +void SMTPortfolio::declareInteger(string _name) +{ + for (auto s : m_solvers) + s->declareInteger(_name); +} + +void SMTPortfolio::declareBool(string _name) +{ + for (auto s : m_solvers) + s->declareBool(_name); +} + +void SMTPortfolio::addAssertion(Expression const& _expr) +{ + for (auto s : m_solvers) + s->addAssertion(_expr); +} + +/* + * Broadcasts the SMT query to all solvers and returns a single result. + * This comment explains how this result is decided. + * + * When a solver is queried, there are four possible answers: + * SATISFIABLE (SAT), UNSATISFIABLE (UNSAT), UNKNOWN, CONFLICTING, ERROR + * We say that a solver _answered_ the query if it returns either: + * SAT or UNSAT + * A solver did not answer the query if it returns either: + * UNKNOWN (it tried but couldn't solve it) or ERROR (crash, internal error, API error, etc). + * + * Ideally all solvers answer the query and agree on what the answer is + * (all say SAT or all say UNSAT). + * + * The actual logic as as follows: + * 1) If at least one solver answers the query, all the non-answer results are ignored. + * Here SAT/UNSAT is preferred over UNKNOWN since it's an actual answer, and over ERROR + * because one buggy solver/integration shouldn't break the portfolio. + * + * 2) If at least one solver answers SAT and at least one answers UNSAT, at least one of them is buggy + * and the result is CONFLICTING. + * In the future if we have more than 2 solvers enabled we could go with the majority. + * + * 3) If NO solver answers the query: + * If at least one solver returned UNKNOWN (where the rest returned ERROR), the result is UNKNOWN. + * This is preferred over ERROR since the SMTChecker might decide to abstract the query + * when it is told that this is a hard query to solve. + * + * If all solvers return ERROR, the result is ERROR. +*/ +pair<CheckResult, vector<string>> SMTPortfolio::check(vector<Expression> const& _expressionsToEvaluate) +{ + CheckResult lastResult = CheckResult::ERROR; + vector<string> finalValues; + for (auto s : m_solvers) + { + CheckResult result; + vector<string> values; + tie(result, values) = s->check(_expressionsToEvaluate); + if (solverAnswered(result)) + { + if (!solverAnswered(lastResult)) + { + lastResult = result; + finalValues = std::move(values); + } + else if (lastResult != result) + { + lastResult = CheckResult::CONFLICTING; + break; + } + } + else if (result == CheckResult::UNKNOWN && lastResult == CheckResult::ERROR) + lastResult = result; + } + return make_pair(lastResult, finalValues); +} + +bool SMTPortfolio::solverAnswered(CheckResult result) +{ + return result == CheckResult::SATISFIABLE || result == CheckResult::UNSATISFIABLE; +} diff --git a/libsolidity/formal/SMTPortfolio.h b/libsolidity/formal/SMTPortfolio.h new file mode 100644 index 00000000..96c7ff57 --- /dev/null +++ b/libsolidity/formal/SMTPortfolio.h @@ -0,0 +1,67 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see <http://www.gnu.org/licenses/>. +*/ + +#pragma once + + +#include <libsolidity/formal/SolverInterface.h> + +#include <libsolidity/interface/ReadFile.h> + +#include <boost/noncopyable.hpp> + +#include <vector> + +namespace dev +{ +namespace solidity +{ +namespace smt +{ + +/** + * The SMTPortfolio wraps all available solvers within a single interface, + * propagating the functionalities to all solvers. + * It also checks whether different solvers give conflicting answers + * to SMT queries. + */ +class SMTPortfolio: public SolverInterface, public boost::noncopyable +{ +public: + SMTPortfolio(ReadCallback::Callback const& _readCallback); + + void reset() override; + + void push() override; + void pop() override; + + void declareFunction(std::string _name, Sort _domain, Sort _codomain) override; + void declareInteger(std::string _name) override; + void declareBool(std::string _name) override; + + void addAssertion(Expression const& _expr) override; + std::pair<CheckResult, std::vector<std::string>> check(std::vector<Expression> const& _expressionsToEvaluate) override; + +private: + static bool solverAnswered(CheckResult result); + + std::vector<std::shared_ptr<smt::SolverInterface>> m_solvers; +}; + +} +} +} diff --git a/libsolidity/formal/SSAVariable.cpp b/libsolidity/formal/SSAVariable.cpp index f3213e03..36e15508 100644 --- a/libsolidity/formal/SSAVariable.cpp +++ b/libsolidity/formal/SSAVariable.cpp @@ -17,70 +17,17 @@ #include <libsolidity/formal/SSAVariable.h> -#include <libsolidity/formal/SymbolicBoolVariable.h> -#include <libsolidity/formal/SymbolicIntVariable.h> - -#include <libsolidity/ast/AST.h> - using namespace std; -using namespace dev; using namespace dev::solidity; -SSAVariable::SSAVariable( - Declaration const& _decl, - smt::SolverInterface& _interface -) +SSAVariable::SSAVariable() { resetIndex(); - - if (isInteger(_decl.type()->category())) - m_symbolicVar = make_shared<SymbolicIntVariable>(_decl, _interface); - else if (isBool(_decl.type()->category())) - m_symbolicVar = make_shared<SymbolicBoolVariable>(_decl, _interface); - else - { - solAssert(false, ""); - } -} - -bool SSAVariable::isSupportedType(Type::Category _category) -{ - return isInteger(_category) || isBool(_category); -} - -bool SSAVariable::isInteger(Type::Category _category) -{ - return _category == Type::Category::Integer; -} - -bool SSAVariable::isBool(Type::Category _category) -{ - return _category == Type::Category::Bool; } void SSAVariable::resetIndex() { - m_currentSequenceCounter = 0; - m_nextFreeSequenceCounter.reset (new int); - *m_nextFreeSequenceCounter = 1; -} - -int SSAVariable::index() const -{ - return m_currentSequenceCounter; -} - -int SSAVariable::next() const -{ - return *m_nextFreeSequenceCounter; -} - -void SSAVariable::setZeroValue() -{ - m_symbolicVar->setZeroValue(index()); -} - -void SSAVariable::setUnknownValue() -{ - m_symbolicVar->setUnknownValue(index()); + m_currentIndex = 0; + m_nextFreeIndex.reset (new unsigned); + *m_nextFreeIndex = 1; } diff --git a/libsolidity/formal/SSAVariable.h b/libsolidity/formal/SSAVariable.h index bf5dae3b..46935472 100644 --- a/libsolidity/formal/SSAVariable.h +++ b/libsolidity/formal/SSAVariable.h @@ -17,8 +17,6 @@ #pragma once -#include <libsolidity/formal/SymbolicVariable.h> - #include <memory> namespace dev @@ -26,64 +24,29 @@ namespace dev namespace solidity { -class Declaration; - /** * This class represents the SSA representation of a program variable. */ class SSAVariable { public: - /// @param _decl Used to determine the type and forwarded to the symbolic var. - /// @param _interface Forwarded to the symbolic var such that it can give constraints to the solver. - SSAVariable( - Declaration const& _decl, - smt::SolverInterface& _interface - ); - + SSAVariable(); void resetIndex(); /// This function returns the current index of this SSA variable. - int index() const; - /// This function returns the next free index of this SSA variable. - int next() const; - - int operator++() - { - return m_currentSequenceCounter = (*m_nextFreeSequenceCounter)++; - } - - smt::Expression operator()() const - { - return valueAtSequence(index()); - } + unsigned index() const { return m_currentIndex; } + unsigned& index() { return m_currentIndex; } - smt::Expression operator()(int _seq) const + unsigned operator++() { - return valueAtSequence(_seq); + return m_currentIndex = (*m_nextFreeIndex)++; } - /// These two functions forward the call to the symbolic var - /// which generates the constraints according to the type. - void setZeroValue(); - void setUnknownValue(); - - /// So far Int and Bool are supported. - static bool isSupportedType(Type::Category _category); - static bool isInteger(Type::Category _category); - static bool isBool(Type::Category _category); - private: - smt::Expression valueAtSequence(int _seq) const - { - return (*m_symbolicVar)(_seq); - } - - std::shared_ptr<SymbolicVariable> m_symbolicVar = nullptr; - int m_currentSequenceCounter; - /// The next free sequence counter is a shared pointer because we want + unsigned m_currentIndex; + /// The next free index is a shared pointer because we want /// the copy and the copied to share it. - std::shared_ptr<int> m_nextFreeSequenceCounter; + std::shared_ptr<unsigned> m_nextFreeIndex; }; } diff --git a/libsolidity/formal/SolverInterface.h b/libsolidity/formal/SolverInterface.h index 16796684..af1cc8e4 100644 --- a/libsolidity/formal/SolverInterface.h +++ b/libsolidity/formal/SolverInterface.h @@ -39,7 +39,7 @@ namespace smt enum class CheckResult { - SATISFIABLE, UNSATISFIABLE, UNKNOWN, ERROR + SATISFIABLE, UNSATISFIABLE, UNKNOWN, CONFLICTING, ERROR }; enum class Sort @@ -171,8 +171,8 @@ public: } } - std::string const name; - std::vector<Expression> const arguments; + std::string name; + std::vector<Expression> arguments; Sort sort; private: @@ -199,8 +199,10 @@ public: virtual void push() = 0; virtual void pop() = 0; - virtual Expression newFunction(std::string _name, Sort _domain, Sort _codomain) + virtual void declareFunction(std::string _name, Sort _domain, Sort _codomain) = 0; + Expression newFunction(std::string _name, Sort _domain, Sort _codomain) { + declareFunction(_name, _domain, _codomain); solAssert(_domain == Sort::Int, "Function sort not supported."); // Subclasses should do something here switch (_codomain) @@ -214,14 +216,18 @@ public: break; } } - virtual Expression newInteger(std::string _name) + virtual void declareInteger(std::string _name) = 0; + Expression newInteger(std::string _name) { // Subclasses should do something here + declareInteger(_name); return Expression(std::move(_name), {}, Sort::Int); } - virtual Expression newBool(std::string _name) + virtual void declareBool(std::string _name) = 0; + Expression newBool(std::string _name) { // Subclasses should do something here + declareBool(_name); return Expression(std::move(_name), {}, Sort::Bool); } @@ -231,8 +237,11 @@ public: /// is available. Throws SMTSolverError on error. virtual std::pair<CheckResult, std::vector<std::string>> check(std::vector<Expression> const& _expressionsToEvaluate) = 0; -}; +protected: + // SMT query timeout in milliseconds. + static int const queryTimeout = 10000; +}; } } diff --git a/libsolidity/formal/SymbolicBoolVariable.cpp b/libsolidity/formal/SymbolicBoolVariable.cpp deleted file mode 100644 index 5cf22d7d..00000000 --- a/libsolidity/formal/SymbolicBoolVariable.cpp +++ /dev/null @@ -1,47 +0,0 @@ -/* - This file is part of solidity. - - solidity is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - solidity is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with solidity. If not, see <http://www.gnu.org/licenses/>. -*/ - -#include <libsolidity/formal/SymbolicBoolVariable.h> - -#include <libsolidity/ast/AST.h> - -using namespace std; -using namespace dev; -using namespace dev::solidity; - -SymbolicBoolVariable::SymbolicBoolVariable( - Declaration const& _decl, - smt::SolverInterface&_interface -): - SymbolicVariable(_decl, _interface) -{ - solAssert(m_declaration.type()->category() == Type::Category::Bool, ""); -} - -smt::Expression SymbolicBoolVariable::valueAtSequence(int _seq) const -{ - return m_interface.newBool(uniqueSymbol(_seq)); -} - -void SymbolicBoolVariable::setZeroValue(int _seq) -{ - m_interface.addAssertion(valueAtSequence(_seq) == smt::Expression(false)); -} - -void SymbolicBoolVariable::setUnknownValue(int) -{ -} diff --git a/libsolidity/formal/SymbolicBoolVariable.h b/libsolidity/formal/SymbolicBoolVariable.h deleted file mode 100644 index 678f97d9..00000000 --- a/libsolidity/formal/SymbolicBoolVariable.h +++ /dev/null @@ -1,50 +0,0 @@ -/* - This file is part of solidity. - - solidity is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - solidity is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with solidity. If not, see <http://www.gnu.org/licenses/>. -*/ - -#pragma once - -#include <libsolidity/formal/SymbolicVariable.h> - -#include <libsolidity/ast/Types.h> - -namespace dev -{ -namespace solidity -{ - -/** - * Specialization of SymbolicVariable for Bool - */ -class SymbolicBoolVariable: public SymbolicVariable -{ -public: - SymbolicBoolVariable( - Declaration const& _decl, - smt::SolverInterface& _interface - ); - - /// Sets the var to false. - void setZeroValue(int _seq); - /// Does nothing since the SMT solver already knows the valid values. - void setUnknownValue(int _seq); - -protected: - smt::Expression valueAtSequence(int _seq) const; -}; - -} -} diff --git a/libsolidity/formal/SymbolicIntVariable.cpp b/libsolidity/formal/SymbolicIntVariable.cpp deleted file mode 100644 index 5e71fdcc..00000000 --- a/libsolidity/formal/SymbolicIntVariable.cpp +++ /dev/null @@ -1,60 +0,0 @@ -/* - This file is part of solidity. - - solidity is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - solidity is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with solidity. If not, see <http://www.gnu.org/licenses/>. -*/ - -#include <libsolidity/formal/SymbolicIntVariable.h> - -#include <libsolidity/ast/AST.h> - -using namespace std; -using namespace dev; -using namespace dev::solidity; - -SymbolicIntVariable::SymbolicIntVariable( - Declaration const& _decl, - smt::SolverInterface& _interface -): - SymbolicVariable(_decl, _interface) -{ - solAssert(m_declaration.type()->category() == Type::Category::Integer, ""); -} - -smt::Expression SymbolicIntVariable::valueAtSequence(int _seq) const -{ - return m_interface.newInteger(uniqueSymbol(_seq)); -} - -void SymbolicIntVariable::setZeroValue(int _seq) -{ - m_interface.addAssertion(valueAtSequence(_seq) == 0); -} - -void SymbolicIntVariable::setUnknownValue(int _seq) -{ - auto const& intType = dynamic_cast<IntegerType const&>(*m_declaration.type()); - m_interface.addAssertion(valueAtSequence(_seq) >= minValue(intType)); - m_interface.addAssertion(valueAtSequence(_seq) <= maxValue(intType)); -} - -smt::Expression SymbolicIntVariable::minValue(IntegerType const& _t) -{ - return smt::Expression(_t.minValue()); -} - -smt::Expression SymbolicIntVariable::maxValue(IntegerType const& _t) -{ - return smt::Expression(_t.maxValue()); -} diff --git a/libsolidity/formal/SymbolicIntVariable.h b/libsolidity/formal/SymbolicIntVariable.h deleted file mode 100644 index d591e8db..00000000 --- a/libsolidity/formal/SymbolicIntVariable.h +++ /dev/null @@ -1,53 +0,0 @@ -/* - This file is part of solidity. - - solidity is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - solidity is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with solidity. If not, see <http://www.gnu.org/licenses/>. -*/ - -#pragma once - -#include <libsolidity/formal/SymbolicVariable.h> - -#include <libsolidity/ast/Types.h> - -namespace dev -{ -namespace solidity -{ - -/** - * Specialization of SymbolicVariable for Integers - */ -class SymbolicIntVariable: public SymbolicVariable -{ -public: - SymbolicIntVariable( - Declaration const& _decl, - smt::SolverInterface& _interface - ); - - /// Sets the var to 0. - void setZeroValue(int _seq); - /// Sets the variable to the full valid value range. - void setUnknownValue(int _seq); - - static smt::Expression minValue(IntegerType const& _t); - static smt::Expression maxValue(IntegerType const& _t); - -protected: - smt::Expression valueAtSequence(int _seq) const; -}; - -} -} diff --git a/libsolidity/formal/SymbolicTypes.cpp b/libsolidity/formal/SymbolicTypes.cpp new file mode 100644 index 00000000..3eb1c1ce --- /dev/null +++ b/libsolidity/formal/SymbolicTypes.cpp @@ -0,0 +1,127 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <libsolidity/formal/SymbolicTypes.h> + +#include <libsolidity/ast/Types.h> + +#include <memory> + +using namespace std; +using namespace dev::solidity; + +bool dev::solidity::isSupportedType(Type::Category _category) +{ + return isNumber(_category) || + isBool(_category) || + isFunction(_category); +} + +pair<bool, shared_ptr<SymbolicVariable>> dev::solidity::newSymbolicVariable( + Type const& _type, + std::string const& _uniqueName, + smt::SolverInterface& _solver +) +{ + bool abstract = false; + shared_ptr<SymbolicVariable> var; + TypePointer type = _type.shared_from_this(); + if (!isSupportedType(_type)) + { + abstract = true; + var = make_shared<SymbolicIntVariable>(make_shared<IntegerType>(256), _uniqueName, _solver); + } + else if (isBool(_type.category())) + var = make_shared<SymbolicBoolVariable>(type, _uniqueName, _solver); + else if (isFunction(_type.category())) + var = make_shared<SymbolicIntVariable>(make_shared<IntegerType>(256), _uniqueName, _solver); + else if (isInteger(_type.category())) + var = make_shared<SymbolicIntVariable>(type, _uniqueName, _solver); + else if (isFixedBytes(_type.category())) + { + auto fixedBytesType = dynamic_cast<FixedBytesType const*>(type.get()); + solAssert(fixedBytesType, ""); + var = make_shared<SymbolicFixedBytesVariable>(fixedBytesType->numBytes(), _uniqueName, _solver); + } + else if (isAddress(_type.category())) + var = make_shared<SymbolicAddressVariable>(_uniqueName, _solver); + else if (isRational(_type.category())) + { + auto rational = dynamic_cast<RationalNumberType const*>(&_type); + solAssert(rational, ""); + if (rational->isFractional()) + var = make_shared<SymbolicIntVariable>(make_shared<IntegerType>(256), _uniqueName, _solver); + else + var = make_shared<SymbolicIntVariable>(type, _uniqueName, _solver); + } + else + solAssert(false, ""); + return make_pair(abstract, var); +} + +bool dev::solidity::isSupportedType(Type const& _type) +{ + return isSupportedType(_type.category()); +} + +bool dev::solidity::isInteger(Type::Category _category) +{ + return _category == Type::Category::Integer; +} + +bool dev::solidity::isRational(Type::Category _category) +{ + return _category == Type::Category::RationalNumber; +} + +bool dev::solidity::isFixedBytes(Type::Category _category) +{ + return _category == Type::Category::FixedBytes; +} + +bool dev::solidity::isAddress(Type::Category _category) +{ + return _category == Type::Category::Address; +} + +bool dev::solidity::isNumber(Type::Category _category) +{ + return isInteger(_category) || + isRational(_category) || + isFixedBytes(_category) || + isAddress(_category); +} + +bool dev::solidity::isBool(Type::Category _category) +{ + return _category == Type::Category::Bool; +} + +bool dev::solidity::isFunction(Type::Category _category) +{ + return _category == Type::Category::Function; +} + +smt::Expression dev::solidity::minValue(IntegerType const& _type) +{ + return smt::Expression(_type.minValue()); +} + +smt::Expression dev::solidity::maxValue(IntegerType const& _type) +{ + return smt::Expression(_type.maxValue()); +} diff --git a/libsolidity/formal/SymbolicTypes.h b/libsolidity/formal/SymbolicTypes.h new file mode 100644 index 00000000..dcdd9ea4 --- /dev/null +++ b/libsolidity/formal/SymbolicTypes.h @@ -0,0 +1,53 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see <http://www.gnu.org/licenses/>. +*/ + +#pragma once + +#include <libsolidity/formal/SolverInterface.h> +#include <libsolidity/formal/SymbolicVariables.h> + +#include <libsolidity/ast/AST.h> +#include <libsolidity/ast/Types.h> + +namespace dev +{ +namespace solidity +{ + +/// So far int, bool and address are supported. +/// Returns true if type is supported. +bool isSupportedType(Type::Category _category); +bool isSupportedType(Type const& _type); + +bool isInteger(Type::Category _category); +bool isRational(Type::Category _category); +bool isFixedBytes(Type::Category _category); +bool isAddress(Type::Category _category); +bool isNumber(Type::Category _category); +bool isBool(Type::Category _category); +bool isFunction(Type::Category _category); + +/// Returns a new symbolic variable, according to _type. +/// Also returns whether the type is abstract or not, +/// which is true for unsupported types. +std::pair<bool, std::shared_ptr<SymbolicVariable>> newSymbolicVariable(Type const& _type, std::string const& _uniqueName, smt::SolverInterface& _solver); + +smt::Expression minValue(IntegerType const& _type); +smt::Expression maxValue(IntegerType const& _type); + +} +} diff --git a/libsolidity/formal/SymbolicVariable.cpp b/libsolidity/formal/SymbolicVariable.cpp deleted file mode 100644 index caefa3a3..00000000 --- a/libsolidity/formal/SymbolicVariable.cpp +++ /dev/null @@ -1,40 +0,0 @@ -/* - This file is part of solidity. - - solidity is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - solidity is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with solidity. If not, see <http://www.gnu.org/licenses/>. -*/ - -#include <libsolidity/formal/SymbolicVariable.h> - -#include <libsolidity/ast/AST.h> - -using namespace std; -using namespace dev; -using namespace dev::solidity; - -SymbolicVariable::SymbolicVariable( - Declaration const& _decl, - smt::SolverInterface& _interface -): - m_declaration(_decl), - m_interface(_interface) -{ -} - -string SymbolicVariable::uniqueSymbol(int _seq) const -{ - return m_declaration.name() + "_" + to_string(m_declaration.id()) + "_" + to_string(_seq); -} - - diff --git a/libsolidity/formal/SymbolicVariable.h b/libsolidity/formal/SymbolicVariable.h deleted file mode 100644 index e29ded26..00000000 --- a/libsolidity/formal/SymbolicVariable.h +++ /dev/null @@ -1,66 +0,0 @@ -/* - This file is part of solidity. - - solidity is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - solidity is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with solidity. If not, see <http://www.gnu.org/licenses/>. -*/ - -#pragma once - -#include <libsolidity/formal/SolverInterface.h> - -#include <libsolidity/ast/AST.h> - -#include <memory> - -namespace dev -{ -namespace solidity -{ - -class Declaration; - -/** - * This class represents the symbolic version of a program variable. - */ -class SymbolicVariable -{ -public: - SymbolicVariable( - Declaration const& _decl, - smt::SolverInterface& _interface - ); - virtual ~SymbolicVariable() = default; - - smt::Expression operator()(int _seq) const - { - return valueAtSequence(_seq); - } - - std::string uniqueSymbol(int _seq) const; - - /// Sets the var to the default value of its type. - virtual void setZeroValue(int _seq) = 0; - /// The unknown value is the full range of valid values, - /// and that's sub-type dependent. - virtual void setUnknownValue(int _seq) = 0; - -protected: - virtual smt::Expression valueAtSequence(int _seq) const = 0; - - Declaration const& m_declaration; - smt::SolverInterface& m_interface; -}; - -} -} diff --git a/libsolidity/formal/SymbolicVariables.cpp b/libsolidity/formal/SymbolicVariables.cpp new file mode 100644 index 00000000..85818ba0 --- /dev/null +++ b/libsolidity/formal/SymbolicVariables.cpp @@ -0,0 +1,112 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <libsolidity/formal/SymbolicVariables.h> + +#include <libsolidity/formal/SymbolicTypes.h> + +#include <libsolidity/ast/AST.h> + +using namespace std; +using namespace dev; +using namespace dev::solidity; + +SymbolicVariable::SymbolicVariable( + TypePointer _type, + string const& _uniqueName, + smt::SolverInterface& _interface +): + m_type(move(_type)), + m_uniqueName(_uniqueName), + m_interface(_interface), + m_ssa(make_shared<SSAVariable>()) +{ +} + +string SymbolicVariable::uniqueSymbol(unsigned _index) const +{ + return m_uniqueName + "_" + to_string(_index); +} + +SymbolicBoolVariable::SymbolicBoolVariable( + TypePointer _type, + string const& _uniqueName, + smt::SolverInterface&_interface +): + SymbolicVariable(move(_type), _uniqueName, _interface) +{ + solAssert(m_type->category() == Type::Category::Bool, ""); +} + +smt::Expression SymbolicBoolVariable::valueAtIndex(int _index) const +{ + return m_interface.newBool(uniqueSymbol(_index)); +} + +void SymbolicBoolVariable::setZeroValue() +{ + m_interface.addAssertion(currentValue() == smt::Expression(false)); +} + +void SymbolicBoolVariable::setUnknownValue() +{ +} + +SymbolicIntVariable::SymbolicIntVariable( + TypePointer _type, + string const& _uniqueName, + smt::SolverInterface& _interface +): + SymbolicVariable(move(_type), _uniqueName, _interface) +{ + solAssert(isNumber(m_type->category()), ""); +} + +smt::Expression SymbolicIntVariable::valueAtIndex(int _index) const +{ + return m_interface.newInteger(uniqueSymbol(_index)); +} + +void SymbolicIntVariable::setZeroValue() +{ + m_interface.addAssertion(currentValue() == 0); +} + +void SymbolicIntVariable::setUnknownValue() +{ + auto intType = dynamic_cast<IntegerType const*>(m_type.get()); + solAssert(intType, ""); + m_interface.addAssertion(currentValue() >= minValue(*intType)); + m_interface.addAssertion(currentValue() <= maxValue(*intType)); +} + +SymbolicAddressVariable::SymbolicAddressVariable( + string const& _uniqueName, + smt::SolverInterface& _interface +): + SymbolicIntVariable(make_shared<IntegerType>(160), _uniqueName, _interface) +{ +} + +SymbolicFixedBytesVariable::SymbolicFixedBytesVariable( + unsigned _numBytes, + string const& _uniqueName, + smt::SolverInterface& _interface +): + SymbolicIntVariable(make_shared<IntegerType>(_numBytes * 8), _uniqueName, _interface) +{ +} diff --git a/libsolidity/formal/SymbolicVariables.h b/libsolidity/formal/SymbolicVariables.h new file mode 100644 index 00000000..4fd9b245 --- /dev/null +++ b/libsolidity/formal/SymbolicVariables.h @@ -0,0 +1,149 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see <http://www.gnu.org/licenses/>. +*/ + +#pragma once + +#include <libsolidity/formal/SSAVariable.h> + +#include <libsolidity/formal/SolverInterface.h> + +#include <libsolidity/ast/Types.h> + +#include <memory> + +namespace dev +{ +namespace solidity +{ + +class Type; + +/** + * This abstract class represents the symbolic version of a program variable. + */ +class SymbolicVariable +{ +public: + SymbolicVariable( + TypePointer _type, + std::string const& _uniqueName, + smt::SolverInterface& _interface + ); + + virtual ~SymbolicVariable() = default; + + smt::Expression currentValue() const + { + return valueAtIndex(m_ssa->index()); + } + + virtual smt::Expression valueAtIndex(int _index) const = 0; + + smt::Expression increaseIndex() + { + ++(*m_ssa); + return currentValue(); + } + + unsigned index() const { return m_ssa->index(); } + unsigned& index() { return m_ssa->index(); } + + /// Sets the var to the default value of its type. + /// Inherited types must implement. + virtual void setZeroValue() = 0; + /// The unknown value is the full range of valid values. + /// It is sub-type dependent, but not mandatory. + virtual void setUnknownValue() {} + +protected: + std::string uniqueSymbol(unsigned _index) const; + + TypePointer m_type = nullptr; + std::string m_uniqueName; + smt::SolverInterface& m_interface; + std::shared_ptr<SSAVariable> m_ssa = nullptr; +}; + +/** + * Specialization of SymbolicVariable for Bool + */ +class SymbolicBoolVariable: public SymbolicVariable +{ +public: + SymbolicBoolVariable( + TypePointer _type, + std::string const& _uniqueName, + smt::SolverInterface& _interface + ); + + /// Sets the var to false. + void setZeroValue(); + /// Does nothing since the SMT solver already knows the valid values for Bool. + void setUnknownValue(); + +protected: + smt::Expression valueAtIndex(int _index) const; +}; + +/** + * Specialization of SymbolicVariable for Integers + */ +class SymbolicIntVariable: public SymbolicVariable +{ +public: + SymbolicIntVariable( + TypePointer _type, + std::string const& _uniqueName, + smt::SolverInterface& _interface + ); + + /// Sets the var to 0. + void setZeroValue(); + /// Sets the variable to the full valid value range. + void setUnknownValue(); + +protected: + smt::Expression valueAtIndex(int _index) const; +}; + +/** + * Specialization of SymbolicVariable for Address + */ +class SymbolicAddressVariable: public SymbolicIntVariable +{ +public: + SymbolicAddressVariable( + std::string const& _uniqueName, + smt::SolverInterface& _interface + ); +}; + +/** + * Specialization of SymbolicVariable for FixedBytes + */ +class SymbolicFixedBytesVariable: public SymbolicIntVariable +{ +public: + SymbolicFixedBytesVariable( + unsigned _numBytes, + std::string const& _uniqueName, + smt::SolverInterface& _interface + ); +}; + +} +} diff --git a/libsolidity/formal/VariableUsage.cpp b/libsolidity/formal/VariableUsage.cpp index c2dea844..9282a560 100644 --- a/libsolidity/formal/VariableUsage.cpp +++ b/libsolidity/formal/VariableUsage.cpp @@ -50,12 +50,12 @@ VariableUsage::VariableUsage(ASTNode const& _node) _node.accept(reducer); } -vector<Declaration const*> VariableUsage::touchedVariables(ASTNode const& _node) const +vector<VariableDeclaration const*> VariableUsage::touchedVariables(ASTNode const& _node) const { if (!m_children.count(&_node) && !m_touchedVariable.count(&_node)) return {}; - set<Declaration const*> touched; + set<VariableDeclaration const*> touched; vector<ASTNode const*> toVisit; toVisit.push_back(&_node); diff --git a/libsolidity/formal/VariableUsage.h b/libsolidity/formal/VariableUsage.h index 62561cce..dda13de2 100644 --- a/libsolidity/formal/VariableUsage.h +++ b/libsolidity/formal/VariableUsage.h @@ -27,7 +27,7 @@ namespace solidity { class ASTNode; -class Declaration; +class VariableDeclaration; /** * This class collects information about which local variables of value type @@ -38,11 +38,11 @@ class VariableUsage public: explicit VariableUsage(ASTNode const& _node); - std::vector<Declaration const*> touchedVariables(ASTNode const& _node) const; + std::vector<VariableDeclaration const*> touchedVariables(ASTNode const& _node) const; private: // Variable touched by a specific AST node. - std::map<ASTNode const*, Declaration const*> m_touchedVariable; + std::map<ASTNode const*, VariableDeclaration const*> m_touchedVariable; std::map<ASTNode const*, std::vector<ASTNode const*>> m_children; }; diff --git a/libsolidity/formal/Z3Interface.cpp b/libsolidity/formal/Z3Interface.cpp index 41943c92..9a0ccf48 100644 --- a/libsolidity/formal/Z3Interface.cpp +++ b/libsolidity/formal/Z3Interface.cpp @@ -28,7 +28,10 @@ using namespace dev::solidity::smt; Z3Interface::Z3Interface(): m_solver(m_context) { + // This needs to be set globally. z3::set_param("rewriter.pull_cheap_ite", true); + // This needs to be set in the context. + m_context.set("timeout", queryTimeout); } void Z3Interface::reset() @@ -48,22 +51,22 @@ void Z3Interface::pop() m_solver.pop(); } -Expression Z3Interface::newFunction(string _name, Sort _domain, Sort _codomain) +void Z3Interface::declareFunction(string _name, Sort _domain, Sort _codomain) { - m_functions.insert({_name, m_context.function(_name.c_str(), z3Sort(_domain), z3Sort(_codomain))}); - return SolverInterface::newFunction(move(_name), _domain, _codomain); + if (!m_functions.count(_name)) + m_functions.insert({_name, m_context.function(_name.c_str(), z3Sort(_domain), z3Sort(_codomain))}); } -Expression Z3Interface::newInteger(string _name) +void Z3Interface::declareInteger(string _name) { - m_constants.insert({_name, m_context.int_const(_name.c_str())}); - return SolverInterface::newInteger(move(_name)); + if (!m_constants.count(_name)) + m_constants.insert({_name, m_context.int_const(_name.c_str())}); } -Expression Z3Interface::newBool(string _name) +void Z3Interface::declareBool(string _name) { - m_constants.insert({_name, m_context.bool_const(_name.c_str())}); - return SolverInterface::newBool(std::move(_name)); + if (!m_constants.count(_name)) + m_constants.insert({_name, m_context.bool_const(_name.c_str())}); } void Z3Interface::addAssertion(Expression const& _expr) @@ -88,11 +91,9 @@ pair<CheckResult, vector<string>> Z3Interface::check(vector<Expression> const& _ case z3::check_result::unknown: result = CheckResult::UNKNOWN; break; - default: - solAssert(false, ""); } - if (result != CheckResult::UNSATISFIABLE && !_expressionsToEvaluate.empty()) + if (result == CheckResult::SATISFIABLE && !_expressionsToEvaluate.empty()) { z3::model m = m_solver.get_model(); for (Expression const& e: _expressionsToEvaluate) diff --git a/libsolidity/formal/Z3Interface.h b/libsolidity/formal/Z3Interface.h index 354ded25..84880ff3 100644 --- a/libsolidity/formal/Z3Interface.h +++ b/libsolidity/formal/Z3Interface.h @@ -40,9 +40,9 @@ public: void push() override; void pop() override; - Expression newFunction(std::string _name, Sort _domain, Sort _codomain) override; - Expression newInteger(std::string _name) override; - Expression newBool(std::string _name) override; + void declareFunction(std::string _name, Sort _domain, Sort _codomain) override; + void declareInteger(std::string _name) override; + void declareBool(std::string _name) override; void addAssertion(Expression const& _expr) override; std::pair<CheckResult, std::vector<std::string>> check(std::vector<Expression> const& _expressionsToEvaluate) override; diff --git a/libsolidity/inlineasm/AsmAnalysis.cpp b/libsolidity/inlineasm/AsmAnalysis.cpp index 9f505889..ac019c06 100644 --- a/libsolidity/inlineasm/AsmAnalysis.cpp +++ b/libsolidity/inlineasm/AsmAnalysis.cpp @@ -57,7 +57,7 @@ bool AsmAnalyzer::operator()(Label const& _label) solAssert(!_label.name.empty(), ""); checkLooseFeature( _label.location, - "The use of labels is deprecated. Please use \"if\", \"switch\", \"for\" or function calls instead." + "The use of labels is disallowed. Please use \"if\", \"switch\", \"for\" or function calls instead." ); m_info.stackHeightInfo[&_label] = m_stackHeight; warnOnInstructions(solidity::Instruction::JUMPDEST, _label.location); @@ -68,7 +68,7 @@ bool AsmAnalyzer::operator()(assembly::Instruction const& _instruction) { checkLooseFeature( _instruction.location, - "The use of non-functional instructions is deprecated. Please use functional notation instead." + "The use of non-functional instructions is disallowed. Please use functional notation instead." ); auto const& info = instructionInfo(_instruction.instruction); m_stackHeight += info.ret - info.args; @@ -79,17 +79,17 @@ bool AsmAnalyzer::operator()(assembly::Instruction const& _instruction) bool AsmAnalyzer::operator()(assembly::Literal const& _literal) { - expectValidType(_literal.type, _literal.location); + expectValidType(_literal.type.str(), _literal.location); ++m_stackHeight; - if (_literal.kind == assembly::LiteralKind::String && _literal.value.size() > 32) + if (_literal.kind == assembly::LiteralKind::String && _literal.value.str().size() > 32) { m_errorReporter.typeError( _literal.location, - "String literal too long (" + boost::lexical_cast<std::string>(_literal.value.size()) + " > 32)" + "String literal too long (" + to_string(_literal.value.str().size()) + " > 32)" ); return false; } - else if (_literal.kind == assembly::LiteralKind::Number && bigint(_literal.value) > u256(-1)) + else if (_literal.kind == assembly::LiteralKind::Number && bigint(_literal.value.str()) > u256(-1)) { m_errorReporter.typeError( _literal.location, @@ -99,8 +99,8 @@ bool AsmAnalyzer::operator()(assembly::Literal const& _literal) } else if (_literal.kind == assembly::LiteralKind::Boolean) { - solAssert(m_flavour == AsmFlavour::IULIA, ""); - solAssert(_literal.value == "true" || _literal.value == "false", ""); + solAssert(m_flavour == AsmFlavour::Yul, ""); + solAssert(_literal.value == YulString{string("true")} || _literal.value == YulString{string("false")}, ""); } m_info.stackHeightInfo[&_literal] = m_stackHeight; return true; @@ -118,7 +118,7 @@ bool AsmAnalyzer::operator()(assembly::Identifier const& _identifier) { m_errorReporter.declarationError( _identifier.location, - "Variable " + _identifier.name + " used before it was declared." + "Variable " + _identifier.name.str() + " used before it was declared." ); success = false; } @@ -132,7 +132,7 @@ bool AsmAnalyzer::operator()(assembly::Identifier const& _identifier) { m_errorReporter.typeError( _identifier.location, - "Function " + _identifier.name + " used without being called." + "Function " + _identifier.name.str() + " used without being called." ); success = false; } @@ -145,7 +145,7 @@ bool AsmAnalyzer::operator()(assembly::Identifier const& _identifier) if (m_resolver) { bool insideFunction = m_currentScope->insideFunction(); - stackSize = m_resolver(_identifier, julia::IdentifierContext::RValue, insideFunction); + stackSize = m_resolver(_identifier, yul::IdentifierContext::RValue, insideFunction); } if (stackSize == size_t(-1)) { @@ -162,7 +162,7 @@ bool AsmAnalyzer::operator()(assembly::Identifier const& _identifier) bool AsmAnalyzer::operator()(FunctionalInstruction const& _instr) { - solAssert(m_flavour != AsmFlavour::IULIA, ""); + solAssert(m_flavour != AsmFlavour::Yul, ""); bool success = true; for (auto const& arg: _instr.arguments | boost::adaptors::reversed) if (!expectExpression(arg)) @@ -185,7 +185,7 @@ bool AsmAnalyzer::operator()(assembly::ExpressionStatement const& _statement) Error::Type errorType = m_flavour == AsmFlavour::Loose ? *m_errorTypeForLoose : Error::Type::TypeError; string msg = "Top-level expressions are not supposed to return values (this expression returns " + - boost::lexical_cast<string>(m_stackHeight - initialStackHeight) + + to_string(m_stackHeight - initialStackHeight) + " value" + (m_stackHeight - initialStackHeight == 1 ? "" : "s") + "). Use ``pop()`` or assign them."; @@ -201,7 +201,7 @@ bool AsmAnalyzer::operator()(assembly::StackAssignment const& _assignment) { checkLooseFeature( _assignment.location, - "The use of stack assignment is deprecated. Please use assignment in functional notation instead." + "The use of stack assignment is disallowed. Please use assignment in functional notation instead." ); bool success = checkAssignment(_assignment.variableName, size_t(-1)); m_info.stackHeightInfo[&_assignment] = m_stackHeight; @@ -253,7 +253,7 @@ bool AsmAnalyzer::operator()(assembly::VariableDeclaration const& _varDecl) for (auto const& variable: _varDecl.variables) { - expectValidType(variable.type, variable.location); + expectValidType(variable.type.str(), variable.location); m_activeVariables.insert(&boost::get<Scope::Variable>(m_currentScope->identifiers.at(variable.name))); } m_info.stackHeightInfo[&_varDecl] = m_stackHeight; @@ -268,7 +268,7 @@ bool AsmAnalyzer::operator()(assembly::FunctionDefinition const& _funDef) Scope& varScope = scope(virtualBlock); for (auto const& var: _funDef.parameters + _funDef.returnVariables) { - expectValidType(var.type, var.location); + expectValidType(var.type.str(), var.location); m_activeVariables.insert(&boost::get<Scope::Variable>(varScope.identifiers.at(var.name))); } @@ -322,8 +322,8 @@ bool AsmAnalyzer::operator()(assembly::FunctionCall const& _funCall) { m_errorReporter.typeError( _funCall.functionName.location, - "Expected " + boost::lexical_cast<string>(arguments) + " arguments but got " + - boost::lexical_cast<string>(_funCall.arguments.size()) + "." + "Expected " + to_string(arguments) + " arguments but got " + + to_string(_funCall.arguments.size()) + "." ); success = false; } @@ -361,7 +361,7 @@ bool AsmAnalyzer::operator()(Switch const& _switch) if (!expectExpression(*_switch.expression)) success = false; - set<tuple<LiteralKind, string>> cases; + set<tuple<LiteralKind, YulString>> cases; for (auto const& _case: _switch.cases) { if (_case.value) @@ -477,7 +477,7 @@ bool AsmAnalyzer::expectDeposit(int _deposit, int _oldHeight, SourceLocation con m_errorReporter.typeError( _location, "Expected expression to return one item to the stack, but did return " + - boost::lexical_cast<string>(m_stackHeight - _oldHeight) + + to_string(m_stackHeight - _oldHeight) + " items." ); return false; @@ -503,7 +503,7 @@ bool AsmAnalyzer::checkAssignment(assembly::Identifier const& _variable, size_t { m_errorReporter.declarationError( _variable.location, - "Variable " + _variable.name + " used before it was declared." + "Variable " + _variable.name.str() + " used before it was declared." ); success = false; } @@ -512,7 +512,7 @@ bool AsmAnalyzer::checkAssignment(assembly::Identifier const& _variable, size_t else if (m_resolver) { bool insideFunction = m_currentScope->insideFunction(); - variableSize = m_resolver(_variable, julia::IdentifierContext::LValue, insideFunction); + variableSize = m_resolver(_variable, yul::IdentifierContext::LValue, insideFunction); } if (variableSize == size_t(-1)) { @@ -550,7 +550,7 @@ Scope& AsmAnalyzer::scope(Block const* _block) } void AsmAnalyzer::expectValidType(string const& type, SourceLocation const& _location) { - if (m_flavour != AsmFlavour::IULIA) + if (m_flavour != AsmFlavour::Yul) return; if (!builtinTypes.count(type)) @@ -565,8 +565,10 @@ void AsmAnalyzer::warnOnInstructions(solidity::Instruction _instr, SourceLocatio // We assume that returndatacopy, returndatasize and staticcall are either all available // or all not available. solAssert(m_evmVersion.supportsReturndata() == m_evmVersion.hasStaticCall(), ""); + // Similarly we assume bitwise shifting and create2 go together. + solAssert(m_evmVersion.hasBitwiseShifting() == m_evmVersion.hasCreate2(), ""); - if (_instr == solidity::Instruction::CREATE2) + if (_instr == solidity::Instruction::EXTCODEHASH) m_errorReporter.warning( _location, "The \"" + @@ -593,7 +595,8 @@ void AsmAnalyzer::warnOnInstructions(solidity::Instruction _instr, SourceLocatio else if (( _instr == solidity::Instruction::SHL || _instr == solidity::Instruction::SHR || - _instr == solidity::Instruction::SAR + _instr == solidity::Instruction::SAR || + _instr == solidity::Instruction::CREATE2 ) && !m_evmVersion.hasBitwiseShifting()) m_errorReporter.warning( _location, diff --git a/libsolidity/inlineasm/AsmAnalysis.h b/libsolidity/inlineasm/AsmAnalysis.h index 8d2a71f0..a8673efa 100644 --- a/libsolidity/inlineasm/AsmAnalysis.h +++ b/libsolidity/inlineasm/AsmAnalysis.h @@ -25,7 +25,7 @@ #include <libsolidity/inlineasm/AsmScope.h> -#include <libjulia/backends/evm/AbstractAssembly.h> +#include <libyul/backends/evm/AbstractAssembly.h> #include <libsolidity/inlineasm/AsmDataForward.h> @@ -59,7 +59,7 @@ public: EVMVersion _evmVersion, boost::optional<Error::Type> _errorTypeForLoose, AsmFlavour _flavour = AsmFlavour::Loose, - julia::ExternalIdentifierAccess::Resolver const& _resolver = julia::ExternalIdentifierAccess::Resolver() + yul::ExternalIdentifierAccess::Resolver const& _resolver = yul::ExternalIdentifierAccess::Resolver() ): m_resolver(_resolver), m_info(_analysisInfo), @@ -106,7 +106,7 @@ private: void checkLooseFeature(SourceLocation const& _location, std::string const& _description); int m_stackHeight = 0; - julia::ExternalIdentifierAccess::Resolver m_resolver; + yul::ExternalIdentifierAccess::Resolver m_resolver; Scope* m_currentScope = nullptr; /// Variables that are active at the current point in assembly (as opposed to /// "part of the scope but not yet declared") diff --git a/libsolidity/inlineasm/AsmCodeGen.cpp b/libsolidity/inlineasm/AsmCodeGen.cpp index dded9f76..3a62b232 100644 --- a/libsolidity/inlineasm/AsmCodeGen.cpp +++ b/libsolidity/inlineasm/AsmCodeGen.cpp @@ -32,8 +32,8 @@ #include <libevmasm/SourceLocation.h> #include <libevmasm/Instruction.h> -#include <libjulia/backends/evm/AbstractAssembly.h> -#include <libjulia/backends/evm/EVMCodeTransform.h> +#include <libyul/backends/evm/AbstractAssembly.h> +#include <libyul/backends/evm/EVMCodeTransform.h> #include <libdevcore/CommonIO.h> @@ -49,7 +49,7 @@ using namespace dev; using namespace dev::solidity; using namespace dev::solidity::assembly; -class EthAssemblyAdapter: public julia::AbstractAssembly +class EthAssemblyAdapter: public yul::AbstractAssembly { public: explicit EthAssemblyAdapter(eth::Assembly& _assembly): @@ -145,12 +145,12 @@ void assembly::CodeGenerator::assemble( Block const& _parsedData, AsmAnalysisInfo& _analysisInfo, eth::Assembly& _assembly, - julia::ExternalIdentifierAccess const& _identifierAccess, + yul::ExternalIdentifierAccess const& _identifierAccess, bool _useNamedLabelsForFunctions ) { EthAssemblyAdapter assemblyAdapter(_assembly); - julia::CodeTransform( + yul::CodeTransform( assemblyAdapter, _analysisInfo, false, diff --git a/libsolidity/inlineasm/AsmCodeGen.h b/libsolidity/inlineasm/AsmCodeGen.h index a7d7ead1..bbc31397 100644 --- a/libsolidity/inlineasm/AsmCodeGen.h +++ b/libsolidity/inlineasm/AsmCodeGen.h @@ -41,12 +41,12 @@ struct Block; class CodeGenerator { public: - /// Performs code generation and appends generated to to _assembly. + /// Performs code generation and appends generated to _assembly. static void assemble( Block const& _parsedData, AsmAnalysisInfo& _analysisInfo, eth::Assembly& _assembly, - julia::ExternalIdentifierAccess const& _identifierAccess = julia::ExternalIdentifierAccess(), + yul::ExternalIdentifierAccess const& _identifierAccess = yul::ExternalIdentifierAccess(), bool _useNamedLabelsForFunctions = false ); }; diff --git a/libsolidity/inlineasm/AsmData.h b/libsolidity/inlineasm/AsmData.h index 2982d5e0..a8d5e327 100644 --- a/libsolidity/inlineasm/AsmData.h +++ b/libsolidity/inlineasm/AsmData.h @@ -27,7 +27,13 @@ #include <libevmasm/Instruction.h> #include <libevmasm/SourceLocation.h> +#include <libyul/YulString.h> + #include <boost/variant.hpp> +#include <boost/noncopyable.hpp> + +#include <map> +#include <memory> namespace dev { @@ -36,20 +42,21 @@ namespace solidity namespace assembly { -using Type = std::string; +using YulString = dev::yul::YulString; +using Type = YulString; -struct TypedName { SourceLocation location; std::string name; Type type; }; +struct TypedName { SourceLocation location; YulString name; Type type; }; using TypedNameList = std::vector<TypedName>; /// Direct EVM instruction (except PUSHi and JUMPDEST) struct Instruction { SourceLocation location; solidity::Instruction instruction; }; /// Literal number or string (up to 32 bytes) enum class LiteralKind { Number, Boolean, String }; -struct Literal { SourceLocation location; LiteralKind kind; std::string value; Type type; }; +struct Literal { SourceLocation location; LiteralKind kind; YulString value; Type type; }; /// External / internal identifier or label reference -struct Identifier { SourceLocation location; std::string name; }; +struct Identifier { SourceLocation location; YulString name; }; /// Jump label ("name:") -struct Label { SourceLocation location; std::string name; }; +struct Label { SourceLocation location; YulString name; }; /// Assignment from stack (":= x", moves stack top into x, potentially multiple slots) struct StackAssignment { SourceLocation location; Identifier variableName; }; /// Assignment ("x := mload(20:u256)", expects push-1-expression on the right hand @@ -69,7 +76,7 @@ struct VariableDeclaration { SourceLocation location; TypedNameList variables; s /// Block that creates a scope (frees declared stack variables) struct Block { SourceLocation location; std::vector<Statement> statements; }; /// Function definition ("function f(a, b) -> (d, e) { ... }") -struct FunctionDefinition { SourceLocation location; std::string name; TypedNameList parameters; TypedNameList returnVariables; Block body; }; +struct FunctionDefinition { SourceLocation location; YulString name; TypedNameList parameters; TypedNameList returnVariables; Block body; }; /// Conditional execution without "else" part. struct If { SourceLocation location; std::shared_ptr<Expression> condition; Block body; }; /// Switch case or default case diff --git a/libsolidity/inlineasm/AsmDataForward.h b/libsolidity/inlineasm/AsmDataForward.h index 3a9600fe..69cf8f1d 100644 --- a/libsolidity/inlineasm/AsmDataForward.h +++ b/libsolidity/inlineasm/AsmDataForward.h @@ -17,7 +17,7 @@ /** * @author Christian <c@ethdev.com> * @date 2016 - * Forward declaration of classes for inline assembly / JULIA AST + * Forward declaration of classes for inline assembly / Yul AST */ #pragma once @@ -57,7 +57,7 @@ enum class AsmFlavour { Loose, // no types, EVM instructions as function, jumps and direct stack manipulations Strict, // no types, EVM instructions as functions, but no jumps and no direct stack manipulations - IULIA // same as Strict mode with types + Yul // same as Strict mode with types }; } diff --git a/libsolidity/inlineasm/AsmParser.cpp b/libsolidity/inlineasm/AsmParser.cpp index d3b0808b..1f399edc 100644 --- a/libsolidity/inlineasm/AsmParser.cpp +++ b/libsolidity/inlineasm/AsmParser.cpp @@ -26,7 +26,7 @@ #include <boost/algorithm/string.hpp> -#include <ctype.h> +#include <cctype> #include <algorithm> using namespace std; @@ -97,7 +97,7 @@ assembly::Statement Parser::parseStatement() fatalParserError("Only one default case allowed."); else if (m_scanner->currentToken() == Token::Case) fatalParserError("Case not allowed after default case."); - if (_switch.cases.size() == 0) + if (_switch.cases.empty()) fatalParserError("Switch statement without any cases."); _switch.location.end = _switch.cases.back().body.location.end; return _switch; @@ -112,8 +112,8 @@ assembly::Statement Parser::parseStatement() advance(); expectToken(Token::Colon); assignment.variableName.location = location(); - assignment.variableName.name = currentLiteral(); - if (instructions().count(assignment.variableName.name)) + assignment.variableName.name = YulString(currentLiteral()); + if (instructions().count(assignment.variableName.name.str())) fatalParserError("Identifier expected, got instruction name."); assignment.location.end = endPosition(); expectToken(Token::Identifier); @@ -150,7 +150,7 @@ assembly::Statement Parser::parseStatement() expectToken(Token::Comma); elementary = parseElementaryOperation(); if (elementary.type() != typeid(assembly::Identifier)) - fatalParserError("Variable name expected in multiple assignemnt."); + fatalParserError("Variable name expected in multiple assignment."); assignment.variableNames.emplace_back(boost::get<assembly::Identifier>(elementary)); } while (currentToken() == Token::Comma); @@ -173,7 +173,7 @@ assembly::Statement Parser::parseStatement() if (currentToken() == Token::Assign && peekNextToken() != Token::Colon) { assembly::Assignment assignment = createWithLocation<assembly::Assignment>(identifier.location); - if (m_flavour != AsmFlavour::IULIA && instructions().count(identifier.name)) + if (m_flavour != AsmFlavour::Yul && instructions().count(identifier.name.str())) fatalParserError("Cannot use instruction names for identifier names."); advance(); assignment.variableNames.emplace_back(identifier); @@ -279,7 +279,7 @@ assembly::Expression Parser::parseExpression() "Expected '(' (instruction \"" + instructionNames().at(instr.instruction) + "\" expects " + - boost::lexical_cast<string>(args) + + to_string(args) + " arguments)" )); } @@ -318,11 +318,6 @@ std::map<string, dev::solidity::Instruction> const& Parser::instructions() transform(name.begin(), name.end(), name.begin(), [](unsigned char _c) { return tolower(_c); }); s_instructions[name] = instruction.second; } - - // add alias for suicide - s_instructions["suicide"] = solidity::Instruction::SELFDESTRUCT; - // add alis for sha3 - s_instructions["sha3"] = solidity::Instruction::KECCAK256; } return s_instructions; } @@ -362,13 +357,13 @@ Parser::ElementaryOperation Parser::parseElementaryOperation() else literal = currentLiteral(); // first search the set of instructions. - if (m_flavour != AsmFlavour::IULIA && instructions().count(literal)) + if (m_flavour != AsmFlavour::Yul && instructions().count(literal)) { dev::solidity::Instruction const& instr = instructions().at(literal); ret = Instruction{location(), instr}; } else - ret = Identifier{location(), literal}; + ret = Identifier{location(), YulString{literal}}; advance(); break; } @@ -399,15 +394,15 @@ Parser::ElementaryOperation Parser::parseElementaryOperation() Literal literal{ location(), kind, - currentLiteral(), - "" + YulString{currentLiteral()}, + {} }; advance(); - if (m_flavour == AsmFlavour::IULIA) + if (m_flavour == AsmFlavour::Yul) { expectToken(Token::Colon); literal.location.end = endPosition(); - literal.type = expectAsmIdentifier(); + literal.type = YulString{expectAsmIdentifier()}; } else if (kind == LiteralKind::Boolean) fatalParserError("True and false are not valid literals."); @@ -416,7 +411,7 @@ Parser::ElementaryOperation Parser::parseElementaryOperation() } default: fatalParserError( - m_flavour == AsmFlavour::IULIA ? + m_flavour == AsmFlavour::Yul ? "Literal or identifier expected." : "Literal, identifier or instruction expected." ); @@ -454,7 +449,7 @@ assembly::FunctionDefinition Parser::parseFunctionDefinition() RecursionGuard recursionGuard(*this); FunctionDefinition funDef = createWithLocation<FunctionDefinition>(); expectToken(Token::Function); - funDef.name = expectAsmIdentifier(); + funDef.name = YulString{expectAsmIdentifier()}; expectToken(Token::LParen); while (currentToken() != Token::RParen) { @@ -486,7 +481,7 @@ assembly::Expression Parser::parseCall(Parser::ElementaryOperation&& _initialOp) RecursionGuard recursionGuard(*this); if (_initialOp.type() == typeid(Instruction)) { - solAssert(m_flavour != AsmFlavour::IULIA, "Instructions are invalid in JULIA"); + solAssert(m_flavour != AsmFlavour::Yul, "Instructions are invalid in Yul"); Instruction& instruction = boost::get<Instruction>(_initialOp); FunctionalInstruction ret; ret.instruction = instruction.instruction; @@ -507,7 +502,7 @@ assembly::Expression Parser::parseCall(Parser::ElementaryOperation&& _initialOp) "Expected expression (instruction \"" + instructionNames().at(instr) + "\" expects " + - boost::lexical_cast<string>(args) + + to_string(args) + " arguments)" )); @@ -519,7 +514,7 @@ assembly::Expression Parser::parseCall(Parser::ElementaryOperation&& _initialOp) "Expected ',' (instruction \"" + instructionNames().at(instr) + "\" expects " + - boost::lexical_cast<string>(args) + + to_string(args) + " arguments)" )); else @@ -532,7 +527,7 @@ assembly::Expression Parser::parseCall(Parser::ElementaryOperation&& _initialOp) "Expected ')' (instruction \"" + instructionNames().at(instr) + "\" expects " + - boost::lexical_cast<string>(args) + + to_string(args) + " arguments)" )); expectToken(Token::RParen); @@ -557,7 +552,7 @@ assembly::Expression Parser::parseCall(Parser::ElementaryOperation&& _initialOp) } else fatalParserError( - m_flavour == AsmFlavour::IULIA ? + m_flavour == AsmFlavour::Yul ? "Function name expected." : "Assembly instruction or function name required in front of \"(\")" ); @@ -569,12 +564,12 @@ TypedName Parser::parseTypedName() { RecursionGuard recursionGuard(*this); TypedName typedName = createWithLocation<TypedName>(); - typedName.name = expectAsmIdentifier(); - if (m_flavour == AsmFlavour::IULIA) + typedName.name = YulString{expectAsmIdentifier()}; + if (m_flavour == AsmFlavour::Yul) { expectToken(Token::Colon); typedName.location.end = endPosition(); - typedName.type = expectAsmIdentifier(); + typedName.type = YulString{expectAsmIdentifier()}; } return typedName; } @@ -582,7 +577,7 @@ TypedName Parser::parseTypedName() string Parser::expectAsmIdentifier() { string name = currentLiteral(); - if (m_flavour == AsmFlavour::IULIA) + if (m_flavour == AsmFlavour::Yul) { switch (currentToken()) { @@ -606,7 +601,9 @@ bool Parser::isValidNumberLiteral(string const& _literal) { try { - u256(_literal); + // Try to convert _literal to u256. + auto tmp = u256(_literal); + (void) tmp; } catch (...) { diff --git a/libsolidity/inlineasm/AsmPrinter.cpp b/libsolidity/inlineasm/AsmPrinter.cpp index bacd7a94..ae0bd1eb 100644 --- a/libsolidity/inlineasm/AsmPrinter.cpp +++ b/libsolidity/inlineasm/AsmPrinter.cpp @@ -24,6 +24,8 @@ #include <libsolidity/inlineasm/AsmData.h> #include <libsolidity/interface/Exceptions.h> +#include <libdevcore/CommonData.h> + #include <boost/algorithm/string.hpp> #include <boost/algorithm/string/replace.hpp> #include <boost/range/adaptor/transformed.hpp> @@ -40,7 +42,8 @@ using namespace dev::solidity::assembly; string AsmPrinter::operator()(assembly::Instruction const& _instruction) { - solAssert(!m_julia, ""); + solAssert(!m_yul, ""); + solAssert(isValidInstruction(_instruction.instruction), "Invalid instruction"); return boost::to_lower_copy(instructionInfo(_instruction.instruction).name); } @@ -49,15 +52,17 @@ string AsmPrinter::operator()(assembly::Literal const& _literal) switch (_literal.kind) { case LiteralKind::Number: - return _literal.value + appendTypeName(_literal.type); + solAssert(isValidDecimal(_literal.value.str()) || isValidHex(_literal.value.str()), "Invalid number literal"); + return _literal.value.str() + appendTypeName(_literal.type); case LiteralKind::Boolean: - return ((_literal.value == "true") ? "true" : "false") + appendTypeName(_literal.type); + solAssert(_literal.value.str() == "true" || _literal.value.str() == "false", "Invalid bool literal."); + return ((_literal.value.str() == "true") ? "true" : "false") + appendTypeName(_literal.type); case LiteralKind::String: break; } string out; - for (char c: _literal.value) + for (char c: _literal.value.str()) if (c == '\\') out += "\\\\"; else if (c == '"') @@ -87,18 +92,20 @@ string AsmPrinter::operator()(assembly::Literal const& _literal) string AsmPrinter::operator()(assembly::Identifier const& _identifier) { - return _identifier.name; + solAssert(!_identifier.name.empty(), "Invalid identifier."); + return _identifier.name.str(); } string AsmPrinter::operator()(assembly::FunctionalInstruction const& _functionalInstruction) { - solAssert(!m_julia, ""); + solAssert(!m_yul, ""); + solAssert(isValidInstruction(_functionalInstruction.instruction), "Invalid instruction"); return boost::to_lower_copy(instructionInfo(_functionalInstruction.instruction).name) + "(" + boost::algorithm::join( _functionalInstruction.arguments | boost::adaptors::transformed(boost::apply_visitor(*this)), - ", " ) + + ", ") + ")"; } @@ -109,13 +116,15 @@ string AsmPrinter::operator()(ExpressionStatement const& _statement) string AsmPrinter::operator()(assembly::Label const& _label) { - solAssert(!m_julia, ""); - return _label.name + ":"; + solAssert(!m_yul, ""); + solAssert(!_label.name.empty(), "Invalid label."); + return _label.name.str() + ":"; } string AsmPrinter::operator()(assembly::StackAssignment const& _assignment) { - solAssert(!m_julia, ""); + solAssert(!m_yul, ""); + solAssert(!_assignment.variableName.name.empty(), "Invalid variable name."); return "=: " + (*this)(_assignment.variableName); } @@ -133,7 +142,7 @@ string AsmPrinter::operator()(assembly::VariableDeclaration const& _variableDecl string out = "let "; out += boost::algorithm::join( _variableDeclaration.variables | boost::adaptors::transformed( - [this](TypedName variable) { return variable.name + appendTypeName(variable.type); } + [this](TypedName argument) { return formatTypedName(argument); } ), ", " ); @@ -147,10 +156,11 @@ string AsmPrinter::operator()(assembly::VariableDeclaration const& _variableDecl string AsmPrinter::operator()(assembly::FunctionDefinition const& _functionDefinition) { - string out = "function " + _functionDefinition.name + "("; + solAssert(!_functionDefinition.name.empty(), "Invalid function name."); + string out = "function " + _functionDefinition.name.str() + "("; out += boost::algorithm::join( _functionDefinition.parameters | boost::adaptors::transformed( - [this](TypedName argument) { return argument.name + appendTypeName(argument.type); } + [this](TypedName argument) { return formatTypedName(argument); } ), ", " ); @@ -160,7 +170,7 @@ string AsmPrinter::operator()(assembly::FunctionDefinition const& _functionDefin out += " -> "; out += boost::algorithm::join( _functionDefinition.returnVariables | boost::adaptors::transformed( - [this](TypedName argument) { return argument.name + appendTypeName(argument.type); } + [this](TypedName argument) { return formatTypedName(argument); } ), ", " ); @@ -181,11 +191,13 @@ string AsmPrinter::operator()(assembly::FunctionCall const& _functionCall) string AsmPrinter::operator()(If const& _if) { + solAssert(_if.condition, "Invalid if condition."); return "if " + boost::apply_visitor(*this, *_if.condition) + "\n" + (*this)(_if.body); } string AsmPrinter::operator()(Switch const& _switch) { + solAssert(_switch.expression, "Invalid expression pointer."); string out = "switch " + boost::apply_visitor(*this, *_switch.expression); for (auto const& _case: _switch.cases) { @@ -200,6 +212,7 @@ string AsmPrinter::operator()(Switch const& _switch) string AsmPrinter::operator()(assembly::ForLoop const& _forLoop) { + solAssert(_forLoop.condition, "Invalid for loop condition."); string out = "for "; out += (*this)(_forLoop.pre); out += "\n"; @@ -223,9 +236,15 @@ string AsmPrinter::operator()(Block const& _block) return "{\n " + body + "\n}"; } -string AsmPrinter::appendTypeName(std::string const& _type) const +string AsmPrinter::formatTypedName(TypedName _variable) const +{ + solAssert(!_variable.name.empty(), "Invalid variable name."); + return _variable.name.str() + appendTypeName(_variable.type); +} + +string AsmPrinter::appendTypeName(YulString _type) const { - if (m_julia) - return ":" + _type; + if (m_yul) + return ":" + _type.str(); return ""; } diff --git a/libsolidity/inlineasm/AsmPrinter.h b/libsolidity/inlineasm/AsmPrinter.h index 5bd87aba..72048975 100644 --- a/libsolidity/inlineasm/AsmPrinter.h +++ b/libsolidity/inlineasm/AsmPrinter.h @@ -24,6 +24,8 @@ #include <libsolidity/inlineasm/AsmDataForward.h> +#include <libyul/YulString.h> + #include <boost/variant.hpp> namespace dev @@ -36,7 +38,7 @@ namespace assembly class AsmPrinter: public boost::static_visitor<std::string> { public: - explicit AsmPrinter(bool _julia = false): m_julia(_julia) {} + explicit AsmPrinter(bool _yul = false): m_yul(_yul) {} std::string operator()(assembly::Instruction const& _instruction); std::string operator()(assembly::Literal const& _literal); @@ -55,9 +57,10 @@ public: std::string operator()(assembly::Block const& _block); private: - std::string appendTypeName(std::string const& _type) const; + std::string formatTypedName(TypedName _variable) const; + std::string appendTypeName(yul::YulString _type) const; - bool m_julia = false; + bool m_yul = false; }; } diff --git a/libsolidity/inlineasm/AsmScope.cpp b/libsolidity/inlineasm/AsmScope.cpp index 64d5bd9a..10893b96 100644 --- a/libsolidity/inlineasm/AsmScope.cpp +++ b/libsolidity/inlineasm/AsmScope.cpp @@ -21,10 +21,10 @@ #include <libsolidity/inlineasm/AsmScope.h> using namespace std; +using namespace dev; using namespace dev::solidity::assembly; - -bool Scope::registerLabel(string const& _name) +bool Scope::registerLabel(yul::YulString _name) { if (exists(_name)) return false; @@ -32,7 +32,7 @@ bool Scope::registerLabel(string const& _name) return true; } -bool Scope::registerVariable(string const& _name, JuliaType const& _type) +bool Scope::registerVariable(yul::YulString _name, YulType const& _type) { if (exists(_name)) return false; @@ -42,7 +42,7 @@ bool Scope::registerVariable(string const& _name, JuliaType const& _type) return true; } -bool Scope::registerFunction(string const& _name, std::vector<JuliaType> const& _arguments, std::vector<JuliaType> const& _returns) +bool Scope::registerFunction(yul::YulString _name, std::vector<YulType> const& _arguments, std::vector<YulType> const& _returns) { if (exists(_name)) return false; @@ -50,7 +50,7 @@ bool Scope::registerFunction(string const& _name, std::vector<JuliaType> const& return true; } -Scope::Identifier* Scope::lookup(string const& _name) +Scope::Identifier* Scope::lookup(yul::YulString _name) { bool crossedFunctionBoundary = false; for (Scope* s = this; s; s = s->superScope) @@ -70,7 +70,7 @@ Scope::Identifier* Scope::lookup(string const& _name) return nullptr; } -bool Scope::exists(string const& _name) const +bool Scope::exists(yul::YulString _name) const { if (identifiers.count(_name)) return true; diff --git a/libsolidity/inlineasm/AsmScope.h b/libsolidity/inlineasm/AsmScope.h index 447d6490..65848018 100644 --- a/libsolidity/inlineasm/AsmScope.h +++ b/libsolidity/inlineasm/AsmScope.h @@ -22,6 +22,10 @@ #include <libsolidity/interface/Exceptions.h> +#include <libyul/YulString.h> + +#include <libdevcore/Visitor.h> + #include <boost/variant.hpp> #include <boost/optional.hpp> @@ -35,54 +39,29 @@ namespace solidity namespace assembly { -template <class...> -struct GenericVisitor{}; - -template <class Visitable, class... Others> -struct GenericVisitor<Visitable, Others...>: public GenericVisitor<Others...> -{ - using GenericVisitor<Others...>::operator (); - explicit GenericVisitor( - std::function<void(Visitable&)> _visitor, - std::function<void(Others&)>... _otherVisitors - ): - GenericVisitor<Others...>(_otherVisitors...), - m_visitor(_visitor) - {} - - void operator()(Visitable& _v) const { m_visitor(_v); } - - std::function<void(Visitable&)> m_visitor; -}; -template <> -struct GenericVisitor<>: public boost::static_visitor<> { - void operator()() const {} -}; - - struct Scope { - using JuliaType = std::string; + using YulType = yul::YulString; using LabelID = size_t; - struct Variable { JuliaType type; }; + struct Variable { YulType type; }; struct Label { }; struct Function { - std::vector<JuliaType> arguments; - std::vector<JuliaType> returns; + std::vector<YulType> arguments; + std::vector<YulType> returns; }; using Identifier = boost::variant<Variable, Label, Function>; using Visitor = GenericVisitor<Variable const, Label const, Function const>; using NonconstVisitor = GenericVisitor<Variable, Label, Function>; - bool registerVariable(std::string const& _name, JuliaType const& _type); - bool registerLabel(std::string const& _name); + bool registerVariable(yul::YulString _name, YulType const& _type); + bool registerLabel(yul::YulString _name); bool registerFunction( - std::string const& _name, - std::vector<JuliaType> const& _arguments, - std::vector<JuliaType> const& _returns + yul::YulString _name, + std::vector<YulType> const& _arguments, + std::vector<YulType> const& _returns ); /// Looks up the identifier in this or super scopes and returns a valid pointer if found @@ -90,12 +69,12 @@ struct Scope /// will any lookups across assembly boundaries. /// The pointer will be invalidated if the scope is modified. /// @param _crossedFunction if true, we already crossed a function boundary during recursive lookup - Identifier* lookup(std::string const& _name); + Identifier* lookup(yul::YulString _name); /// Looks up the identifier in this and super scopes (will not find variables across function /// boundaries and generally stops at assembly boundaries) and calls the visitor, returns /// false if not found. template <class V> - bool lookup(std::string const& _name, V const& _visitor) + bool lookup(yul::YulString _name, V const& _visitor) { if (Identifier* id = lookup(_name)) { @@ -107,7 +86,7 @@ struct Scope } /// @returns true if the name exists in this scope or in super scopes (also searches /// across function and assembly boundaries). - bool exists(std::string const& _name) const; + bool exists(yul::YulString _name) const; /// @returns the number of variables directly registered inside the scope. size_t numberOfVariables() const; @@ -118,7 +97,7 @@ struct Scope /// If true, variables from the super scope are not visible here (other identifiers are), /// but they are still taken into account to prevent shadowing. bool functionScope = false; - std::map<std::string, Identifier> identifiers; + std::map<yul::YulString, Identifier> identifiers; }; } diff --git a/libsolidity/inlineasm/AsmScopeFiller.cpp b/libsolidity/inlineasm/AsmScopeFiller.cpp index 86f3809c..d1f98083 100644 --- a/libsolidity/inlineasm/AsmScopeFiller.cpp +++ b/libsolidity/inlineasm/AsmScopeFiller.cpp @@ -57,7 +57,7 @@ bool ScopeFiller::operator()(Label const& _item) //@TODO secondary location m_errorReporter.declarationError( _item.location, - "Label name " + _item.name + " already taken in this scope." + "Label name " + _item.name.str() + " already taken in this scope." ); return false; } @@ -75,18 +75,18 @@ bool ScopeFiller::operator()(assembly::VariableDeclaration const& _varDecl) bool ScopeFiller::operator()(assembly::FunctionDefinition const& _funDef) { bool success = true; - vector<Scope::JuliaType> arguments; + vector<Scope::YulType> arguments; for (auto const& _argument: _funDef.parameters) - arguments.push_back(_argument.type); - vector<Scope::JuliaType> returns; + arguments.emplace_back(_argument.type.str()); + vector<Scope::YulType> returns; for (auto const& _return: _funDef.returnVariables) - returns.push_back(_return.type); + returns.emplace_back(_return.type.str()); if (!m_currentScope->registerFunction(_funDef.name, arguments, returns)) { //@TODO secondary location m_errorReporter.declarationError( _funDef.location, - "Function name " + _funDef.name + " already taken in this scope." + "Function name " + _funDef.name.str() + " already taken in this scope." ); success = false; } @@ -164,7 +164,7 @@ bool ScopeFiller::registerVariable(TypedName const& _name, SourceLocation const& //@TODO secondary location m_errorReporter.declarationError( _location, - "Variable name " + _name.name + " already taken in this scope." + "Variable name " + _name.name.str() + " already taken in this scope." ); return false; } diff --git a/libsolidity/interface/AssemblyStack.cpp b/libsolidity/interface/AssemblyStack.cpp index 7f97336b..26496de7 100644 --- a/libsolidity/interface/AssemblyStack.cpp +++ b/libsolidity/interface/AssemblyStack.cpp @@ -15,7 +15,7 @@ along with solidity. If not, see <http://www.gnu.org/licenses/>. */ /** - * Full assembly stack that can support EVM-assembly and JULIA as input and EVM, EVM1.5 and + * Full assembly stack that can support EVM-assembly and Yul as input and EVM, EVM1.5 and * eWasm as output. */ @@ -31,8 +31,8 @@ #include <libevmasm/Assembly.h> -#include <libjulia/backends/evm/EVMCodeTransform.h> -#include <libjulia/backends/evm/EVMAssembly.h> +#include <libyul/backends/evm/EVMCodeTransform.h> +#include <libyul/backends/evm/EVMAssembly.h> using namespace std; using namespace dev; @@ -48,11 +48,11 @@ assembly::AsmFlavour languageToAsmFlavour(AssemblyStack::Language _language) return assembly::AsmFlavour::Loose; case AssemblyStack::Language::StrictAssembly: return assembly::AsmFlavour::Strict; - case AssemblyStack::Language::JULIA: - return assembly::AsmFlavour::IULIA; + case AssemblyStack::Language::Yul: + return assembly::AsmFlavour::Yul; } solAssert(false, ""); - return assembly::AsmFlavour::IULIA; + return assembly::AsmFlavour::Yul; } } @@ -116,10 +116,10 @@ MachineAssemblyObject AssemblyStack::assemble(Machine _machine) const case Machine::EVM15: { MachineAssemblyObject object; - julia::EVMAssembly assembly(true); - julia::CodeTransform(assembly, *m_analysisInfo, m_language == Language::JULIA, true)(*m_parserResult); + yul::EVMAssembly assembly(true); + yul::CodeTransform(assembly, *m_analysisInfo, m_language == Language::Yul, true)(*m_parserResult); object.bytecode = make_shared<eth::LinkerObject>(assembly.finalize()); - /// TOOD: fill out text representation + /// TODO: fill out text representation return object; } case Machine::eWasm: @@ -132,5 +132,5 @@ MachineAssemblyObject AssemblyStack::assemble(Machine _machine) const string AssemblyStack::print() const { solAssert(m_parserResult, ""); - return assembly::AsmPrinter(m_language == Language::JULIA)(*m_parserResult); + return assembly::AsmPrinter(m_language == Language::Yul)(*m_parserResult); } diff --git a/libsolidity/interface/AssemblyStack.h b/libsolidity/interface/AssemblyStack.h index 720220ab..8132ce63 100644 --- a/libsolidity/interface/AssemblyStack.h +++ b/libsolidity/interface/AssemblyStack.h @@ -15,7 +15,7 @@ along with solidity. If not, see <http://www.gnu.org/licenses/>. */ /** - * Full assembly stack that can support EVM-assembly and JULIA as input and EVM, EVM1.5 and + * Full assembly stack that can support EVM-assembly and Yul as input and EVM, EVM1.5 and * eWasm as output. */ @@ -47,13 +47,13 @@ struct MachineAssemblyObject }; /* - * Full assembly stack that can support EVM-assembly and JULIA as input and EVM, EVM1.5 and + * Full assembly stack that can support EVM-assembly and Yul as input and EVM, EVM1.5 and * eWasm as output. */ class AssemblyStack { public: - enum class Language { JULIA, Assembly, StrictAssembly }; + enum class Language { Yul, Assembly, StrictAssembly }; enum class Machine { EVM, EVM15, eWasm }; explicit AssemblyStack(EVMVersion _evmVersion = EVMVersion(), Language _language = Language::Assembly): diff --git a/libsolidity/interface/CompilerStack.cpp b/libsolidity/interface/CompilerStack.cpp index 47dc30cf..441c7897 100644 --- a/libsolidity/interface/CompilerStack.cpp +++ b/libsolidity/interface/CompilerStack.cpp @@ -47,6 +47,8 @@ #include <libevmasm/Exceptions.h> +#include <libyul/YulString.h> + #include <libdevcore/SwarmHash.h> #include <libdevcore/JSON.h> @@ -58,22 +60,31 @@ using namespace std; using namespace dev; using namespace dev::solidity; -void CompilerStack::setRemappings(vector<string> const& _remappings) +boost::optional<CompilerStack::Remapping> CompilerStack::parseRemapping(string const& _remapping) +{ + auto eq = find(_remapping.begin(), _remapping.end(), '='); + if (eq == _remapping.end()) + return {}; + + auto colon = find(_remapping.begin(), eq, ':'); + + Remapping r; + + 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()); + + if (r.prefix.empty()) + return {}; + + return r; +} + +void CompilerStack::setRemappings(vector<Remapping> const& _remappings) { - vector<Remapping> remappings; for (auto const& remapping: _remappings) - { - auto eq = find(remapping.begin(), remapping.end(), '='); - if (eq == remapping.end()) - continue; // ignore - auto colon = find(remapping.begin(), eq, ':'); - Remapping r; - 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()); - remappings.push_back(r); - } - swap(m_remappings, remappings); + solAssert(!remapping.prefix.empty(), ""); + m_remappings = _remappings; } void CompilerStack::setEVMVersion(EVMVersion _version) @@ -119,7 +130,7 @@ bool CompilerStack::addSource(string const& _name, string const& _content, bool bool CompilerStack::parse() { //reset - if(m_stackState != SourcesSet) + if (m_stackState != SourcesSet) return false; m_errorReporter.clear(); ASTNode::resetID(); @@ -191,6 +202,8 @@ bool CompilerStack::analyze() if (!resolver.performImports(*source->ast, sourceUnitsByName)) return false; + // This is the main name and type resolution loop. Needs to be run for every contract, because + // the special variables "this" and "super" must be set appropriately. for (Source const* source: m_sourceOrder) for (ASTPointer<ASTNode> const& node: source->ast->nodes()) if (ContractDefinition* contract = dynamic_cast<ContractDefinition*>(node.get())) @@ -204,11 +217,15 @@ bool CompilerStack::analyze() // thus contracts can only conflict if declared in the same source file. This // already causes a double-declaration error elsewhere, so we do not report // an error here and instead silently drop any additional contracts we find. - if (m_contracts.find(contract->fullyQualifiedName()) == m_contracts.end()) m_contracts[contract->fullyQualifiedName()].contract = contract; } + // This cannot be done in the above loop, because cross-contract types couldn't be resolved. + // A good example is `LibraryName.TypeName x;`. + // + // Note: this does not resolve overloaded functions. In order to do that, types of arguments are needed, + // which is only done one step later. TypeChecker typeChecker(m_evmVersion, m_errorReporter); for (Source const* source: m_sourceOrder) for (ASTPointer<ASTNode> const& node: source->ast->nodes()) @@ -218,6 +235,7 @@ bool CompilerStack::analyze() if (noErrors) { + // Checks that can only be done when all types of all AST nodes are known. PostTypeChecker postTypeChecker(m_errorReporter); for (Source const* source: m_sourceOrder) if (!postTypeChecker.check(*source->ast)) @@ -226,6 +244,8 @@ bool CompilerStack::analyze() if (noErrors) { + // Control flow graph generator and analyzer. It can check for issues such as + // variable is used before it is assigned to. CFG cfg(m_errorReporter); for (Source const* source: m_sourceOrder) if (!cfg.constructFlow(*source->ast)) @@ -242,6 +262,7 @@ bool CompilerStack::analyze() if (noErrors) { + // Checks for common mistakes. Only generates warnings. StaticAnalyzer staticAnalyzer(m_errorReporter); for (Source const* source: m_sourceOrder) if (!staticAnalyzer.analyze(*source->ast)) @@ -250,6 +271,7 @@ bool CompilerStack::analyze() if (noErrors) { + // Check for state mutability in every function. vector<ASTPointer<ASTNode>> ast; for (Source const* source: m_sourceOrder) ast.push_back(source->ast); @@ -262,7 +284,7 @@ bool CompilerStack::analyze() { SMTChecker smtChecker(m_errorReporter, m_smtQuery); for (Source const* source: m_sourceOrder) - smtChecker.analyze(*source->ast); + smtChecker.analyze(*source->ast, source->scanner); } } catch(FatalError const&) @@ -300,6 +322,7 @@ bool CompilerStack::compile() if (!parseAndAnalyze()) return false; + // Only compile contracts individually which have been requested. map<ContractDefinition const*, eth::Assembly const*> compiledContracts; for (Source const* source: m_sourceOrder) for (ASTPointer<ASTNode> const& node: source->ast->nodes()) @@ -317,7 +340,6 @@ void CompilerStack::link() { contract.second.object.link(m_libraries); contract.second.runtimeObject.link(m_libraries); - contract.second.cloneObject.link(m_libraries); } } @@ -396,11 +418,6 @@ eth::LinkerObject const& CompilerStack::runtimeObject(string const& _contractNam return contract(_contractName).runtimeObject; } -eth::LinkerObject const& CompilerStack::cloneObject(string const& _contractName) const -{ - return contract(_contractName).cloneObject; -} - /// FIXME: cache this string string CompilerStack::assemblyString(string const& _contractName, StringMap _sourceCodes) const { @@ -571,7 +588,7 @@ StringMap CompilerStack::loadMissingSources(SourceUnit const& _ast, std::string for (auto const& node: _ast.nodes()) if (ImportDirective const* import = dynamic_cast<ImportDirective*>(node.get())) { - string importPath = absolutePath(import->path(), _sourcePath); + string importPath = dev::absolutePath(import->path(), _sourcePath); // The current value of `path` is the absolute path as seen from this source file. // We first have to apply remappings before we can store the actual absolute path // as seen globally. @@ -614,8 +631,8 @@ string CompilerStack::applyRemapping(string const& _path, string const& _context for (auto const& redir: m_remappings) { - string context = sanitizePath(redir.context); - string prefix = sanitizePath(redir.prefix); + string context = dev::sanitizePath(redir.context); + string prefix = dev::sanitizePath(redir.prefix); // Skip if current context is closer if (context.length() < longestContext) @@ -632,7 +649,7 @@ string CompilerStack::applyRemapping(string const& _path, string const& _context longestContext = context.length(); longestPrefix = prefix.length(); - bestMatchTarget = sanitizePath(redir.target); + bestMatchTarget = dev::sanitizePath(redir.target); } string path = bestMatchTarget; path.append(_path.begin() + longestPrefix, _path.end()); @@ -669,23 +686,6 @@ void CompilerStack::resolveImports() swap(m_sourceOrder, sourceOrder); } -string CompilerStack::absolutePath(string const& _path, string const& _reference) const -{ - using path = boost::filesystem::path; - path p(_path); - // Anything that does not start with `.` is an absolute path. - if (p.begin() == p.end() || (*p.begin() != "." && *p.begin() != "..")) - return _path; - path result(_reference); - result.remove_filename(); - for (path::iterator it = p.begin(); it != p.end(); ++it) - if (*it == "..") - result = result.parent_path(); - else if (*it != ".") - result /= *it; - return result.generic_string(); -} - namespace { bool onlySafeExperimentalFeaturesActivated(set<ExperimentalFeature> const& features) @@ -711,39 +711,33 @@ void CompilerStack::compileContract( for (auto const* dependency: _contract.annotation().contractDependencies) compileContract(*dependency, _compiledContracts); - shared_ptr<Compiler> compiler = make_shared<Compiler>(m_evmVersion, m_optimize, m_optimizeRuns); Contract& compiledContract = m_contracts.at(_contract.fullyQualifiedName()); - string metadata = createMetadata(compiledContract); - bytes cborEncodedHash = - // CBOR-encoding of the key "bzzr0" - bytes{0x65, 'b', 'z', 'z', 'r', '0'}+ - // CBOR-encoding of the hash - bytes{0x58, 0x20} + dev::swarmHash(metadata).asBytes(); - bytes cborEncodedMetadata; - if (onlySafeExperimentalFeaturesActivated(_contract.sourceUnit().annotation().experimentalFeatures)) - cborEncodedMetadata = - // CBOR-encoding of {"bzzr0": dev::swarmHash(metadata)} - bytes{0xa1} + - cborEncodedHash; - else - cborEncodedMetadata = - // CBOR-encoding of {"bzzr0": dev::swarmHash(metadata), "experimental": true} - bytes{0xa2} + - cborEncodedHash + - bytes{0x6c, 'e', 'x', 'p', 'e', 'r', 'i', 'm', 'e', 'n', 't', 'a', 'l', 0xf5}; - solAssert(cborEncodedMetadata.size() <= 0xffff, "Metadata too large"); - // 16-bit big endian length - cborEncodedMetadata += toCompactBigEndian(cborEncodedMetadata.size(), 2); - compiler->compileContract(_contract, _compiledContracts, cborEncodedMetadata); + + shared_ptr<Compiler> compiler = make_shared<Compiler>(m_evmVersion, m_optimize, m_optimizeRuns); compiledContract.compiler = compiler; + string metadata = createMetadata(compiledContract); + compiledContract.metadata = metadata; + + bytes cborEncodedMetadata = createCBORMetadata( + metadata, + !onlySafeExperimentalFeaturesActivated(_contract.sourceUnit().annotation().experimentalFeatures) + ); + try { - compiledContract.object = compiler->assembledObject(); + // Run optimiser and compile the contract. + compiler->compileContract(_contract, _compiledContracts, cborEncodedMetadata); } catch(eth::OptimizerException const&) { - solAssert(false, "Assembly optimizer exception for bytecode"); + solAssert(false, "Optimizer exception during compilation"); + } + + try + { + // Assemble deployment (incl. runtime) object. + compiledContract.object = compiler->assembledObject(); } catch(eth::AssemblyException const&) { @@ -752,36 +746,15 @@ void CompilerStack::compileContract( try { + // Assemble runtime object. compiledContract.runtimeObject = compiler->runtimeObject(); } - catch(eth::OptimizerException const&) - { - solAssert(false, "Assembly optimizer exception for deployed bytecode"); - } catch(eth::AssemblyException const&) { solAssert(false, "Assembly exception for deployed bytecode"); } - compiledContract.metadata = metadata; _compiledContracts[compiledContract.contract] = &compiler->assembly(); - - try - { - if (!_contract.isLibrary()) - { - Compiler cloneCompiler(m_evmVersion, m_optimize, m_optimizeRuns); - cloneCompiler.compileClone(_contract, _compiledContracts); - compiledContract.cloneObject = cloneCompiler.assembledObject(); - } - } - catch (eth::AssemblyException const&) - { - // In some cases (if the constructor requests a runtime function), it is not - // possible to compile the clone. - - // TODO: Report error / warning - } } string const CompilerStack::lastContractName() const @@ -809,7 +782,7 @@ CompilerStack::Contract const& CompilerStack::contract(string const& _contractNa // To provide a measure of backward-compatibility, if a contract is not located by its // fully-qualified name, a lookup will be attempted purely on the contract's name to see // if anything will satisfy. - if (_contractName.find(":") == string::npos) + if (_contractName.find(':') == string::npos) { for (auto const& contractEntry: m_contracts) { @@ -894,6 +867,31 @@ string CompilerStack::createMetadata(Contract const& _contract) const return jsonCompactPrint(meta); } +bytes CompilerStack::createCBORMetadata(string _metadata, bool _experimentalMode) +{ + bytes cborEncodedHash = + // CBOR-encoding of the key "bzzr0" + bytes{0x65, 'b', 'z', 'z', 'r', '0'}+ + // CBOR-encoding of the hash + bytes{0x58, 0x20} + dev::swarmHash(_metadata).asBytes(); + bytes cborEncodedMetadata; + if (_experimentalMode) + cborEncodedMetadata = + // CBOR-encoding of {"bzzr0": dev::swarmHash(metadata), "experimental": true} + bytes{0xa2} + + cborEncodedHash + + bytes{0x6c, 'e', 'x', 'p', 'e', 'r', 'i', 'm', 'e', 'n', 't', 'a', 'l', 0xf5}; + else + cborEncodedMetadata = + // CBOR-encoding of {"bzzr0": dev::swarmHash(metadata)} + bytes{0xa1} + + cborEncodedHash; + solAssert(cborEncodedMetadata.size() <= 0xffff, "Metadata too large"); + // 16-bit big endian length + cborEncodedMetadata += toCompactBigEndian(cborEncodedMetadata.size(), 2); + return cborEncodedMetadata; +} + string CompilerStack::computeSourceMapping(eth::AssemblyItems const& _items) const { string ret; @@ -938,17 +936,17 @@ string CompilerStack::computeSourceMapping(eth::AssemblyItems const& _items) con if (components-- > 0) { if (location.start != prevStart) - ret += std::to_string(location.start); + ret += to_string(location.start); if (components-- > 0) { ret += ':'; if (length != prevLength) - ret += std::to_string(length); + ret += to_string(length); if (components-- > 0) { ret += ':'; if (sourceIndex != prevSourceIndex) - ret += std::to_string(sourceIndex); + ret += to_string(sourceIndex); if (components-- > 0) { ret += ':'; @@ -992,8 +990,7 @@ Json::Value CompilerStack::gasEstimates(string const& _contractName) const if (eth::AssemblyItems const* items = assemblyItems(_contractName)) { Gas executionGas = gasEstimator.functionalEstimation(*items); - u256 bytecodeSize(runtimeObject(_contractName).bytecode.size()); - Gas codeDepositGas = bytecodeSize * eth::GasCosts::createDataGas; + Gas codeDepositGas{eth::GasMeter::dataGas(runtimeObject(_contractName).bytecode, false)}; Json::Value creation(Json::objectValue); creation["codeDepositCost"] = gasToJson(codeDepositGas); diff --git a/libsolidity/interface/CompilerStack.h b/libsolidity/interface/CompilerStack.h index 13c9cc7a..9a15fbf0 100644 --- a/libsolidity/interface/CompilerStack.h +++ b/libsolidity/interface/CompilerStack.h @@ -36,7 +36,6 @@ #include <json/json.h> #include <boost/noncopyable.hpp> -#include <boost/filesystem.hpp> #include <ostream> #include <string> @@ -85,6 +84,13 @@ public: CompilationSuccessful }; + struct Remapping + { + std::string context; + std::string prefix; + std::string target; + }; + /// Creates a new compiler stack. /// @param _readFile callback to used to read files for import statements. Must return /// and must not emit exceptions. @@ -93,7 +99,7 @@ public: m_errorList(), m_errorReporter(m_errorList) {} - /// @returns the list of errors that occured during parsing and type checking. + /// @returns the list of errors that occurred during parsing and type checking. ErrorList const& errors() const { return m_errorReporter.errors(); } /// @returns the current state. @@ -104,8 +110,11 @@ public: /// All settings, with the exception of remappings, are reset. void reset(bool _keepSources = false); - /// Sets path remappings in the format "context:prefix=target" - void setRemappings(std::vector<std::string> const& _remappings); + // Parses a remapping of the format "context:prefix=target". + static boost::optional<Remapping> parseRemapping(std::string const& _remapping); + + /// Sets path remappings. + void setRemappings(std::vector<Remapping> const& _remappings); /// Sets library addresses. Addresses are cleared iff @a _libraries is missing. /// Will not take effect before running compile. @@ -122,6 +131,8 @@ public: m_optimizeRuns = _runs; } + /// Set the EVM version used before running compile. + /// When called without an argument it will revert to the default version. void setEVMVersion(EVMVersion _version = EVMVersion{}); /// Sets the list of requested contract names. If empty, no filtering is performed and every contract @@ -188,12 +199,6 @@ public: /// @returns the runtime object for the contract. eth::LinkerObject const& runtimeObject(std::string const& _contractName) const; - /// @returns the bytecode of a contract that uses an already deployed contract via DELEGATECALL. - /// The returned bytes will contain a sequence of 20 bytes of the format "XXX...XXX" which have to - /// substituted by the actual address. Note that this sequence starts end ends in three X - /// characters but can contain anything in between. - eth::LinkerObject const& cloneObject(std::string const& _contractName) const; - /// @returns normal contract assembly items eth::AssemblyItems const* assemblyItems(std::string const& _contractName) const; @@ -240,9 +245,7 @@ public: Json::Value gasEstimates(std::string const& _contractName) const; private: - /** - * Information pertaining to one source unit, filled gradually during parsing and compilation. - */ + /// The state per source unit. Filled gradually during parsing. struct Source { std::shared_ptr<Scanner> scanner; @@ -251,13 +254,13 @@ private: void reset() { scanner.reset(); ast.reset(); } }; + /// The state per contract. Filled gradually during compilation. struct Contract { ContractDefinition const* contract = nullptr; std::shared_ptr<Compiler> compiler; - eth::LinkerObject object; - eth::LinkerObject runtimeObject; - eth::LinkerObject cloneObject; + eth::LinkerObject object; ///< Deployment object (includes the runtime sub-object). + eth::LinkerObject runtimeObject; ///< Runtime object. std::string metadata; ///< The metadata json that will be hashed into the chain. mutable std::unique_ptr<Json::Value const> abi; mutable std::unique_ptr<Json::Value const> userDocumentation; @@ -272,10 +275,6 @@ private: StringMap loadMissingSources(SourceUnit const& _ast, std::string const& _path); std::string applyRemapping(std::string const& _path, std::string const& _context); void resolveImports(); - /// @returns the absolute path corresponding to @a _path relative to @a _reference. - std::string absolutePath(std::string const& _path, std::string const& _reference) const; - /// Helper function to return path converted strings. - std::string sanitizePath(std::string const& _path) const { return boost::filesystem::path(_path).generic_string(); } /// @returns true if the contract is requested to be compiled. bool isRequestedContract(ContractDefinition const& _contract) const; @@ -285,19 +284,42 @@ private: ContractDefinition const& _contract, std::map<ContractDefinition const*, eth::Assembly const*>& _compiledContracts ); + + /// Links all the known library addresses in the available objects. Any unknown + /// library will still be kept as an unlinked placeholder in the objects. void link(); + /// @returns the contract object for the given @a _contractName. + /// Can only be called after state is CompilationSuccessful. Contract const& contract(std::string const& _contractName) const; + + /// @returns the source object for the given @a _sourceName. + /// Can only be called after state is SourcesSet. Source const& source(std::string const& _sourceName) const; /// @returns the parsed contract with the supplied name. Throws an exception if the contract /// does not exist. ContractDefinition const& contractDefinition(std::string const& _contractName) const; + /// @returns the metadata JSON as a compact string for the given contract. std::string createMetadata(Contract const& _contract) const; + + /// @returns the metadata CBOR for the given serialised metadata JSON. + static bytes createCBORMetadata(std::string _metadata, bool _experimentalMode); + + /// @returns the computer source mapping string. std::string computeSourceMapping(eth::AssemblyItems const& _items) 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. Json::Value const& contractABI(Contract const&) const; + + /// @returns the Natspec User documentation as a JSON object. + /// This will generate the JSON object and store it in the Contract object if it is not present yet. Json::Value const& natspecUser(Contract const&) const; + + /// @returns the Natspec Developer documentation as a JSON object. + /// 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 offset of the entry point of the given function into the list of assembly items @@ -307,13 +329,6 @@ private: FunctionDefinition const& _function ) const; - struct Remapping - { - std::string context; - std::string prefix; - std::string target; - }; - ReadCallback::Callback m_readFile; ReadCallback::Callback m_smtQuery; bool m_optimize = false; @@ -326,8 +341,9 @@ private: std::vector<Remapping> m_remappings; std::map<std::string const, Source> m_sources; std::shared_ptr<GlobalContext> m_globalContext; - std::map<ASTNode const*, std::shared_ptr<DeclarationContainer>> m_scopes; std::vector<Source const*> m_sourceOrder; + /// This is updated during compilation. + std::map<ASTNode const*, std::shared_ptr<DeclarationContainer>> m_scopes; std::map<std::string const, Contract> m_contracts; ErrorList m_errorList; ErrorReporter m_errorReporter; diff --git a/libsolidity/interface/EVMVersion.h b/libsolidity/interface/EVMVersion.h index b68e1f4e..657727ac 100644 --- a/libsolidity/interface/EVMVersion.h +++ b/libsolidity/interface/EVMVersion.h @@ -75,6 +75,7 @@ public: bool supportsReturndata() const { return *this >= byzantium(); } bool hasStaticCall() const { return *this >= byzantium(); } bool hasBitwiseShifting() const { return *this >= constantinople(); } + bool hasCreate2() const { return *this >= constantinople(); } /// Whether we have to retain the costs for the call opcode itself (false), /// or whether we can just forward easily all remaining gas (true). diff --git a/libsolidity/interface/ErrorReporter.h b/libsolidity/interface/ErrorReporter.h index d1a0030f..fd53587a 100644 --- a/libsolidity/interface/ErrorReporter.h +++ b/libsolidity/interface/ErrorReporter.h @@ -92,6 +92,12 @@ public: void clear(); + /// @returns true iff there is any error (ignores warnings). + bool hasErrors() const + { + return m_errorCount > 0; + } + private: void error(Error::Type _type, SourceLocation const& _location, diff --git a/libsolidity/interface/Exceptions.cpp b/libsolidity/interface/Exceptions.cpp index a837dce6..ecadd0b7 100644 --- a/libsolidity/interface/Exceptions.cpp +++ b/libsolidity/interface/Exceptions.cpp @@ -49,9 +49,6 @@ Error::Error(Type _type, SourceLocation const& _location, string const& _descrip case Type::Warning: m_typeName = "Warning"; break; - default: - solAssert(false, ""); - break; } if (!_location.isEmpty()) diff --git a/libsolidity/interface/Exceptions.h b/libsolidity/interface/Exceptions.h index 7c66d572..629b8f3f 100644 --- a/libsolidity/interface/Exceptions.h +++ b/libsolidity/interface/Exceptions.h @@ -117,7 +117,7 @@ public: if (occurrences > 32) { infos.resize(32); - _message += " Truncated from " + boost::lexical_cast<std::string>(occurrences) + " to the first 32 occurrences."; + _message += " Truncated from " + std::to_string(occurrences) + " to the first 32 occurrences."; } } diff --git a/libsolidity/interface/GasEstimator.cpp b/libsolidity/interface/GasEstimator.cpp index a532f86e..1f20366e 100644 --- a/libsolidity/interface/GasEstimator.cpp +++ b/libsolidity/interface/GasEstimator.cpp @@ -24,7 +24,7 @@ #include <map> #include <functional> #include <memory> -#include <libdevcore/SHA3.h> +#include <libdevcore/Keccak256.h> #include <libevmasm/ControlFlowGraph.h> #include <libevmasm/KnownState.h> #include <libevmasm/PathGasMeter.h> @@ -160,8 +160,7 @@ GasEstimator::GasConsumption GasEstimator::functionalEstimation( ); } - PathGasMeter meter(_items, m_evmVersion); - return meter.estimateMax(0, state); + return PathGasMeter::estimateMax(_items, m_evmVersion, 0, state); } GasEstimator::GasConsumption GasEstimator::functionalEstimation( @@ -183,7 +182,7 @@ GasEstimator::GasConsumption GasEstimator::functionalEstimation( if (parametersSize > 0) state->feedItem(swapInstruction(parametersSize)); - return PathGasMeter(_items, m_evmVersion).estimateMax(_offset, state); + return PathGasMeter::estimateMax(_items, m_evmVersion, _offset, state); } set<ASTNode const*> GasEstimator::finestNodesAtLocation( diff --git a/libsolidity/interface/Natspec.cpp b/libsolidity/interface/Natspec.cpp index 7f7084ef..11dde349 100644 --- a/libsolidity/interface/Natspec.cpp +++ b/libsolidity/interface/Natspec.cpp @@ -36,6 +36,19 @@ Json::Value Natspec::userDocumentation(ContractDefinition const& _contractDef) Json::Value doc; Json::Value methods(Json::objectValue); + auto constructorDefinition(_contractDef.constructor()); + if (constructorDefinition) + { + string value = extractDoc(constructorDefinition->annotation().docTags, "notice"); + if (!value.empty()) + // add the constructor, only if we have any documentation to add + methods["constructor"] = Json::Value(value); + } + + string notice = extractDoc(_contractDef.annotation().docTags, "notice"); + if (!notice.empty()) + doc["notice"] = Json::Value(notice); + for (auto const& it: _contractDef.interfaceFunctions()) if (it.second->hasDeclaration()) if (auto const* f = dynamic_cast<FunctionDefinition const*>(&it.second->declaration())) @@ -65,34 +78,26 @@ Json::Value Natspec::devDocumentation(ContractDefinition const& _contractDef) auto title = extractDoc(_contractDef.annotation().docTags, "title"); if (!title.empty()) doc["title"] = title; + auto dev = extractDoc(_contractDef.annotation().docTags, "dev"); + if (!dev.empty()) + doc["details"] = Json::Value(dev); + + auto constructorDefinition(_contractDef.constructor()); + if (constructorDefinition) + { + Json::Value constructor(devDocumentation(constructorDefinition->annotation().docTags)); + if (!constructor.empty()) + // add the constructor, only if we have any documentation to add + methods["constructor"] = constructor; + } for (auto const& it: _contractDef.interfaceFunctions()) { if (!it.second->hasDeclaration()) continue; - Json::Value method; if (auto fun = dynamic_cast<FunctionDefinition const*>(&it.second->declaration())) { - auto dev = extractDoc(fun->annotation().docTags, "dev"); - if (!dev.empty()) - method["details"] = Json::Value(dev); - - auto author = extractDoc(fun->annotation().docTags, "author"); - if (!author.empty()) - method["author"] = author; - - auto ret = extractDoc(fun->annotation().docTags, "return"); - if (!ret.empty()) - method["return"] = ret; - - Json::Value params(Json::objectValue); - auto paramRange = fun->annotation().docTags.equal_range("param"); - for (auto i = paramRange.first; i != paramRange.second; ++i) - params[i->second.paramName] = Json::Value(i->second.content); - - if (!params.empty()) - method["params"] = params; - + Json::Value method(devDocumentation(fun->annotation().docTags)); if (!method.empty()) // add the function, only if we have any documentation to add methods[it.second->externalSignature()] = method; @@ -111,3 +116,31 @@ string Natspec::extractDoc(multimap<string, DocTag> const& _tags, string const& value += i->second.content; return value; } + +Json::Value Natspec::devDocumentation(std::multimap<std::string, DocTag> const &_tags) +{ + Json::Value json(Json::objectValue); + auto dev = extractDoc(_tags, "dev"); + if (!dev.empty()) + json["details"] = Json::Value(dev); + + auto author = extractDoc(_tags, "author"); + if (!author.empty()) + json["author"] = author; + + // for constructors, the "return" node will never exist. invalid tags + // will already generate an error within dev::solidity::DocStringAnalyzer. + auto ret = extractDoc(_tags, "return"); + if (!ret.empty()) + json["return"] = ret; + + Json::Value params(Json::objectValue); + auto paramRange = _tags.equal_range("param"); + for (auto i = paramRange.first; i != paramRange.second; ++i) + params[i->second.paramName] = Json::Value(i->second.content); + + if (!params.empty()) + json["params"] = params; + + return json; +} diff --git a/libsolidity/interface/Natspec.h b/libsolidity/interface/Natspec.h index 0701f821..0be4dda2 100644 --- a/libsolidity/interface/Natspec.h +++ b/libsolidity/interface/Natspec.h @@ -45,7 +45,7 @@ public: /// @param _contractDef The contract definition /// @return A JSON representation of the contract's user documentation static Json::Value userDocumentation(ContractDefinition const& _contractDef); - /// Genereates the Developer's documentation of the contract + /// Generates the Developer's documentation of the contract /// @param _contractDef The contract definition /// @return A JSON representation /// of the contract's developer documentation @@ -54,6 +54,12 @@ public: private: /// @returns concatenation of all content under the given tag name. static std::string extractDoc(std::multimap<std::string, DocTag> const& _tags, std::string const& _name); + + /// Helper-function that will create a json object with dev specific annotations, if present. + /// @param _tags docTags that are used. + /// @return A JSON representation + /// of the contract's developer documentation + static Json::Value devDocumentation(std::multimap<std::string, DocTag> const &_tags); }; } //solidity NS diff --git a/libsolidity/interface/SourceReferenceFormatter.cpp b/libsolidity/interface/SourceReferenceFormatter.cpp index 0f014372..865907e2 100644 --- a/libsolidity/interface/SourceReferenceFormatter.cpp +++ b/libsolidity/interface/SourceReferenceFormatter.cpp @@ -55,8 +55,15 @@ void SourceReferenceFormatter::printSourceLocation(SourceLocation const* _locati } if (line.length() > 150) { - line = " ... " + line.substr(startColumn, locationLength) + " ... "; - startColumn = 5; + int len = line.length(); + line = line.substr(max(0, startColumn - 35), min(startColumn, 35) + min(locationLength + 35, len - startColumn)); + if (startColumn + locationLength + 35 < len) + line += " ..."; + if (startColumn > 35) + { + line = " ... " + line; + startColumn = 40; + } endColumn = startColumn + locationLength; } diff --git a/libsolidity/interface/StandardCompiler.cpp b/libsolidity/interface/StandardCompiler.cpp index ee9b1440..c8b03a94 100644 --- a/libsolidity/interface/StandardCompiler.cpp +++ b/libsolidity/interface/StandardCompiler.cpp @@ -25,7 +25,7 @@ #include <libsolidity/ast/ASTJsonConverter.h> #include <libevmasm/Instruction.h> #include <libdevcore/JSON.h> -#include <libdevcore/SHA3.h> +#include <libdevcore/Keccak256.h> #include <boost/algorithm/string.hpp> @@ -117,7 +117,7 @@ bool hashMatchesContent(string const& _hash, string const& _content) { return dev::h256(_hash) == dev::keccak256(_content); } - catch (dev::BadHexCharacter) + catch (dev::BadHexCharacter const&) { return false; } @@ -280,6 +280,8 @@ Json::Value StandardCompiler::compileInternal(Json::Value const& _input) for (auto const& url: sources[sourceName]["urls"]) { + if (!url.isString()) + return formatFatalError("JSONError", "URL must be a string."); ReadCallback::Result result = m_readFile(url.asString()); if (result.success) { @@ -320,21 +322,45 @@ Json::Value StandardCompiler::compileInternal(Json::Value const& _input) if (settings.isMember("evmVersion")) { - boost::optional<EVMVersion> version = EVMVersion::fromString(settings.get("evmVersion", {}).asString()); + if (!settings["evmVersion"].isString()) + return formatFatalError("JSONError", "evmVersion must be a string."); + boost::optional<EVMVersion> version = EVMVersion::fromString(settings["evmVersion"].asString()); if (!version) return formatFatalError("JSONError", "Invalid EVM version requested."); m_compilerStack.setEVMVersion(*version); } - vector<string> remappings; + vector<CompilerStack::Remapping> remappings; for (auto const& remapping: settings.get("remappings", Json::Value())) - remappings.push_back(remapping.asString()); + { + if (!remapping.isString()) + return formatFatalError("JSONError", "Remapping entry must be a string."); + if (auto r = CompilerStack::parseRemapping(remapping.asString())) + remappings.emplace_back(std::move(*r)); + else + return formatFatalError("JSONError", "Invalid remapping: \"" + remapping.asString() + "\""); + } m_compilerStack.setRemappings(remappings); - Json::Value optimizerSettings = settings.get("optimizer", Json::Value()); - bool const optimize = optimizerSettings.get("enabled", Json::Value(false)).asBool(); - unsigned const optimizeRuns = optimizerSettings.get("runs", Json::Value(200u)).asUInt(); - m_compilerStack.setOptimiserSettings(optimize, optimizeRuns); + if (settings.isMember("optimizer")) + { + Json::Value optimizerSettings = settings["optimizer"]; + if (optimizerSettings.isMember("enabled")) + { + if (!optimizerSettings["enabled"].isBool()) + return formatFatalError("JSONError", "The \"enabled\" setting must be a boolean."); + + bool const optimize = optimizerSettings["enabled"].asBool(); + unsigned optimizeRuns = 200; + if (optimizerSettings.isMember("runs")) + { + if (!optimizerSettings["runs"].isUInt()) + return formatFatalError("JSONError", "The \"runs\" setting must be an unsigned number."); + optimizeRuns = optimizerSettings["runs"].asUInt(); + } + m_compilerStack.setOptimiserSettings(optimize, optimizeRuns); + } + } map<string, h160> libraries; Json::Value jsonLibraries = settings.get("libraries", Json::Value(Json::objectValue)); @@ -344,9 +370,11 @@ Json::Value StandardCompiler::compileInternal(Json::Value const& _input) { auto const& jsonSourceName = jsonLibraries[sourceName]; if (!jsonSourceName.isObject()) - return formatFatalError("JSONError", "library entry is not a JSON object."); + return formatFatalError("JSONError", "Library entry is not a JSON object."); for (auto const& library: jsonSourceName.getMemberNames()) { + if (!jsonSourceName[library].isString()) + return formatFatalError("JSONError", "Library address must be a string."); string address = jsonSourceName[library].asString(); if (!boost::starts_with(address, "0x")) @@ -366,7 +394,7 @@ Json::Value StandardCompiler::compileInternal(Json::Value const& _input) // @TODO use libraries only for the given source libraries[library] = h160(address); } - catch (dev::BadHexCharacter) + catch (dev::BadHexCharacter const&) { return formatFatalError( "JSONError", @@ -481,7 +509,7 @@ Json::Value StandardCompiler::compileInternal(Json::Value const& _input) bool const compilationSuccess = m_compilerStack.state() == CompilerStack::State::CompilationSuccessful; /// Inconsistent state - stop here to receive error reports from users - if (!compilationSuccess && (errors.size() == 0)) + if (!compilationSuccess && errors.empty()) return formatFatalError("InternalCompilerError", "No error reported, but compilation failed."); Json::Value output = Json::objectValue; @@ -567,7 +595,7 @@ Json::Value StandardCompiler::compileInternal(Json::Value const& _input) return output; } -Json::Value StandardCompiler::compile(Json::Value const& _input) +Json::Value StandardCompiler::compile(Json::Value const& _input) noexcept { try { @@ -591,7 +619,7 @@ Json::Value StandardCompiler::compile(Json::Value const& _input) } } -string StandardCompiler::compile(string const& _input) +string StandardCompiler::compile(string const& _input) noexcept { Json::Value input; string errors; @@ -600,9 +628,9 @@ string StandardCompiler::compile(string const& _input) if (!jsonParseStrict(_input, input, &errors)) return jsonCompactPrint(formatFatalError("JSONError", errors)); } - catch(...) + catch (...) { - return "{\"errors\":\"[{\"type\":\"JSONError\",\"component\":\"general\",\"severity\":\"error\",\"message\":\"Error parsing input JSON.\"}]}"; + return "{\"errors\":[{\"type\":\"JSONError\",\"component\":\"general\",\"severity\":\"error\",\"message\":\"Error parsing input JSON.\"}]}"; } // cout << "Input: " << input.toStyledString() << endl; @@ -613,8 +641,8 @@ string StandardCompiler::compile(string const& _input) { return jsonCompactPrint(output); } - catch(...) + catch (...) { - return "{\"errors\":\"[{\"type\":\"JSONError\",\"component\":\"general\",\"severity\":\"error\",\"message\":\"Error writing output JSON.\"}]}"; + return "{\"errors\":[{\"type\":\"JSONError\",\"component\":\"general\",\"severity\":\"error\",\"message\":\"Error writing output JSON.\"}]}"; } } diff --git a/libsolidity/interface/StandardCompiler.h b/libsolidity/interface/StandardCompiler.h index 11a0b4c2..fc9c3a59 100644 --- a/libsolidity/interface/StandardCompiler.h +++ b/libsolidity/interface/StandardCompiler.h @@ -31,7 +31,7 @@ namespace solidity { /** - * Standard JSON compiler interface, which expects a JSON input and returns a JSON ouput. + * Standard JSON compiler interface, which expects a JSON input and returns a JSON output. * See docs/using-the-compiler#compiler-input-and-output-json-description. */ class StandardCompiler: boost::noncopyable @@ -47,10 +47,10 @@ public: /// Sets all input parameters according to @a _input which conforms to the standardized input /// format, performs compilation and returns a standardized output. - Json::Value compile(Json::Value const& _input); + Json::Value compile(Json::Value const& _input) noexcept; /// Parses input as JSON and peforms the above processing steps, returning a serialized JSON /// output. Parsing errors are returned as regular errors. - std::string compile(std::string const& _input); + std::string compile(std::string const& _input) noexcept; private: Json::Value compileInternal(Json::Value const& _input); diff --git a/libsolidity/interface/Version.cpp b/libsolidity/interface/Version.cpp index a35bfd29..b5f68ce8 100644 --- a/libsolidity/interface/Version.cpp +++ b/libsolidity/interface/Version.cpp @@ -55,13 +55,13 @@ bytes dev::solidity::binaryVersion() ret = ret * 10 + (VersionString[i] - '0'); return ret; }; - ret.push_back(byte(parseDecimal())); + ret.push_back(uint8_t(parseDecimal())); solAssert(i < VersionString.size() && VersionString[i] == '.', ""); ++i; - ret.push_back(byte(parseDecimal())); + ret.push_back(uint8_t(parseDecimal())); solAssert(i < VersionString.size() && VersionString[i] == '.', ""); ++i; - ret.push_back(byte(parseDecimal())); + ret.push_back(uint8_t(parseDecimal())); solAssert(i < VersionString.size() && (VersionString[i] == '-' || VersionString[i] == '+'), ""); ++i; size_t commitpos = VersionString.find("commit."); diff --git a/libsolidity/parsing/Parser.cpp b/libsolidity/parsing/Parser.cpp index e2e1eebc..b17dad9a 100644 --- a/libsolidity/parsing/Parser.cpp +++ b/libsolidity/parsing/Parser.cpp @@ -20,7 +20,7 @@ * Solidity parser. */ -#include <ctype.h> +#include <cctype> #include <vector> #include <libevmasm/SourceLocation.h> #include <libsolidity/parsing/Parser.h> @@ -57,7 +57,7 @@ public: solAssert(m_location.sourceName, ""); if (m_location.end < 0) markEndPosition(); - return make_shared<NodeType>(m_location, forward<Args>(_args)...); + return make_shared<NodeType>(m_location, std::forward<Args>(_args)...); } private: @@ -75,7 +75,7 @@ ASTPointer<SourceUnit> Parser::parse(shared_ptr<Scanner> const& _scanner) vector<ASTPointer<ASTNode>> nodes; while (m_scanner->currentToken() != Token::EOS) { - switch (auto token = m_scanner->currentToken()) + switch (m_scanner->currentToken()) { case Token::Pragma: nodes.push_back(parsePragmaDirective()); @@ -86,7 +86,7 @@ ASTPointer<SourceUnit> Parser::parse(shared_ptr<Scanner> const& _scanner) case Token::Interface: case Token::Contract: case Token::Library: - nodes.push_back(parseContractDefinition(token)); + nodes.push_back(parseContractDefinition()); break; default: fatalParserError(string("Expected pragma, import directive or contract/interface/library definition.")); @@ -112,17 +112,17 @@ ASTPointer<PragmaDirective> Parser::parsePragmaDirective() ASTNodeFactory nodeFactory(*this); expectToken(Token::Pragma); vector<string> literals; - vector<Token::Value> tokens; + vector<Token> tokens; do { - Token::Value token = m_scanner->currentToken(); + Token token = m_scanner->currentToken(); if (token == Token::Illegal) parserError("Token incompatible with Solidity parser as part of pragma directive."); else { string literal = m_scanner->currentLiteral(); - if (literal.empty() && Token::toString(token)) - literal = Token::toString(token); + if (literal.empty() && TokenTraits::toString(token)) + literal = TokenTraits::toString(token); literals.push_back(literal); tokens.push_back(token); } @@ -198,31 +198,35 @@ ASTPointer<ImportDirective> Parser::parseImportDirective() return nodeFactory.createNode<ImportDirective>(path, unitAlias, move(symbolAliases)); } -ContractDefinition::ContractKind Parser::tokenToContractKind(Token::Value _token) +ContractDefinition::ContractKind Parser::parseContractKind() { - switch(_token) + ContractDefinition::ContractKind kind; + switch(m_scanner->currentToken()) { case Token::Interface: - return ContractDefinition::ContractKind::Interface; + kind = ContractDefinition::ContractKind::Interface; + break; case Token::Contract: - return ContractDefinition::ContractKind::Contract; + kind = ContractDefinition::ContractKind::Contract; + break; case Token::Library: - return ContractDefinition::ContractKind::Library; + kind = ContractDefinition::ContractKind::Library; + break; default: - fatalParserError("Unsupported contract type."); + solAssert(false, "Invalid contract kind."); } - // FIXME: fatalParserError is not considered as throwing here - return ContractDefinition::ContractKind::Contract; + m_scanner->next(); + return kind; } -ASTPointer<ContractDefinition> Parser::parseContractDefinition(Token::Value _expectedKind) +ASTPointer<ContractDefinition> Parser::parseContractDefinition() { RecursionGuard recursionGuard(*this); ASTNodeFactory nodeFactory(*this); ASTPointer<ASTString> docString; if (m_scanner->currentCommentLiteral() != "") docString = make_shared<ASTString>(m_scanner->currentCommentLiteral()); - expectToken(_expectedKind); + ContractDefinition::ContractKind contractKind = parseContractKind(); ASTPointer<ASTString> name = expectIdentifierToken(); vector<ASTPointer<InheritanceSpecifier>> baseContracts; if (m_scanner->currentToken() == Token::Is) @@ -236,16 +240,13 @@ ASTPointer<ContractDefinition> Parser::parseContractDefinition(Token::Value _exp expectToken(Token::LBrace); while (true) { - Token::Value currentTokenValue = m_scanner->currentToken(); + Token currentTokenValue = m_scanner->currentToken(); if (currentTokenValue == Token::RBrace) break; - else if ( - currentTokenValue == Token::Function || - (currentTokenValue == Token::Identifier && m_scanner->currentLiteral() == "constructor") - ) + else if (currentTokenValue == Token::Function || currentTokenValue == Token::Constructor) // This can be a function or a state variable of function type (especially // complicated to distinguish fallback function from function type state variable) - subNodes.push_back(parseFunctionDefinitionOrFunctionTypeStateVariable(name.get())); + subNodes.push_back(parseFunctionDefinitionOrFunctionTypeStateVariable()); else if (currentTokenValue == Token::Struct) subNodes.push_back(parseStructDefinition()); else if (currentTokenValue == Token::Enum) @@ -253,7 +254,7 @@ ASTPointer<ContractDefinition> Parser::parseContractDefinition(Token::Value _exp else if ( currentTokenValue == Token::Identifier || currentTokenValue == Token::Mapping || - Token::isElementaryTypeName(currentTokenValue) + TokenTraits::isElementaryTypeName(currentTokenValue) ) { VarDeclParserOptions options; @@ -278,7 +279,7 @@ ASTPointer<ContractDefinition> Parser::parseContractDefinition(Token::Value _exp docString, baseContracts, subNodes, - tokenToContractKind(_expectedKind) + contractKind ); } @@ -300,70 +301,92 @@ ASTPointer<InheritanceSpecifier> Parser::parseInheritanceSpecifier() return nodeFactory.createNode<InheritanceSpecifier>(name, std::move(arguments)); } -Declaration::Visibility Parser::parseVisibilitySpecifier(Token::Value _token) +Declaration::Visibility Parser::parseVisibilitySpecifier() { Declaration::Visibility visibility(Declaration::Visibility::Default); - if (_token == Token::Public) - visibility = Declaration::Visibility::Public; - else if (_token == Token::Internal) - visibility = Declaration::Visibility::Internal; - else if (_token == Token::Private) - visibility = Declaration::Visibility::Private; - else if (_token == Token::External) - visibility = Declaration::Visibility::External; - else - solAssert(false, "Invalid visibility specifier."); + Token token = m_scanner->currentToken(); + switch (token) + { + case Token::Public: + visibility = Declaration::Visibility::Public; + break; + case Token::Internal: + visibility = Declaration::Visibility::Internal; + break; + case Token::Private: + visibility = Declaration::Visibility::Private; + break; + case Token::External: + visibility = Declaration::Visibility::External; + break; + default: + solAssert(false, "Invalid visibility specifier."); + } m_scanner->next(); return visibility; } -StateMutability Parser::parseStateMutability(Token::Value _token) +StateMutability Parser::parseStateMutability() { StateMutability stateMutability(StateMutability::NonPayable); - if (_token == Token::Payable) - stateMutability = StateMutability::Payable; - // FIXME: constant should be removed at the next breaking release - else if (_token == Token::View || _token == Token::Constant) - stateMutability = StateMutability::View; - else if (_token == Token::Pure) - stateMutability = StateMutability::Pure; - else - solAssert(false, "Invalid state mutability specifier."); + Token token = m_scanner->currentToken(); + switch(token) + { + case Token::Payable: + stateMutability = StateMutability::Payable; + break; + case Token::View: + stateMutability = StateMutability::View; + break; + case Token::Pure: + stateMutability = StateMutability::Pure; + break; + case Token::Constant: + stateMutability = StateMutability::View; + parserError( + "The state mutability modifier \"constant\" was removed in version 0.5.0. " + "Use \"view\" or \"pure\" instead." + ); + break; + default: + solAssert(false, "Invalid state mutability specifier."); + } m_scanner->next(); return stateMutability; } -Parser::FunctionHeaderParserResult Parser::parseFunctionHeader( - bool _forceEmptyName, - bool _allowModifiers, - ASTString const* _contractName -) +Parser::FunctionHeaderParserResult Parser::parseFunctionHeader(bool _forceEmptyName, bool _allowModifiers) { RecursionGuard recursionGuard(*this); FunctionHeaderParserResult result; result.isConstructor = false; - if (m_scanner->currentToken() == Token::Identifier && m_scanner->currentLiteral() == "constructor") + if (m_scanner->currentToken() == Token::Constructor) result.isConstructor = true; else if (m_scanner->currentToken() != Token::Function) solAssert(false, "Function or constructor expected."); m_scanner->next(); - if (result.isConstructor || _forceEmptyName || m_scanner->currentToken() == Token::LParen) + if (result.isConstructor) + result.name = make_shared<ASTString>(); + else if (_forceEmptyName || m_scanner->currentToken() == Token::LParen) result.name = make_shared<ASTString>(); + else if (m_scanner->currentToken() == Token::Constructor) + fatalParserError(string( + "This function is named \"constructor\" but is not the constructor of the contract. " + "If you intend this to be a constructor, use \"constructor(...) { ... }\" without the \"function\" keyword to define it." + )); else result.name = expectIdentifierToken(); - if (!result.name->empty() && _contractName && *result.name == *_contractName) - result.isConstructor = true; VarDeclParserOptions options; options.allowLocationSpecifier = true; result.parameters = parseParameterList(options); while (true) { - Token::Value token = m_scanner->currentToken(); + Token token = m_scanner->currentToken(); if (_allowModifiers && token == Token::Identifier) { // If the name is empty (and this is not a constructor), @@ -378,7 +401,7 @@ Parser::FunctionHeaderParserResult Parser::parseFunctionHeader( else result.modifiers.push_back(parseModifierInvocation()); } - else if (Token::isVisibilitySpecifier(token)) + else if (TokenTraits::isVisibilitySpecifier(token)) { if (result.visibility != Declaration::Visibility::Default) { @@ -398,9 +421,9 @@ Parser::FunctionHeaderParserResult Parser::parseFunctionHeader( m_scanner->next(); } else - result.visibility = parseVisibilitySpecifier(token); + result.visibility = parseVisibilitySpecifier(); } - else if (Token::isStateMutabilitySpecifier(token)) + else if (TokenTraits::isStateMutabilitySpecifier(token)) { if (result.stateMutability != StateMutability::NonPayable) { @@ -412,7 +435,7 @@ Parser::FunctionHeaderParserResult Parser::parseFunctionHeader( m_scanner->next(); } else - result.stateMutability = parseStateMutability(token); + result.stateMutability = parseStateMutability(); } else break; @@ -428,7 +451,7 @@ Parser::FunctionHeaderParserResult Parser::parseFunctionHeader( return result; } -ASTPointer<ASTNode> Parser::parseFunctionDefinitionOrFunctionTypeStateVariable(ASTString const* _contractName) +ASTPointer<ASTNode> Parser::parseFunctionDefinitionOrFunctionTypeStateVariable() { RecursionGuard recursionGuard(*this); ASTNodeFactory nodeFactory(*this); @@ -436,7 +459,7 @@ ASTPointer<ASTNode> Parser::parseFunctionDefinitionOrFunctionTypeStateVariable(A if (m_scanner->currentCommentLiteral() != "") docstring = make_shared<ASTString>(m_scanner->currentCommentLiteral()); - FunctionHeaderParserResult header = parseFunctionHeader(false, true, _contractName); + FunctionHeaderParserResult header = parseFunctionHeader(false, true); if ( header.isConstructor || @@ -531,7 +554,7 @@ ASTPointer<EnumDefinition> Parser::parseEnumDefinition() if (m_scanner->currentToken() != Token::Identifier) fatalParserError(string("Expected identifier after ','")); } - if (members.size() == 0) + if (members.empty()) parserError({"enum with no members is not allowed."}); nodeFactory.markEndPosition(); @@ -559,14 +582,15 @@ ASTPointer<VariableDeclaration> Parser::parseVariableDeclaration( bool isIndexed = false; bool isDeclaredConst = false; Declaration::Visibility visibility(Declaration::Visibility::Default); - VariableDeclaration::Location location = VariableDeclaration::Location::Default; + VariableDeclaration::Location location = VariableDeclaration::Location::Unspecified; ASTPointer<ASTString> identifier; while (true) { - Token::Value token = m_scanner->currentToken(); - if (_options.isStateVariable && Token::isVariableVisibilitySpecifier(token)) + Token token = m_scanner->currentToken(); + if (_options.isStateVariable && TokenTraits::isVariableVisibilitySpecifier(token)) { + nodeFactory.markEndPosition(); if (visibility != Declaration::Visibility::Default) { parserError(string( @@ -577,7 +601,7 @@ ASTPointer<VariableDeclaration> Parser::parseVariableDeclaration( m_scanner->next(); } else - visibility = parseVisibilitySpecifier(token); + visibility = parseVisibilitySpecifier(); } else { @@ -585,36 +609,47 @@ ASTPointer<VariableDeclaration> Parser::parseVariableDeclaration( isIndexed = true; else if (token == Token::Constant) isDeclaredConst = true; - else if (_options.allowLocationSpecifier && Token::isLocationSpecifier(token)) + else if (_options.allowLocationSpecifier && TokenTraits::isLocationSpecifier(token)) { - if (location != VariableDeclaration::Location::Default) + if (location != VariableDeclaration::Location::Unspecified) parserError(string("Location already specified.")); else if (!type) parserError(string("Location specifier needs explicit type name.")); else - location = ( - token == Token::Memory ? - VariableDeclaration::Location::Memory : - VariableDeclaration::Location::Storage - ); + { + switch (token) + { + case Token::Storage: + location = VariableDeclaration::Location::Storage; + break; + case Token::Memory: + location = VariableDeclaration::Location::Memory; + break; + case Token::CallData: + location = VariableDeclaration::Location::CallData; + break; + default: + solAssert(false, "Unknown data location."); + } + } } else break; + nodeFactory.markEndPosition(); m_scanner->next(); } } - nodeFactory.markEndPosition(); if (_options.allowEmptyName && m_scanner->currentToken() != Token::Identifier) { identifier = make_shared<ASTString>(""); solAssert(!_options.allowVar, ""); // allowEmptyName && allowVar makes no sense - if (type) - nodeFactory.setEndPositionFromNode(type); - // if type is null this has already caused an error } else + { + nodeFactory.markEndPosition(); identifier = expectIdentifierToken(); + } ASTPointer<Expression> value; if (_options.allowInitialValue) { @@ -771,15 +806,31 @@ ASTPointer<TypeName> Parser::parseTypeName(bool _allowVar) RecursionGuard recursionGuard(*this); ASTNodeFactory nodeFactory(*this); ASTPointer<TypeName> type; - Token::Value token = m_scanner->currentToken(); - if (Token::isElementaryTypeName(token)) + Token token = m_scanner->currentToken(); + if (TokenTraits::isElementaryTypeName(token)) { unsigned firstSize; unsigned secondSize; tie(firstSize, secondSize) = m_scanner->currentTokenInfo(); ElementaryTypeNameToken elemTypeName(token, firstSize, secondSize); - type = ASTNodeFactory(*this).createNode<ElementaryTypeName>(elemTypeName); + ASTNodeFactory nodeFactory(*this); + nodeFactory.markEndPosition(); m_scanner->next(); + auto stateMutability = boost::make_optional(elemTypeName.token() == Token::Address, StateMutability::NonPayable); + if (TokenTraits::isStateMutabilitySpecifier(m_scanner->currentToken(), false)) + { + if (elemTypeName.token() == Token::Address) + { + nodeFactory.markEndPosition(); + stateMutability = parseStateMutability(); + } + else + { + parserError("State mutability can only be specified for address types."); + m_scanner->next(); + } + } + type = nodeFactory.createNode<ElementaryTypeName>(elemTypeName, stateMutability); } else if (token == Token::Var) { @@ -823,8 +874,8 @@ ASTPointer<Mapping> Parser::parseMapping() expectToken(Token::Mapping); expectToken(Token::LParen); ASTPointer<ElementaryTypeName> keyType; - Token::Value token = m_scanner->currentToken(); - if (!Token::isElementaryTypeName(token)) + Token token = m_scanner->currentToken(); + if (!TokenTraits::isElementaryTypeName(token)) fatalParserError(string("Expected elementary type name for mapping key type")); unsigned firstSize; unsigned secondSize; @@ -928,10 +979,11 @@ ASTPointer<Statement> Parser::parseStatement() } case Token::Assembly: return parseInlineAssembly(docString); + case Token::Emit: + statement = parseEmitStatement(docString); + break; case Token::Identifier: - if (m_scanner->currentLiteral() == "emit") - statement = parseEmitStatement(docString); - else if (m_insideModifier && m_scanner->currentLiteral() == "_") + if (m_insideModifier && m_scanner->currentLiteral() == "_") { statement = ASTNodeFactory(*this).createNode<PlaceholderStatement>(docString); m_scanner->next(); @@ -1051,6 +1103,8 @@ ASTPointer<ForStatement> Parser::parseForStatement(ASTPointer<ASTString> const& ASTPointer<EmitStatement> Parser::parseEmitStatement(ASTPointer<ASTString> const& _docString) { + expectToken(Token::Emit, false); + ASTNodeFactory nodeFactory(*this); m_scanner->next(); ASTNodeFactory eventCallNodeFactory(*this); @@ -1198,7 +1252,7 @@ pair<Parser::LookAheadInfo, Parser::IndexAccessedPath> Parser::tryParseIndexAcce // VariableDeclarationStatement out of it. IndexAccessedPath iap = parseIndexAccessedPath(); - if (m_scanner->currentToken() == Token::Identifier || Token::isLocationSpecifier(m_scanner->currentToken())) + if (m_scanner->currentToken() == Token::Identifier || TokenTraits::isLocationSpecifier(m_scanner->currentToken())) return make_pair(LookAheadInfo::VariableDeclaration, move(iap)); else return make_pair(LookAheadInfo::Expression, move(iap)); @@ -1288,16 +1342,16 @@ ASTPointer<Expression> Parser::parseExpression( { RecursionGuard recursionGuard(*this); ASTPointer<Expression> expression = parseBinaryExpression(4, _partiallyParsedExpression); - if (Token::isAssignmentOp(m_scanner->currentToken())) + if (TokenTraits::isAssignmentOp(m_scanner->currentToken())) { - Token::Value assignmentOperator = m_scanner->currentToken(); + Token assignmentOperator = m_scanner->currentToken(); m_scanner->next(); ASTPointer<Expression> rightHandSide = parseExpression(); ASTNodeFactory nodeFactory(*this, expression); nodeFactory.setEndPositionFromNode(rightHandSide); return nodeFactory.createNode<Assignment>(expression, assignmentOperator, rightHandSide); } - else if (m_scanner->currentToken() == Token::Value::Conditional) + else if (m_scanner->currentToken() == Token::Conditional) { m_scanner->next(); ASTPointer<Expression> trueExpression = parseExpression(); @@ -1319,11 +1373,11 @@ ASTPointer<Expression> Parser::parseBinaryExpression( RecursionGuard recursionGuard(*this); ASTPointer<Expression> expression = parseUnaryExpression(_partiallyParsedExpression); ASTNodeFactory nodeFactory(*this, expression); - int precedence = Token::precedence(m_scanner->currentToken()); + int precedence = TokenTraits::precedence(m_scanner->currentToken()); for (; precedence >= _minPrecedence; --precedence) - while (Token::precedence(m_scanner->currentToken()) == precedence) + while (TokenTraits::precedence(m_scanner->currentToken()) == precedence) { - Token::Value op = m_scanner->currentToken(); + Token op = m_scanner->currentToken(); m_scanner->next(); ASTPointer<Expression> right = parseBinaryExpression(precedence + 1); nodeFactory.setEndPositionFromNode(right); @@ -1339,8 +1393,8 @@ ASTPointer<Expression> Parser::parseUnaryExpression( RecursionGuard recursionGuard(*this); ASTNodeFactory nodeFactory = _partiallyParsedExpression ? ASTNodeFactory(*this, _partiallyParsedExpression) : ASTNodeFactory(*this); - Token::Value token = m_scanner->currentToken(); - if (!_partiallyParsedExpression && (Token::isUnaryOp(token) || Token::isCountOp(token))) + Token token = m_scanner->currentToken(); + if (!_partiallyParsedExpression && (TokenTraits::isUnaryOp(token) || TokenTraits::isCountOp(token))) { // prefix expression m_scanner->next(); @@ -1353,7 +1407,7 @@ ASTPointer<Expression> Parser::parseUnaryExpression( // potential postfix expression ASTPointer<Expression> subExpression = parseLeftHandSideExpression(_partiallyParsedExpression); token = m_scanner->currentToken(); - if (!Token::isCountOp(token)) + if (!TokenTraits::isCountOp(token)) return subExpression; nodeFactory.markEndPosition(); m_scanner->next(); @@ -1428,7 +1482,7 @@ ASTPointer<Expression> Parser::parsePrimaryExpression() { RecursionGuard recursionGuard(*this); ASTNodeFactory nodeFactory(*this); - Token::Value token = m_scanner->currentToken(); + Token token = m_scanner->currentToken(); ASTPointer<Expression> expression; switch (token) @@ -1439,7 +1493,7 @@ ASTPointer<Expression> Parser::parsePrimaryExpression() expression = nodeFactory.createNode<Literal>(token, getLiteralAndAdvance()); break; case Token::Number: - if (Token::isEtherSubdenomination(m_scanner->peekNextToken())) + if (TokenTraits::isEtherSubdenomination(m_scanner->peekNextToken())) { ASTPointer<ASTString> literal = getLiteralAndAdvance(); nodeFactory.markEndPosition(); @@ -1447,7 +1501,7 @@ ASTPointer<Expression> Parser::parsePrimaryExpression() m_scanner->next(); expression = nodeFactory.createNode<Literal>(token, literal, subdenomination); } - else if (Token::isTimeSubdenomination(m_scanner->peekNextToken())) + else if (TokenTraits::isTimeSubdenomination(m_scanner->peekNextToken())) { ASTPointer<ASTString> literal = getLiteralAndAdvance(); nodeFactory.markEndPosition(); @@ -1477,7 +1531,7 @@ ASTPointer<Expression> Parser::parsePrimaryExpression() // (x,) is one-dimensional tuple, elements in arrays cannot be left out, only in tuples. m_scanner->next(); vector<ASTPointer<Expression>> components; - Token::Value oppositeToken = (token == Token::LParen ? Token::RParen : Token::RBrack); + Token oppositeToken = (token == Token::LParen ? Token::RParen : Token::RBrack); bool isArray = (token == Token::LBrack); if (m_scanner->currentToken() != oppositeToken) @@ -1500,8 +1554,11 @@ ASTPointer<Expression> Parser::parsePrimaryExpression() expression = nodeFactory.createNode<TupleExpression>(components, isArray); break; } + case Token::IllegalHex: + fatalParserError("Expected even number of hex-nibbles within double-quotes."); + break; default: - if (Token::isElementaryTypeName(token)) + if (TokenTraits::isElementaryTypeName(token)) { //used for casts unsigned firstSize; @@ -1538,7 +1595,7 @@ pair<vector<ASTPointer<Expression>>, vector<ASTPointer<ASTString>>> Parser::pars { RecursionGuard recursionGuard(*this); pair<vector<ASTPointer<Expression>>, vector<ASTPointer<ASTString>>> ret; - Token::Value token = m_scanner->currentToken(); + Token token = m_scanner->currentToken(); if (token == Token::LBrace) { // call({arg1 : 1, arg2 : 2 }) @@ -1577,19 +1634,25 @@ Parser::LookAheadInfo Parser::peekStatementType() const // Distinguish between variable declaration (and potentially assignment) and expression statement // (which include assignments to other expressions and pre-declared variables). // We have a variable declaration if we get a keyword that specifies a type name. - // If it is an identifier or an elementary type name followed by an identifier, we also have - // a variable declaration. + // If it is an identifier or an elementary type name followed by an identifier + // or a mutability specifier, we also have a variable declaration. // If we get an identifier followed by a "[" or ".", it can be both ("lib.type[9] a;" or "variable.el[9] = 7;"). // In all other cases, we have an expression statement. - Token::Value token(m_scanner->currentToken()); - bool mightBeTypeName = (Token::isElementaryTypeName(token) || token == Token::Identifier); + Token token(m_scanner->currentToken()); + bool mightBeTypeName = (TokenTraits::isElementaryTypeName(token) || token == Token::Identifier); if (token == Token::Mapping || token == Token::Function || token == Token::Var) return LookAheadInfo::VariableDeclaration; if (mightBeTypeName) { - Token::Value next = m_scanner->peekNextToken(); - if (next == Token::Identifier || Token::isLocationSpecifier(next)) + Token next = m_scanner->peekNextToken(); + // So far we only allow ``address payable`` in variable declaration statements and in no other + // kind of statement. This means, for example, that we do not allow type expressions of the form + // ``address payable;``. + // If we want to change this in the future, we need to consider another scanner token here. + if (TokenTraits::isElementaryTypeName(token) && TokenTraits::isStateMutabilitySpecifier(next, false)) + return LookAheadInfo::VariableDeclaration; + if (next == Token::Identifier || TokenTraits::isLocationSpecifier(next)) return LookAheadInfo::VariableDeclaration; if (next == Token::LBrack || next == Token::Period) return LookAheadInfo::IndexAccessStructure; diff --git a/libsolidity/parsing/Parser.h b/libsolidity/parsing/Parser.h index 08653364..fa974171 100644 --- a/libsolidity/parsing/Parser.h +++ b/libsolidity/parsing/Parser.h @@ -69,17 +69,13 @@ private: ///@name Parsing functions for the AST nodes ASTPointer<PragmaDirective> parsePragmaDirective(); ASTPointer<ImportDirective> parseImportDirective(); - ContractDefinition::ContractKind tokenToContractKind(Token::Value _token); - ASTPointer<ContractDefinition> parseContractDefinition(Token::Value _expectedKind); + ContractDefinition::ContractKind parseContractKind(); + ASTPointer<ContractDefinition> parseContractDefinition(); ASTPointer<InheritanceSpecifier> parseInheritanceSpecifier(); - Declaration::Visibility parseVisibilitySpecifier(Token::Value _token); - StateMutability parseStateMutability(Token::Value _token); - FunctionHeaderParserResult parseFunctionHeader( - bool _forceEmptyName, - bool _allowModifiers, - ASTString const* _contractName = nullptr - ); - ASTPointer<ASTNode> parseFunctionDefinitionOrFunctionTypeStateVariable(ASTString const* _contractName); + Declaration::Visibility parseVisibilitySpecifier(); + StateMutability parseStateMutability(); + FunctionHeaderParserResult parseFunctionHeader(bool _forceEmptyName, bool _allowModifiers); + ASTPointer<ASTNode> parseFunctionDefinitionOrFunctionTypeStateVariable(); ASTPointer<FunctionDefinition> parseFunctionDefinition(ASTString const* _contractName); ASTPointer<StructDefinition> parseStructDefinition(); ASTPointer<EnumDefinition> parseEnumDefinition(); diff --git a/libsolidity/parsing/ParserBase.cpp b/libsolidity/parsing/ParserBase.cpp index 71133746..1d4cb1e2 100644 --- a/libsolidity/parsing/ParserBase.cpp +++ b/libsolidity/parsing/ParserBase.cpp @@ -43,12 +43,12 @@ int ParserBase::endPosition() const return m_scanner->currentLocation().end; } -Token::Value ParserBase::currentToken() const +Token ParserBase::currentToken() const { return m_scanner->currentToken(); } -Token::Value ParserBase::peekNextToken() const +Token ParserBase::peekNextToken() const { return m_scanner->peekNextToken(); } @@ -58,31 +58,31 @@ std::string ParserBase::currentLiteral() const return m_scanner->currentLiteral(); } -Token::Value ParserBase::advance() +Token ParserBase::advance() { return m_scanner->next(); } -void ParserBase::expectToken(Token::Value _value, bool _advance) +void ParserBase::expectToken(Token _value, bool _advance) { - Token::Value tok = m_scanner->currentToken(); + Token tok = m_scanner->currentToken(); if (tok != _value) { - auto tokenName = [this](Token::Value _token) + auto tokenName = [this](Token _token) { if (_token == Token::Identifier) return string("identifier"); else if (_token == Token::EOS) return string("end of source"); - else if (Token::isReservedKeyword(_token)) - return string("reserved keyword '") + Token::friendlyName(_token) + "'"; - else if (Token::isElementaryTypeName(_token)) //for the sake of accuracy in reporting + else if (TokenTraits::isReservedKeyword(_token)) + return string("reserved keyword '") + TokenTraits::friendlyName(_token) + "'"; + else if (TokenTraits::isElementaryTypeName(_token)) //for the sake of accuracy in reporting { ElementaryTypeNameToken elemTypeName = m_scanner->currentElementaryTypeNameToken(); return string("'") + elemTypeName.toString() + "'"; } else - return string("'") + Token::friendlyName(_token) + "'"; + return string("'") + TokenTraits::friendlyName(_token) + "'"; }; fatalParserError(string("Expected ") + tokenName(_value) + string(" but got ") + tokenName(tok)); diff --git a/libsolidity/parsing/ParserBase.h b/libsolidity/parsing/ParserBase.h index b28e1b1b..e01f37d8 100644 --- a/libsolidity/parsing/ParserBase.h +++ b/libsolidity/parsing/ParserBase.h @@ -63,11 +63,11 @@ protected: ///@{ ///@name Helper functions /// If current token value is not _value, throw exception otherwise advance token. - void expectToken(Token::Value _value, bool _advance = true); - Token::Value currentToken() const; - Token::Value peekNextToken() const; + void expectToken(Token _value, bool _advance = true); + Token currentToken() const; + Token peekNextToken() const; std::string currentLiteral() const; - Token::Value advance(); + Token advance(); ///@} /// Increases the recursion depth and throws an exception if it is too deep. diff --git a/libsolidity/parsing/Scanner.cpp b/libsolidity/parsing/Scanner.cpp index dbe1f389..e9dad2ad 100644 --- a/libsolidity/parsing/Scanner.cpp +++ b/libsolidity/parsing/Scanner.cpp @@ -214,9 +214,9 @@ void Scanner::addUnicodeAsUTF8(unsigned codepoint) } // Ensure that tokens can be stored in a byte. -BOOST_STATIC_ASSERT(Token::NUM_TOKENS <= 0x100); +BOOST_STATIC_ASSERT(TokenTraits::count() <= 0x100); -Token::Value Scanner::next() +Token Scanner::next() { m_currentToken = m_nextToken; m_skippedComment = m_nextSkippedComment; @@ -225,7 +225,7 @@ Token::Value Scanner::next() return m_currentToken.token; } -Token::Value Scanner::selectToken(char _next, Token::Value _then, Token::Value _else) +Token Scanner::selectToken(char _next, Token _then, Token _else) { advance(); if (m_char == _next) @@ -249,7 +249,7 @@ void Scanner::skipWhitespaceExceptUnicodeLinebreak() advance(); } -Token::Value Scanner::skipSingleLineComment() +Token Scanner::skipSingleLineComment() { // Line terminator is not part of the comment. If it is a // non-ascii line terminator, it will result in a parser error. @@ -259,7 +259,7 @@ Token::Value Scanner::skipSingleLineComment() return Token::Whitespace; } -Token::Value Scanner::scanSingleLineDocComment() +Token Scanner::scanSingleLineDocComment() { LiteralScope literal(this, LITERAL_TYPE_COMMENT); advance(); //consume the last '/' at /// @@ -295,7 +295,7 @@ Token::Value Scanner::scanSingleLineDocComment() return Token::CommentLiteral; } -Token::Value Scanner::skipMultiLineComment() +Token Scanner::skipMultiLineComment() { advance(); while (!isSourcePastEndOfInput()) @@ -316,7 +316,7 @@ Token::Value Scanner::skipMultiLineComment() return Token::Illegal; } -Token::Value Scanner::scanMultiLineDocComment() +Token Scanner::scanMultiLineDocComment() { LiteralScope literal(this, LITERAL_TYPE_COMMENT); bool endFound = false; @@ -369,7 +369,7 @@ Token::Value Scanner::scanMultiLineDocComment() return Token::CommentLiteral; } -Token::Value Scanner::scanSlash() +Token Scanner::scanSlash() { int firstSlashPosition = sourcePos(); advance(); @@ -380,7 +380,7 @@ Token::Value Scanner::scanSlash() else if (m_char == '/') { // doxygen style /// comment - Token::Value comment; + Token comment; m_nextSkippedComment.location.start = firstSlashPosition; comment = scanSingleLineDocComment(); m_nextSkippedComment.location.end = sourcePos(); @@ -406,7 +406,7 @@ Token::Value Scanner::scanSlash() return Token::Whitespace; } // we actually have a multiline documentation comment - Token::Value comment; + Token comment; m_nextSkippedComment.location.start = firstSlashPosition; comment = scanMultiLineDocComment(); m_nextSkippedComment.location.end = sourcePos(); @@ -432,7 +432,7 @@ void Scanner::scanToken() m_nextSkippedComment.literal.clear(); m_nextSkippedComment.extendedTokenInfo = make_tuple(0, 0); - Token::Value token; + Token token; // M and N are for the purposes of grabbing different type sizes unsigned m; unsigned n; @@ -601,7 +601,7 @@ void Scanner::scanToken() { tie(token, m, n) = scanIdentifierOrKeyword(); - // Special case for hexademical literals + // Special case for hexadecimal literals if (token == Token::Hex) { // reset @@ -612,7 +612,7 @@ void Scanner::scanToken() if (m_char == '"' || m_char == '\'') token = scanHexString(); else - token = Token::Illegal; + token = Token::IllegalHex; } } else if (isDecimalDigit(m_char)) @@ -703,7 +703,7 @@ bool Scanner::isUnicodeLinebreak() return false; } -Token::Value Scanner::scanString() +Token Scanner::scanString() { char const quote = m_char; advance(); // consume quote @@ -727,7 +727,7 @@ Token::Value Scanner::scanString() return Token::StringLiteral; } -Token::Value Scanner::scanHexString() +Token Scanner::scanHexString() { char const quote = m_char; advance(); // consume quote @@ -736,23 +736,31 @@ Token::Value Scanner::scanHexString() { char c = m_char; if (!scanHexByte(c)) - return Token::Illegal; + return Token::IllegalHex; addLiteralChar(c); } if (m_char != quote) - return Token::Illegal; + return Token::IllegalHex; literal.complete(); advance(); // consume quote return Token::StringLiteral; } +// Parse for regex [:digit:]+(_[:digit:]+)* void Scanner::scanDecimalDigits() { - while (isDecimalDigit(m_char)) - addLiteralCharAndAdvance(); + // MUST begin with a decimal digit. + if (!isDecimalDigit(m_char)) + return; + + // May continue with decimal digit or underscore for grouping. + do addLiteralCharAndAdvance(); + while (!m_source.isPastEndOfInput() && (isDecimalDigit(m_char) || m_char == '_')); + + // Defer further validation of underscore to SyntaxChecker. } -Token::Value Scanner::scanNumber(char _charSeen) +Token Scanner::scanNumber(char _charSeen) { enum { DECIMAL, HEX, BINARY } kind = DECIMAL; LiteralScope literal(this, LITERAL_TYPE_NUMBER); @@ -760,6 +768,8 @@ Token::Value Scanner::scanNumber(char _charSeen) { // we have already seen a decimal point of the float addLiteralChar('.'); + if (m_char == '_') + return Token::Illegal; scanDecimalDigits(); // we know we have at least one digit } else @@ -770,14 +780,15 @@ Token::Value Scanner::scanNumber(char _charSeen) { addLiteralCharAndAdvance(); // either 0, 0exxx, 0Exxx, 0.xxx or a hex number - if (m_char == 'x' || m_char == 'X') + if (m_char == 'x') { // hex number kind = HEX; addLiteralCharAndAdvance(); if (!isHexDigit(m_char)) - return Token::Illegal; // we must have at least one hex digit after 'x'/'X' - while (isHexDigit(m_char)) + return Token::Illegal; // we must have at least one hex digit after 'x' + + while (isHexDigit(m_char) || m_char == '_') // We keep the underscores for later validation addLiteralCharAndAdvance(); } else if (isDecimalDigit(m_char)) @@ -790,8 +801,22 @@ Token::Value Scanner::scanNumber(char _charSeen) scanDecimalDigits(); // optional if (m_char == '.') { + if (!m_source.isPastEndOfInput(1) && m_source.get(1) == '_') + { + // Assume the input may be a floating point number with leading '_' in fraction part. + // Recover by consuming it all but returning `Illegal` right away. + addLiteralCharAndAdvance(); // '.' + addLiteralCharAndAdvance(); // '_' + scanDecimalDigits(); + } + if (m_source.isPastEndOfInput() || !isDecimalDigit(m_source.get(1))) + { + // A '.' has to be followed by a number. + literal.complete(); + return Token::Number; + } addLiteralCharAndAdvance(); - scanDecimalDigits(); // optional + scanDecimalDigits(); } } } @@ -801,8 +826,18 @@ Token::Value Scanner::scanNumber(char _charSeen) solAssert(kind != HEX, "'e'/'E' must be scanned as part of the hex number"); if (kind != DECIMAL) return Token::Illegal; + else if (!m_source.isPastEndOfInput(1) && m_source.get(1) == '_') + { + // Recover from wrongly placed underscore as delimiter in literal with scientific + // notation by consuming until the end. + addLiteralCharAndAdvance(); // 'e' + addLiteralCharAndAdvance(); // '_' + scanDecimalDigits(); + literal.complete(); + return Token::Number; + } // scan exponent - addLiteralCharAndAdvance(); + addLiteralCharAndAdvance(); // 'e' | 'E' if (m_char == '+' || m_char == '-') addLiteralCharAndAdvance(); if (!isDecimalDigit(m_char)) @@ -819,7 +854,7 @@ Token::Value Scanner::scanNumber(char _charSeen) return Token::Number; } -tuple<Token::Value, unsigned, unsigned> Scanner::scanIdentifierOrKeyword() +tuple<Token, unsigned, unsigned> Scanner::scanIdentifierOrKeyword() { solAssert(isIdentifierStart(m_char), ""); LiteralScope literal(this, LITERAL_TYPE_STRING); @@ -828,7 +863,7 @@ tuple<Token::Value, unsigned, unsigned> Scanner::scanIdentifierOrKeyword() while (isIdentifierPart(m_char)) //get full literal addLiteralCharAndAdvance(); literal.complete(); - return Token::fromIdentifierOrKeyword(m_nextToken.literal); + return TokenTraits::fromIdentifierOrKeyword(m_nextToken.literal); } char CharStream::advanceAndGet(size_t _chars) diff --git a/libsolidity/parsing/Scanner.h b/libsolidity/parsing/Scanner.h index 602532e4..14eeb66e 100644 --- a/libsolidity/parsing/Scanner.h +++ b/libsolidity/parsing/Scanner.h @@ -112,13 +112,13 @@ public: void reset(); /// @returns the next token and advances input - Token::Value next(); + Token next(); ///@{ ///@name Information about the current token /// @returns the current token - Token::Value currentToken() const + Token currentToken() const { return m_currentToken.token; } @@ -149,7 +149,7 @@ public: ///@name Information about the next token /// @returns the next token without advancing input. - Token::Value peekNextToken() const { return m_nextToken.token; } + Token peekNextToken() const { return m_nextToken.token; } SourceLocation peekLocation() const { return m_nextToken.location; } std::string const& peekLiteral() const { return m_nextToken.literal; } ///@} @@ -162,13 +162,20 @@ public: /// Do only use in error cases, they are quite expensive. std::string lineAtPosition(int _position) const { return m_source.lineAtPosition(_position); } std::tuple<int, int> translatePositionToLineColumn(int _position) const { return m_source.translatePositionToLineColumn(_position); } + std::string sourceAt(SourceLocation const& _location) const + { + solAssert(!_location.isEmpty(), ""); + solAssert(m_sourceName && _location.sourceName, ""); + solAssert(*m_sourceName == *_location.sourceName, ""); + return m_source.source().substr(_location.start, _location.end - _location.start); + } ///@} private: /// Used for the current and look-ahead token and comments struct TokenDesc { - Token::Value token; + Token token; SourceLocation location; std::string literal; std::tuple<unsigned, unsigned> extendedTokenInfo; @@ -185,9 +192,9 @@ private: bool advance() { m_char = m_source.advanceAndGet(); return !m_source.isPastEndOfInput(); } void rollback(int _amount) { m_char = m_source.rollback(_amount); } - inline Token::Value selectToken(Token::Value _tok) { advance(); return _tok; } + inline Token selectToken(Token _tok) { advance(); return _tok; } /// If the next character is _next, advance and return _then, otherwise return _else. - inline Token::Value selectToken(char _next, Token::Value _then, Token::Value _else); + inline Token selectToken(char _next, Token _then, Token _else); bool scanHexByte(char& o_scannedByte); bool scanUnicode(unsigned& o_codepoint); @@ -199,19 +206,19 @@ private: bool skipWhitespace(); /// Skips all whitespace that are neither '\r' nor '\n'. void skipWhitespaceExceptUnicodeLinebreak(); - Token::Value skipSingleLineComment(); - Token::Value skipMultiLineComment(); + Token skipSingleLineComment(); + Token skipMultiLineComment(); void scanDecimalDigits(); - Token::Value scanNumber(char _charSeen = 0); - std::tuple<Token::Value, unsigned, unsigned> scanIdentifierOrKeyword(); + Token scanNumber(char _charSeen = 0); + std::tuple<Token, unsigned, unsigned> scanIdentifierOrKeyword(); - Token::Value scanString(); - Token::Value scanHexString(); - Token::Value scanSingleLineDocComment(); - Token::Value scanMultiLineDocComment(); + Token scanString(); + Token scanHexString(); + Token scanSingleLineDocComment(); + Token scanMultiLineDocComment(); /// Scans a slash '/' and depending on the characters returns the appropriate token - Token::Value scanSlash(); + Token scanSlash(); /// Scans an escape-sequence which is part of a string and adds the /// decoded character to the current literal. Returns true if a pattern @@ -226,7 +233,7 @@ private: bool isSourcePastEndOfInput() const { return m_source.isPastEndOfInput(); } TokenDesc m_skippedComment; // desc for current skipped comment - TokenDesc m_nextSkippedComment; // desc for next skiped comment + TokenDesc m_nextSkippedComment; // desc for next skipped comment TokenDesc m_currentToken; // desc for current token (as returned by Next()) TokenDesc m_nextToken; // desc for next token (one token look-ahead) diff --git a/libsolidity/parsing/Token.cpp b/libsolidity/parsing/Token.cpp index 5ce74316..dccd9037 100644 --- a/libsolidity/parsing/Token.cpp +++ b/libsolidity/parsing/Token.cpp @@ -51,9 +51,9 @@ namespace dev namespace solidity { -void ElementaryTypeNameToken::assertDetails(Token::Value _baseType, unsigned const& _first, unsigned const& _second) +void ElementaryTypeNameToken::assertDetails(Token _baseType, unsigned const& _first, unsigned const& _second) { - solAssert(Token::isElementaryTypeName(_baseType), "Expected elementary type name: " + string(Token::toString(_baseType))); + solAssert(TokenTraits::isElementaryTypeName(_baseType), "Expected elementary type name: " + string(TokenTraits::toString(_baseType))); if (_baseType == Token::BytesM) { solAssert(_second == 0, "There should not be a second size argument to type bytesM."); @@ -61,17 +61,17 @@ void ElementaryTypeNameToken::assertDetails(Token::Value _baseType, unsigned con } else if (_baseType == Token::UIntM || _baseType == Token::IntM) { - solAssert(_second == 0, "There should not be a second size argument to type " + string(Token::toString(_baseType)) + "."); + solAssert(_second == 0, "There should not be a second size argument to type " + string(TokenTraits::toString(_baseType)) + "."); solAssert( - _first <= 256 && _first % 8 == 0, - "No elementary type " + string(Token::toString(_baseType)) + to_string(_first) + "." + _first <= 256 && _first % 8 == 0, + "No elementary type " + string(TokenTraits::toString(_baseType)) + to_string(_first) + "." ); } else if (_baseType == Token::UFixedMxN || _baseType == Token::FixedMxN) { solAssert( _first >= 8 && _first <= 256 && _first % 8 == 0 && _second <= 80, - "No elementary type " + string(Token::toString(_baseType)) + to_string(_first) + "x" + to_string(_second) + "." + "No elementary type " + string(TokenTraits::toString(_baseType)) + to_string(_first) + "x" + to_string(_second) + "." ); } m_token = _baseType; @@ -79,38 +79,54 @@ void ElementaryTypeNameToken::assertDetails(Token::Value _baseType, unsigned con m_secondNumber = _second; } -#define T(name, string, precedence) #name, -char const* const Token::m_name[NUM_TOKENS] = +namespace TokenTraits { - TOKEN_LIST(T, T) -}; -#undef T +char const* toString(Token tok) +{ + switch (tok) + { +#define T(name, string, precedence) case Token::name: return string; + TOKEN_LIST(T, T) +#undef T + default: // Token::NUM_TOKENS: + return ""; + } +} -#define T(name, string, precedence) string, -char const* const Token::m_string[NUM_TOKENS] = +char const* name(Token tok) { - TOKEN_LIST(T, T) -}; +#define T(name, string, precedence) #name, + static char const* const names[TokenTraits::count()] = { TOKEN_LIST(T, T) }; #undef T + solAssert(static_cast<size_t>(tok) < TokenTraits::count(), ""); + return names[static_cast<size_t>(tok)]; +} -#define T(name, string, precedence) precedence, -int8_t const Token::m_precedence[NUM_TOKENS] = +std::string friendlyName(Token tok) { - TOKEN_LIST(T, T) -}; -#undef T + char const* ret = toString(tok); + if (ret) + return std::string(ret); + ret = name(tok); + solAssert(ret != nullptr, ""); + return std::string(ret); +} -#define KT(a, b, c) 'T', -#define KK(a, b, c) 'K', -char const Token::m_tokenType[] = +#define T(name, string, precedence) precedence, +int precedence(Token tok) { - TOKEN_LIST(KT, KK) -}; + int8_t const static precs[TokenTraits::count()] = + { + TOKEN_LIST(T, T) + }; + return precs[static_cast<size_t>(tok)]; +} +#undef T -int Token::parseSize(string::const_iterator _begin, string::const_iterator _end) +int parseSize(string::const_iterator _begin, string::const_iterator _end) { try { @@ -123,7 +139,20 @@ int Token::parseSize(string::const_iterator _begin, string::const_iterator _end) } } -tuple<Token::Value, unsigned int, unsigned int> Token::fromIdentifierOrKeyword(string const& _literal) +static Token keywordByName(string const& _name) +{ + // The following macros are used inside TOKEN_LIST and cause non-keyword tokens to be ignored + // and keywords to be put inside the keywords variable. +#define KEYWORD(name, string, precedence) {string, Token::name}, +#define TOKEN(name, string, precedence) + static const map<string, Token> keywords({TOKEN_LIST(TOKEN, KEYWORD)}); +#undef KEYWORD +#undef TOKEN + auto it = keywords.find(_name); + return it == keywords.end() ? Token::Identifier : it->second; +} + +tuple<Token, unsigned int, unsigned int> fromIdentifierOrKeyword(string const& _literal) { auto positionM = find_if(_literal.begin(), _literal.end(), ::isdigit); if (positionM != _literal.end()) @@ -131,7 +160,7 @@ tuple<Token::Value, unsigned int, unsigned int> Token::fromIdentifierOrKeyword(s string baseType(_literal.begin(), positionM); auto positionX = find_if_not(positionM, _literal.end(), ::isdigit); int m = parseSize(positionM, positionX); - Token::Value keyword = keywordByName(baseType); + Token keyword = keywordByName(baseType); if (keyword == Token::Bytes) { if (0 < m && m <= 32 && positionX == _literal.end()) @@ -165,27 +194,14 @@ tuple<Token::Value, unsigned int, unsigned int> Token::fromIdentifierOrKeyword(s else return make_tuple(Token::FixedMxN, m, n); } - } + } } return make_tuple(Token::Identifier, 0, 0); } return make_tuple(keywordByName(_literal), 0, 0); } -Token::Value Token::keywordByName(string const& _name) -{ - // The following macros are used inside TOKEN_LIST and cause non-keyword tokens to be ignored - // and keywords to be put inside the keywords variable. -#define KEYWORD(name, string, precedence) {string, Token::name}, -#define TOKEN(name, string, precedence) - static const map<string, Token::Value> keywords({TOKEN_LIST(TOKEN, KEYWORD)}); -#undef KEYWORD -#undef TOKEN - auto it = keywords.find(_name); - return it == keywords.end() ? Token::Identifier : it->second; -} -#undef KT -#undef KK +} } } diff --git a/libsolidity/parsing/Token.h b/libsolidity/parsing/Token.h index 4d7a7bc6..81e8dd98 100644 --- a/libsolidity/parsing/Token.h +++ b/libsolidity/parsing/Token.h @@ -45,6 +45,7 @@ #include <libdevcore/Common.h> #include <libsolidity/interface/Exceptions.h> #include <libsolidity/parsing/UndefMacros.h> +#include <iosfwd> namespace dev { @@ -144,11 +145,13 @@ namespace solidity K(Assembly, "assembly", 0) \ K(Break, "break", 0) \ K(Constant, "constant", 0) \ + K(Constructor, "constructor", 0) \ K(Continue, "continue", 0) \ K(Contract, "contract", 0) \ K(Do, "do", 0) \ K(Else, "else", 0) \ K(Enum, "enum", 0) \ + K(Emit, "emit", 0) \ K(Event, "event", 0) \ K(External, "external", 0) \ K(For, "for", 0) \ @@ -173,6 +176,7 @@ namespace solidity K(Return, "return", 0) \ K(Returns, "returns", 0) \ K(Storage, "storage", 0) \ + K(CallData, "calldata", 0) \ K(Struct, "struct", 0) \ K(Throw, "throw", 0) \ K(Using, "using", 0) \ @@ -221,136 +225,138 @@ namespace solidity /* Keywords reserved for future use. */ \ K(Abstract, "abstract", 0) \ K(After, "after", 0) \ + K(Alias, "alias", 0) \ + K(Apply, "apply", 0) \ + K(Auto, "auto", 0) \ K(Case, "case", 0) \ K(Catch, "catch", 0) \ + K(CopyOf, "copyof", 0) \ K(Default, "default", 0) \ + K(Define, "define", 0) \ K(Final, "final", 0) \ + K(Immutable, "immutable", 0) \ + K(Implements, "implements", 0) \ K(In, "in", 0) \ K(Inline, "inline", 0) \ K(Let, "let", 0) \ + K(Macro, "macro", 0) \ K(Match, "match", 0) \ + K(Mutable, "mutable", 0) \ K(NullLiteral, "null", 0) \ K(Of, "of", 0) \ + K(Override, "override", 0) \ + K(Partial, "partial", 0) \ + K(Promise, "promise", 0) \ + K(Reference, "reference", 0) \ K(Relocatable, "relocatable", 0) \ + K(Sealed, "sealed", 0) \ + K(Sizeof, "sizeof", 0) \ K(Static, "static", 0) \ + K(Supports, "supports", 0) \ K(Switch, "switch", 0) \ K(Try, "try", 0) \ K(Type, "type", 0) \ + K(Typedef, "typedef", 0) \ K(TypeOf, "typeof", 0) \ + K(Unchecked, "unchecked", 0) \ + \ /* Illegal token - not able to scan. */ \ T(Illegal, "ILLEGAL", 0) \ + /* Illegal hex token */ \ + T(IllegalHex, "ILLEGAL_HEX", 0) \ \ /* Scanner-internal use only. */ \ T(Whitespace, NULL, 0) - -class Token -{ -public: - // All token values. - // attention! msvc issue: - // http://stackoverflow.com/questions/9567868/compile-errors-after-adding-v8-to-my-project-c2143-c2059 - // @todo: avoid TOKEN_LIST macro +// All token values. +// attention! msvc issue: +// http://stackoverflow.com/questions/9567868/compile-errors-after-adding-v8-to-my-project-c2143-c2059 +// @todo: avoid TOKEN_LIST macro +enum class Token : unsigned int { #define T(name, string, precedence) name, - enum Value - { - TOKEN_LIST(T, T) - NUM_TOKENS - }; + TOKEN_LIST(T, T) + NUM_TOKENS #undef T +}; - // @returns a string corresponding to the C++ token name - // (e.g. "LT" for the token LT). - static char const* name(Value tok) - { - solAssert(tok < NUM_TOKENS, ""); - return m_name[tok]; - } +namespace TokenTraits +{ + constexpr size_t count() { return static_cast<size_t>(Token::NUM_TOKENS); } // Predicates - static bool isElementaryTypeName(Value tok) { return Int <= tok && tok < TypesEnd; } - static bool isAssignmentOp(Value tok) { return Assign <= tok && tok <= AssignMod; } - static bool isBinaryOp(Value op) { return Comma <= op && op <= Exp; } - static bool isCommutativeOp(Value op) { return op == BitOr || op == BitXor || op == BitAnd || - op == Add || op == Mul || op == Equal || op == NotEqual; } - static bool isArithmeticOp(Value op) { return Add <= op && op <= Exp; } - static bool isCompareOp(Value op) { return Equal <= op && op <= GreaterThanOrEqual; } - - static Value AssignmentToBinaryOp(Value op) - { - solAssert(isAssignmentOp(op) && op != Assign, ""); - return Value(op + (BitOr - AssignBitOr)); - } + constexpr bool isElementaryTypeName(Token tok) { return Token::Int <= tok && tok < Token::TypesEnd; } + constexpr bool isAssignmentOp(Token tok) { return Token::Assign <= tok && tok <= Token::AssignMod; } + constexpr bool isBinaryOp(Token op) { return Token::Comma <= op && op <= Token::Exp; } + constexpr bool isCommutativeOp(Token op) { return op == Token::BitOr || op == Token::BitXor || op == Token::BitAnd || + op == Token::Add || op == Token::Mul || op == Token::Equal || op == Token::NotEqual; } + constexpr bool isArithmeticOp(Token op) { return Token::Add <= op && op <= Token::Exp; } + constexpr bool isCompareOp(Token op) { return Token::Equal <= op && op <= Token::GreaterThanOrEqual; } - static bool isBitOp(Value op) { return (BitOr <= op && op <= BitAnd) || op == BitNot; } - static bool isBooleanOp(Value op) { return (Or <= op && op <= And) || op == Not; } - static bool isUnaryOp(Value op) { return (Not <= op && op <= Delete) || op == Add || op == Sub; } - static bool isCountOp(Value op) { return op == Inc || op == Dec; } - static bool isShiftOp(Value op) { return (SHL <= op) && (op <= SHR); } - static bool isVisibilitySpecifier(Value op) { return isVariableVisibilitySpecifier(op) || op == External; } - static bool isVariableVisibilitySpecifier(Value op) { return op == Public || op == Private || op == Internal; } - static bool isLocationSpecifier(Value op) { return op == Memory || op == Storage; } - static bool isStateMutabilitySpecifier(Value op) { return op == Pure || op == Constant || op == View || op == Payable; } - static bool isEtherSubdenomination(Value op) { return op == SubWei || op == SubSzabo || op == SubFinney || op == SubEther; } - static bool isTimeSubdenomination(Value op) { return op == SubSecond || op == SubMinute || op == SubHour || op == SubDay || op == SubWeek || op == SubYear; } - static bool isReservedKeyword(Value op) { return (Abstract <= op && op <= TypeOf); } + constexpr bool isBitOp(Token op) { return (Token::BitOr <= op && op <= Token::BitAnd) || op == Token::BitNot; } + constexpr bool isBooleanOp(Token op) { return (Token::Or <= op && op <= Token::And) || op == Token::Not; } + constexpr bool isUnaryOp(Token op) { return (Token::Not <= op && op <= Token::Delete) || op == Token::Add || op == Token::Sub; } + constexpr bool isCountOp(Token op) { return op == Token::Inc || op == Token::Dec; } + constexpr bool isShiftOp(Token op) { return (Token::SHL <= op) && (op <= Token::SHR); } + constexpr bool isVariableVisibilitySpecifier(Token op) { return op == Token::Public || op == Token::Private || op == Token::Internal; } + constexpr bool isVisibilitySpecifier(Token op) { return isVariableVisibilitySpecifier(op) || op == Token::External; } + constexpr bool isLocationSpecifier(Token op) { return op == Token::Memory || op == Token::Storage || op == Token::CallData; } - // @returns a string corresponding to the JS token string - // (.e., "<" for the token LT) or NULL if the token doesn't - // have a (unique) string (e.g. an IDENTIFIER). - static char const* toString(Value tok) + constexpr bool isStateMutabilitySpecifier(Token op, bool _allowConstant = true) { - solAssert(tok < NUM_TOKENS, ""); - return m_string[tok]; + return (op == Token::Constant && _allowConstant) + || op == Token::Pure || op == Token::View || op == Token::Payable; } - static std::string friendlyName(Value tok) + constexpr bool isEtherSubdenomination(Token op) { return op == Token::SubWei || op == Token::SubSzabo || op == Token::SubFinney || op == Token::SubEther; } + constexpr bool isTimeSubdenomination(Token op) { return op == Token::SubSecond || op == Token::SubMinute || op == Token::SubHour || op == Token::SubDay || op == Token::SubWeek || op == Token::SubYear; } + constexpr bool isReservedKeyword(Token op) { return (Token::Abstract <= op && op <= Token::Unchecked); } + + inline Token AssignmentToBinaryOp(Token op) { - char const* ret = toString(tok); - if (ret == nullptr) - { - ret = name(tok); - solAssert(ret != nullptr, ""); - } - return std::string(ret); + solAssert(isAssignmentOp(op) && op != Token::Assign, ""); + return static_cast<Token>(static_cast<int>(op) + (static_cast<int>(Token::BitOr) - static_cast<int>(Token::AssignBitOr))); } // @returns the precedence > 0 for binary and compare // operators; returns 0 otherwise. - static int precedence(Value tok) - { - solAssert(tok < NUM_TOKENS, ""); - return m_precedence[tok]; - } + int precedence(Token tok); - static std::tuple<Token::Value, unsigned int, unsigned int> fromIdentifierOrKeyword(std::string const& _literal); + std::tuple<Token, unsigned int, unsigned int> fromIdentifierOrKeyword(std::string const& _literal); -private: - // @returns -1 on error (invalid digit or number too large) - static int parseSize(std::string::const_iterator _begin, std::string::const_iterator _end); - // @returns the keyword with name @a _name or Token::Identifier of no such keyword exists. - static Token::Value keywordByName(std::string const& _name); - static char const* const m_name[NUM_TOKENS]; - static char const* const m_string[NUM_TOKENS]; - static int8_t const m_precedence[NUM_TOKENS]; - static char const m_tokenType[NUM_TOKENS]; -}; + // @returns a string corresponding to the C++ token name + // (e.g. "LT" for the token LT). + char const* name(Token tok); + + // @returns a string corresponding to the JS token string + // (.e., "<" for the token LT) or NULL if the token doesn't + // have a (unique) string (e.g. an IDENTIFIER). + char const* toString(Token tok); + + std::string friendlyName(Token tok); +} + +inline std::ostream& operator<<(std::ostream& os, Token token) +{ + os << TokenTraits::friendlyName(token); + return os; +} class ElementaryTypeNameToken { public: - ElementaryTypeNameToken(Token::Value _token, unsigned const& _firstNumber, unsigned const& _secondNumber) + ElementaryTypeNameToken(Token _token, unsigned const& _firstNumber, unsigned const& _secondNumber) { assertDetails(_token, _firstNumber, _secondNumber); } unsigned int firstNumber() const { return m_firstNumber; } unsigned int secondNumber() const { return m_secondNumber; } - Token::Value token() const { return m_token; } + Token token() const { return m_token; } + ///if tokValue is set to true, then returns the actual token type name, otherwise, returns full type - std::string toString(bool const& tokenValue = false) const + std::string toString(bool const& tokenValue = false) const { - std::string name = Token::toString(m_token); + std::string name = TokenTraits::toString(m_token); if (tokenValue || (firstNumber() == 0 && secondNumber() == 0)) return name; solAssert(name.size() >= 3, "Token name size should be greater than 3. Should not reach here."); @@ -361,11 +367,11 @@ public: } private: - Token::Value m_token; + Token m_token; unsigned int m_firstNumber; unsigned int m_secondNumber; /// throws if type is not properly sized - void assertDetails(Token::Value _baseType, unsigned const& _first, unsigned const& _second); + void assertDetails(Token _baseType, unsigned const& _first, unsigned const& _second); }; } diff --git a/libyul/ASTDataForward.h b/libyul/ASTDataForward.h new file mode 100644 index 00000000..8c49e68f --- /dev/null +++ b/libyul/ASTDataForward.h @@ -0,0 +1,55 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see <http://www.gnu.org/licenses/>. +*/ +/** + * @date 2017 + * Pull in some identifiers from the solidity::assembly namespace. + */ + +#pragma once + +#include <libsolidity/inlineasm/AsmDataForward.h> + +namespace dev +{ +namespace yul +{ + +using Instruction = solidity::assembly::Instruction; +using Literal = solidity::assembly::Literal; +using Label = solidity::assembly::Label; +using StackAssignment = solidity::assembly::StackAssignment; +using Identifier = solidity::assembly::Identifier; +using Assignment = solidity::assembly::Assignment; +using VariableDeclaration = solidity::assembly::VariableDeclaration; +using FunctionalInstruction = solidity::assembly::FunctionalInstruction; +using FunctionDefinition = solidity::assembly::FunctionDefinition; +using FunctionCall = solidity::assembly::FunctionCall; +using If = solidity::assembly::If; +using Case = solidity::assembly::Case; +using Switch = solidity::assembly::Switch; +using ForLoop = solidity::assembly::ForLoop; +using ExpressionStatement = solidity::assembly::ExpressionStatement; +using Block = solidity::assembly::Block; + +using TypedName = solidity::assembly::TypedName; +class YulString; + +using Expression = boost::variant<FunctionalInstruction, FunctionCall, Identifier, Literal>; +using Statement = boost::variant<ExpressionStatement, Instruction, Label, StackAssignment, Assignment, VariableDeclaration, FunctionDefinition, If, Switch, ForLoop, Block>; + +} +} diff --git a/libyul/Exceptions.h b/libyul/Exceptions.h new file mode 100644 index 00000000..0c421dbf --- /dev/null +++ b/libyul/Exceptions.h @@ -0,0 +1,40 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see <http://www.gnu.org/licenses/>. +*/ +/** + * Exceptions in Yul. + */ + +#pragma once + +#include <libdevcore/Exceptions.h> +#include <libdevcore/Assertions.h> + +namespace dev +{ +namespace yul +{ + +struct YulException: virtual Exception {}; +struct OptimizerException: virtual YulException {}; +struct YulAssertion: virtual YulException {}; + +/// Assertion that throws an YulAssertion containing the given description if it is not met. +#define yulAssert(CONDITION, DESCRIPTION) \ + assertThrow(CONDITION, ::dev::yul::YulException, DESCRIPTION) + +} +} diff --git a/libyul/YulString.h b/libyul/YulString.h new file mode 100644 index 00000000..ad900a70 --- /dev/null +++ b/libyul/YulString.h @@ -0,0 +1,133 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see <http://www.gnu.org/licenses/>. +*/ +/** + * String abstraction that avoids copies. + */ + +#pragma once + +#include <boost/noncopyable.hpp> + +#include <unordered_map> +#include <memory> +#include <vector> +#include <string> + +namespace dev +{ +namespace yul +{ + +/// Repository for YulStrings. +/// Owns the string data for all YulStrings, which can be referenced by a Handle. +/// A Handle consists of an ID (that depends on the insertion order of YulStrings and is potentially +/// non-deterministic) and a deterministic string hash. +class YulStringRepository: boost::noncopyable +{ +public: + struct Handle + { + size_t id; + std::uint64_t hash; + }; + YulStringRepository(): + m_strings{std::make_shared<std::string>()}, + m_hashToID{std::make_pair(emptyHash(), 0)} + {} + static YulStringRepository& instance() + { + static YulStringRepository inst; + return inst; + } + Handle stringToHandle(std::string const& _string) + { + if (_string.empty()) + return { 0, emptyHash() }; + std::uint64_t h = hash(_string); + auto range = m_hashToID.equal_range(h); + for (auto it = range.first; it != range.second; ++it) + if (*m_strings[it->second] == _string) + return Handle{it->second, h}; + m_strings.emplace_back(std::make_shared<std::string>(_string)); + size_t id = m_strings.size() - 1; + m_hashToID.emplace_hint(range.second, std::make_pair(h, id)); + return Handle{id, h}; + } + std::string const& idToString(size_t _id) const { return *m_strings.at(_id); } + + static std::uint64_t hash(std::string const& v) + { + // FNV hash - can be replaced by a better one, e.g. xxhash64 + std::uint64_t hash = emptyHash(); + for (auto c: v) + { + hash *= 1099511628211u; + hash ^= c; + } + + return hash; + } + static constexpr std::uint64_t emptyHash() { return 14695981039346656037u; } +private: + std::vector<std::shared_ptr<std::string>> m_strings; + std::unordered_multimap<std::uint64_t, size_t> m_hashToID; +}; + +/// Wrapper around handles into the YulString repository. +/// Equality of two YulStrings is determined by comparing their ID. +/// The <-operator depends on the string hash and is not consistent +/// with string comparisons (however, it is still deterministic). +class YulString +{ +public: + YulString() = default; + explicit YulString(std::string const& _s): m_handle(YulStringRepository::instance().stringToHandle(_s)) {} + YulString(YulString const&) = default; + YulString(YulString&&) = default; + YulString& operator=(YulString const&) = default; + YulString& operator=(YulString&&) = default; + + /// This is not consistent with the string <-operator! + /// First compares the string hashes. If they are equal + /// it checks for identical IDs (only identical strings have + /// identical IDs and identical strings do not compare as "less"). + /// If the hashes are identical and the strings are distinct, it + /// falls back to string comparison. + bool operator<(YulString const& _other) const + { + if (m_handle.hash < _other.m_handle.hash) return true; + if (_other.m_handle.hash < m_handle.hash) return false; + if (m_handle.id == _other.m_handle.id) return false; + return str() < _other.str(); + } + /// Equality is determined based on the string ID. + bool operator==(YulString const& _other) const { return m_handle.id == _other.m_handle.id; } + bool operator!=(YulString const& _other) const { return m_handle.id != _other.m_handle.id; } + + bool empty() const { return m_handle.id == 0; } + std::string const& str() const + { + return YulStringRepository::instance().idToString(m_handle.id); + } + +private: + /// Handle of the string. Assumes that the empty string has ID zero. + YulStringRepository::Handle m_handle{ 0, YulStringRepository::emptyHash() }; +}; + +} +} diff --git a/libyul/backends/evm/AbstractAssembly.h b/libyul/backends/evm/AbstractAssembly.h new file mode 100644 index 00000000..d75058f7 --- /dev/null +++ b/libyul/backends/evm/AbstractAssembly.h @@ -0,0 +1,119 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see <http://www.gnu.org/licenses/>. +*/ +/** + * @date 2017 + * Abstract assembly interface, subclasses of which are to be used with the generic + * bytecode generator. + */ + +#pragma once + +#include <libdevcore/CommonData.h> + +#include <functional> + +namespace dev +{ +struct SourceLocation; +namespace solidity +{ +enum class Instruction: uint8_t; +namespace assembly +{ +struct Instruction; +struct Identifier; +} +} +namespace yul +{ + +/// +/// Assembly class that abstracts both the libevmasm assembly and the new Yul assembly. +/// +class AbstractAssembly +{ +public: + using LabelID = size_t; + + virtual ~AbstractAssembly() {} + + /// Set a new source location valid starting from the next instruction. + virtual void setSourceLocation(SourceLocation const& _location) = 0; + /// Retrieve the current height of the stack. This does not have to be zero + /// at the beginning. + virtual int stackHeight() const = 0; + /// Append an EVM instruction. + virtual void appendInstruction(solidity::Instruction _instruction) = 0; + /// Append a constant. + virtual void appendConstant(u256 const& _constant) = 0; + /// Append a label. + virtual void appendLabel(LabelID _labelId) = 0; + /// Append a label reference. + virtual void appendLabelReference(LabelID _labelId) = 0; + /// Generate a new unique label. + virtual LabelID newLabelId() = 0; + /// Returns a label identified by the given name. Creates it if it does not yet exist. + virtual LabelID namedLabel(std::string const& _name) = 0; + /// Append a reference to a to-be-linked symbol. + /// Currently, we assume that the value is always a 20 byte number. + virtual void appendLinkerSymbol(std::string const& _name) = 0; + + /// Append a jump instruction. + /// @param _stackDiffAfter the stack adjustment after this instruction. + /// This is helpful to stack height analysis if there is no continuing control flow. + virtual void appendJump(int _stackDiffAfter) = 0; + + /// Append a jump-to-immediate operation. + /// @param _stackDiffAfter the stack adjustment after this instruction. + virtual void appendJumpTo(LabelID _labelId, int _stackDiffAfter = 0) = 0; + /// Append a jump-to-if-immediate operation. + virtual void appendJumpToIf(LabelID _labelId) = 0; + /// Start a subroutine identified by @a _labelId that takes @a _arguments + /// stack slots as arguments. + virtual void appendBeginsub(LabelID _labelId, int _arguments) = 0; + /// Call a subroutine identified by @a _labelId, taking @a _arguments from the + /// stack upon call and putting @a _returns arguments onto the stack upon return. + virtual void appendJumpsub(LabelID _labelId, int _arguments, int _returns) = 0; + /// Return from a subroutine. + /// @param _stackDiffAfter the stack adjustment after this instruction. + virtual void appendReturnsub(int _returns, int _stackDiffAfter = 0) = 0; + + /// Append the assembled size as a constant. + virtual void appendAssemblySize() = 0; +}; + +enum class IdentifierContext { LValue, RValue }; + +/// Object that is used to resolve references and generate code for access to identifiers external +/// to inline assembly (not used in standalone assembly mode). +struct ExternalIdentifierAccess +{ + using Resolver = std::function<size_t(solidity::assembly::Identifier const&, IdentifierContext, bool /*_crossesFunctionBoundary*/)>; + /// Resolve an external reference given by the identifier in the given context. + /// @returns the size of the value (number of stack slots) or size_t(-1) if not found. + Resolver resolve; + using CodeGenerator = std::function<void(solidity::assembly::Identifier const&, IdentifierContext, yul::AbstractAssembly&)>; + /// Generate code for retrieving the value (rvalue context) or storing the value (lvalue context) + /// of an identifier. The code should be appended to the assembly. In rvalue context, the value is supposed + /// to be put onto the stack, in lvalue context, the value is assumed to be at the top of the stack. + CodeGenerator generateCode; +}; + + + +} +} diff --git a/libyul/backends/evm/EVMAssembly.cpp b/libyul/backends/evm/EVMAssembly.cpp new file mode 100644 index 00000000..b37a3231 --- /dev/null +++ b/libyul/backends/evm/EVMAssembly.cpp @@ -0,0 +1,202 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see <http://www.gnu.org/licenses/>. +*/ +/** + * Assembly interface for EVM and EVM1.5. + */ + +#include <libyul/backends/evm/EVMAssembly.h> + +#include <libevmasm/Instruction.h> + +#include <libsolidity/interface/Exceptions.h> + +using namespace std; +using namespace dev; +using namespace dev::yul; + +namespace +{ +/// Size of labels in bytes. Four-byte labels are required by some EVM1.5 instructions. +size_t constexpr labelReferenceSize = 4; + +size_t constexpr assemblySizeReferenceSize = 4; +} + + +void EVMAssembly::setSourceLocation(SourceLocation const&) +{ + // Ignored for now; +} + +void EVMAssembly::appendInstruction(solidity::Instruction _instr) +{ + m_bytecode.push_back(uint8_t(_instr)); + m_stackHeight += solidity::instructionInfo(_instr).ret - solidity::instructionInfo(_instr).args; +} + +void EVMAssembly::appendConstant(u256 const& _constant) +{ + bytes data = toCompactBigEndian(_constant, 1); + appendInstruction(solidity::pushInstruction(data.size())); + m_bytecode += data; +} + +void EVMAssembly::appendLabel(LabelID _labelId) +{ + setLabelToCurrentPosition(_labelId); + appendInstruction(solidity::Instruction::JUMPDEST); +} + +void EVMAssembly::appendLabelReference(LabelID _labelId) +{ + solAssert(!m_evm15, "Cannot use plain label references in EMV1.5 mode."); + // @TODO we now always use labelReferenceSize for all labels, it could be shortened + // for some of them. + appendInstruction(solidity::pushInstruction(labelReferenceSize)); + m_labelReferences[m_bytecode.size()] = _labelId; + m_bytecode += bytes(labelReferenceSize); +} + +EVMAssembly::LabelID EVMAssembly::newLabelId() +{ + m_labelPositions[m_nextLabelId] = size_t(-1); + return m_nextLabelId++; +} + +AbstractAssembly::LabelID EVMAssembly::namedLabel(string const& _name) +{ + solAssert(!_name.empty(), ""); + if (!m_namedLabels.count(_name)) + m_namedLabels[_name] = newLabelId(); + return m_namedLabels[_name]; +} + +void EVMAssembly::appendLinkerSymbol(string const&) +{ + solAssert(false, "Linker symbols not yet implemented."); +} + +void EVMAssembly::appendJump(int _stackDiffAfter) +{ + solAssert(!m_evm15, "Plain JUMP used for EVM 1.5"); + appendInstruction(solidity::Instruction::JUMP); + m_stackHeight += _stackDiffAfter; +} + +void EVMAssembly::appendJumpTo(LabelID _labelId, int _stackDiffAfter) +{ + if (m_evm15) + { + m_bytecode.push_back(uint8_t(solidity::Instruction::JUMPTO)); + appendLabelReferenceInternal(_labelId); + m_stackHeight += _stackDiffAfter; + } + else + { + appendLabelReference(_labelId); + appendJump(_stackDiffAfter); + } +} + +void EVMAssembly::appendJumpToIf(LabelID _labelId) +{ + if (m_evm15) + { + m_bytecode.push_back(uint8_t(solidity::Instruction::JUMPIF)); + appendLabelReferenceInternal(_labelId); + m_stackHeight--; + } + else + { + appendLabelReference(_labelId); + appendInstruction(solidity::Instruction::JUMPI); + } +} + +void EVMAssembly::appendBeginsub(LabelID _labelId, int _arguments) +{ + solAssert(m_evm15, "BEGINSUB used for EVM 1.0"); + solAssert(_arguments >= 0, ""); + setLabelToCurrentPosition(_labelId); + m_bytecode.push_back(uint8_t(solidity::Instruction::BEGINSUB)); + m_stackHeight += _arguments; +} + +void EVMAssembly::appendJumpsub(LabelID _labelId, int _arguments, int _returns) +{ + solAssert(m_evm15, "JUMPSUB used for EVM 1.0"); + solAssert(_arguments >= 0 && _returns >= 0, ""); + m_bytecode.push_back(uint8_t(solidity::Instruction::JUMPSUB)); + appendLabelReferenceInternal(_labelId); + m_stackHeight += _returns - _arguments; +} + +void EVMAssembly::appendReturnsub(int _returns, int _stackDiffAfter) +{ + solAssert(m_evm15, "RETURNSUB used for EVM 1.0"); + solAssert(_returns >= 0, ""); + m_bytecode.push_back(uint8_t(solidity::Instruction::RETURNSUB)); + m_stackHeight += _stackDiffAfter - _returns; +} + +eth::LinkerObject EVMAssembly::finalize() +{ + size_t bytecodeSize = m_bytecode.size(); + for (auto const& ref: m_assemblySizePositions) + updateReference(ref, assemblySizeReferenceSize, u256(bytecodeSize)); + + for (auto const& ref: m_labelReferences) + { + size_t referencePos = ref.first; + solAssert(m_labelPositions.count(ref.second), ""); + size_t labelPos = m_labelPositions.at(ref.second); + solAssert(labelPos != size_t(-1), "Undefined but allocated label used."); + updateReference(referencePos, labelReferenceSize, u256(labelPos)); + } + + eth::LinkerObject obj; + obj.bytecode = m_bytecode; + return obj; +} + +void EVMAssembly::setLabelToCurrentPosition(LabelID _labelId) +{ + solAssert(m_labelPositions.count(_labelId), "Label not found."); + solAssert(m_labelPositions[_labelId] == size_t(-1), "Label already set."); + m_labelPositions[_labelId] = m_bytecode.size(); +} + +void EVMAssembly::appendLabelReferenceInternal(LabelID _labelId) +{ + m_labelReferences[m_bytecode.size()] = _labelId; + m_bytecode += bytes(labelReferenceSize); +} + +void EVMAssembly::appendAssemblySize() +{ + appendInstruction(solidity::pushInstruction(assemblySizeReferenceSize)); + m_assemblySizePositions.push_back(m_bytecode.size()); + m_bytecode += bytes(assemblySizeReferenceSize); +} + +void EVMAssembly::updateReference(size_t pos, size_t size, u256 value) +{ + solAssert(m_bytecode.size() >= size && pos <= m_bytecode.size() - size, ""); + solAssert(value < (u256(1) << (8 * size)), ""); + for (size_t i = 0; i < size; i++) + m_bytecode[pos + i] = uint8_t((value >> (8 * (size - i - 1))) & 0xff); +} diff --git a/libyul/backends/evm/EVMAssembly.h b/libyul/backends/evm/EVMAssembly.h new file mode 100644 index 00000000..556ed5a5 --- /dev/null +++ b/libyul/backends/evm/EVMAssembly.h @@ -0,0 +1,97 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see <http://www.gnu.org/licenses/>. +*/ +/** + * Assembly interface for EVM and EVM1.5. + */ + +#pragma once + +#include <libyul/backends/evm/AbstractAssembly.h> + +#include <libevmasm/LinkerObject.h> + +#include <map> + +namespace dev +{ +namespace yul +{ + +class EVMAssembly: public AbstractAssembly +{ +public: + explicit EVMAssembly(bool _evm15 = false): m_evm15(_evm15) { } + virtual ~EVMAssembly() {} + + /// Set a new source location valid starting from the next instruction. + virtual void setSourceLocation(SourceLocation const& _location) override; + /// Retrieve the current height of the stack. This does not have to be zero + /// at the beginning. + virtual int stackHeight() const override { return m_stackHeight; } + /// Append an EVM instruction. + virtual void appendInstruction(solidity::Instruction _instruction) override; + /// Append a constant. + virtual void appendConstant(u256 const& _constant) override; + /// Append a label. + virtual void appendLabel(LabelID _labelId) override; + /// Append a label reference. + virtual void appendLabelReference(LabelID _labelId) override; + /// Generate a new unique label. + virtual LabelID newLabelId() override; + /// Returns a label identified by the given name. Creates it if it does not yet exist. + virtual LabelID namedLabel(std::string const& _name) override; + /// Append a reference to a to-be-linked symbol. + /// Currently, we assume that the value is always a 20 byte number. + virtual void appendLinkerSymbol(std::string const& _name) override; + + /// Append a jump instruction. + /// @param _stackDiffAfter the stack adjustment after this instruction. + virtual void appendJump(int _stackDiffAfter) override; + /// Append a jump-to-immediate operation. + virtual void appendJumpTo(LabelID _labelId, int _stackDiffAfter) override; + /// Append a jump-to-if-immediate operation. + virtual void appendJumpToIf(LabelID _labelId) override; + /// Start a subroutine. + virtual void appendBeginsub(LabelID _labelId, int _arguments) override; + /// Call a subroutine. + virtual void appendJumpsub(LabelID _labelId, int _arguments, int _returns) override; + /// Return from a subroutine. + virtual void appendReturnsub(int _returns, int _stackDiffAfter) override; + + /// Append the assembled size as a constant. + virtual void appendAssemblySize() override; + + /// Resolves references inside the bytecode and returns the linker object. + eth::LinkerObject finalize(); + +private: + void setLabelToCurrentPosition(AbstractAssembly::LabelID _labelId); + void appendLabelReferenceInternal(AbstractAssembly::LabelID _labelId); + void updateReference(size_t pos, size_t size, u256 value); + + bool m_evm15 = false; ///< if true, switch to evm1.5 mode + LabelID m_nextLabelId = 0; + int m_stackHeight = 0; + bytes m_bytecode; + std::map<std::string, LabelID> m_namedLabels; + std::map<LabelID, size_t> m_labelPositions; + std::map<size_t, LabelID> m_labelReferences; + std::vector<size_t> m_assemblySizePositions; +}; + +} +} diff --git a/libyul/backends/evm/EVMCodeTransform.cpp b/libyul/backends/evm/EVMCodeTransform.cpp new file mode 100644 index 00000000..650a8c0a --- /dev/null +++ b/libyul/backends/evm/EVMCodeTransform.cpp @@ -0,0 +1,557 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see <http://www.gnu.org/licenses/>. +*/ +/** + * Common code generator for translating Yul / inline assembly to EVM and EVM1.5. + */ + +#include <libyul/backends/evm/EVMCodeTransform.h> + +#include <libsolidity/inlineasm/AsmAnalysisInfo.h> +#include <libsolidity/inlineasm/AsmData.h> + +#include <libsolidity/interface/Exceptions.h> + +#include <boost/range/adaptor/reversed.hpp> + +using namespace std; +using namespace dev; +using namespace dev::yul; +using namespace dev::solidity; + +using Scope = dev::solidity::assembly::Scope; + +void CodeTransform::operator()(VariableDeclaration const& _varDecl) +{ + solAssert(m_scope, ""); + + int const numVariables = _varDecl.variables.size(); + int height = m_assembly.stackHeight(); + if (_varDecl.value) + { + boost::apply_visitor(*this, *_varDecl.value); + expectDeposit(numVariables, height); + } + else + { + int variablesLeft = numVariables; + while (variablesLeft--) + m_assembly.appendConstant(u256(0)); + } + for (auto const& variable: _varDecl.variables) + { + auto& var = boost::get<Scope::Variable>(m_scope->identifiers.at(variable.name)); + m_context->variableStackHeights[&var] = height++; + } + checkStackHeight(&_varDecl); +} + +void CodeTransform::operator()(Assignment const& _assignment) +{ + int height = m_assembly.stackHeight(); + boost::apply_visitor(*this, *_assignment.value); + expectDeposit(_assignment.variableNames.size(), height); + + m_assembly.setSourceLocation(_assignment.location); + generateMultiAssignment(_assignment.variableNames); + checkStackHeight(&_assignment); +} + +void CodeTransform::operator()(StackAssignment const& _assignment) +{ + m_assembly.setSourceLocation(_assignment.location); + generateAssignment(_assignment.variableName); + checkStackHeight(&_assignment); +} + +void CodeTransform::operator()(ExpressionStatement const& _statement) +{ + m_assembly.setSourceLocation(_statement.location); + boost::apply_visitor(*this, _statement.expression); + checkStackHeight(&_statement); +} + +void CodeTransform::operator()(Label const& _label) +{ + m_assembly.setSourceLocation(_label.location); + solAssert(m_scope, ""); + solAssert(m_scope->identifiers.count(_label.name), ""); + Scope::Label& label = boost::get<Scope::Label>(m_scope->identifiers.at(_label.name)); + m_assembly.appendLabel(labelID(label)); + checkStackHeight(&_label); +} + +void CodeTransform::operator()(FunctionCall const& _call) +{ + solAssert(m_scope, ""); + + m_assembly.setSourceLocation(_call.location); + EVMAssembly::LabelID returnLabel(-1); // only used for evm 1.0 + if (!m_evm15) + { + returnLabel = m_assembly.newLabelId(); + m_assembly.appendLabelReference(returnLabel); + m_stackAdjustment++; + } + + Scope::Function* function = nullptr; + solAssert(m_scope->lookup(_call.functionName.name, Scope::NonconstVisitor( + [=](Scope::Variable&) { solAssert(false, "Expected function name."); }, + [=](Scope::Label&) { solAssert(false, "Expected function name."); }, + [&](Scope::Function& _function) { function = &_function; } + )), "Function name not found."); + solAssert(function, ""); + solAssert(function->arguments.size() == _call.arguments.size(), ""); + for (auto const& arg: _call.arguments | boost::adaptors::reversed) + visitExpression(arg); + m_assembly.setSourceLocation(_call.location); + if (m_evm15) + m_assembly.appendJumpsub(functionEntryID(_call.functionName.name, *function), function->arguments.size(), function->returns.size()); + else + { + m_assembly.appendJumpTo(functionEntryID(_call.functionName.name, *function), function->returns.size() - function->arguments.size() - 1); + m_assembly.appendLabel(returnLabel); + m_stackAdjustment--; + } + checkStackHeight(&_call); +} + +void CodeTransform::operator()(FunctionalInstruction const& _instruction) +{ + if (m_evm15 && ( + _instruction.instruction == solidity::Instruction::JUMP || + _instruction.instruction == solidity::Instruction::JUMPI + )) + { + bool const isJumpI = _instruction.instruction == solidity::Instruction::JUMPI; + if (isJumpI) + { + solAssert(_instruction.arguments.size() == 2, ""); + visitExpression(_instruction.arguments.at(1)); + } + else + { + solAssert(_instruction.arguments.size() == 1, ""); + } + m_assembly.setSourceLocation(_instruction.location); + auto label = labelFromIdentifier(boost::get<assembly::Identifier>(_instruction.arguments.at(0))); + if (isJumpI) + m_assembly.appendJumpToIf(label); + else + m_assembly.appendJumpTo(label); + } + else + { + for (auto const& arg: _instruction.arguments | boost::adaptors::reversed) + visitExpression(arg); + m_assembly.setSourceLocation(_instruction.location); + m_assembly.appendInstruction(_instruction.instruction); + } + checkStackHeight(&_instruction); +} + +void CodeTransform::operator()(assembly::Identifier const& _identifier) +{ + m_assembly.setSourceLocation(_identifier.location); + // First search internals, then externals. + solAssert(m_scope, ""); + if (m_scope->lookup(_identifier.name, Scope::NonconstVisitor( + [=](Scope::Variable& _var) + { + if (int heightDiff = variableHeightDiff(_var, false)) + m_assembly.appendInstruction(solidity::dupInstruction(heightDiff)); + else + // Store something to balance the stack + m_assembly.appendConstant(u256(0)); + }, + [=](Scope::Label& _label) + { + m_assembly.appendLabelReference(labelID(_label)); + }, + [=](Scope::Function&) + { + solAssert(false, "Function not removed during desugaring."); + } + ))) + { + return; + } + solAssert( + m_identifierAccess.generateCode, + "Identifier not found and no external access available." + ); + m_identifierAccess.generateCode(_identifier, IdentifierContext::RValue, m_assembly); + checkStackHeight(&_identifier); +} + +void CodeTransform::operator()(assembly::Literal const& _literal) +{ + m_assembly.setSourceLocation(_literal.location); + if (_literal.kind == assembly::LiteralKind::Number) + m_assembly.appendConstant(u256(_literal.value.str())); + else if (_literal.kind == assembly::LiteralKind::Boolean) + { + if (_literal.value.str() == "true") + m_assembly.appendConstant(u256(1)); + else + m_assembly.appendConstant(u256(0)); + } + else + { + solAssert(_literal.value.str().size() <= 32, ""); + m_assembly.appendConstant(u256(h256(_literal.value.str(), h256::FromBinary, h256::AlignLeft))); + } + checkStackHeight(&_literal); +} + +void CodeTransform::operator()(assembly::Instruction const& _instruction) +{ + solAssert(!m_evm15 || _instruction.instruction != solidity::Instruction::JUMP, "Bare JUMP instruction used for EVM1.5"); + solAssert(!m_evm15 || _instruction.instruction != solidity::Instruction::JUMPI, "Bare JUMPI instruction used for EVM1.5"); + m_assembly.setSourceLocation(_instruction.location); + m_assembly.appendInstruction(_instruction.instruction); + checkStackHeight(&_instruction); +} + +void CodeTransform::operator()(If const& _if) +{ + visitExpression(*_if.condition); + m_assembly.setSourceLocation(_if.location); + m_assembly.appendInstruction(solidity::Instruction::ISZERO); + AbstractAssembly::LabelID end = m_assembly.newLabelId(); + m_assembly.appendJumpToIf(end); + (*this)(_if.body); + m_assembly.setSourceLocation(_if.location); + m_assembly.appendLabel(end); + checkStackHeight(&_if); +} + +void CodeTransform::operator()(Switch const& _switch) +{ + //@TODO use JUMPV in EVM1.5? + + visitExpression(*_switch.expression); + int expressionHeight = m_assembly.stackHeight(); + map<Case const*, AbstractAssembly::LabelID> caseBodies; + AbstractAssembly::LabelID end = m_assembly.newLabelId(); + for (Case const& c: _switch.cases) + { + if (c.value) + { + (*this)(*c.value); + m_assembly.setSourceLocation(c.location); + AbstractAssembly::LabelID bodyLabel = m_assembly.newLabelId(); + caseBodies[&c] = bodyLabel; + solAssert(m_assembly.stackHeight() == expressionHeight + 1, ""); + m_assembly.appendInstruction(solidity::dupInstruction(2)); + m_assembly.appendInstruction(solidity::Instruction::EQ); + m_assembly.appendJumpToIf(bodyLabel); + } + else + // default case + (*this)(c.body); + } + m_assembly.setSourceLocation(_switch.location); + m_assembly.appendJumpTo(end); + + size_t numCases = caseBodies.size(); + for (auto const& c: caseBodies) + { + m_assembly.setSourceLocation(c.first->location); + m_assembly.appendLabel(c.second); + (*this)(c.first->body); + // Avoid useless "jump to next" for the last case. + if (--numCases > 0) + { + m_assembly.setSourceLocation(c.first->location); + m_assembly.appendJumpTo(end); + } + } + + m_assembly.setSourceLocation(_switch.location); + m_assembly.appendLabel(end); + m_assembly.appendInstruction(solidity::Instruction::POP); + checkStackHeight(&_switch); +} + +void CodeTransform::operator()(FunctionDefinition const& _function) +{ + solAssert(m_scope, ""); + solAssert(m_scope->identifiers.count(_function.name), ""); + Scope::Function& function = boost::get<Scope::Function>(m_scope->identifiers.at(_function.name)); + + int const localStackAdjustment = m_evm15 ? 0 : 1; + int height = localStackAdjustment; + solAssert(m_info.scopes.at(&_function.body), ""); + Scope* varScope = m_info.scopes.at(m_info.virtualBlocks.at(&_function).get()).get(); + solAssert(varScope, ""); + for (auto const& v: _function.parameters | boost::adaptors::reversed) + { + auto& var = boost::get<Scope::Variable>(varScope->identifiers.at(v.name)); + m_context->variableStackHeights[&var] = height++; + } + + m_assembly.setSourceLocation(_function.location); + int stackHeightBefore = m_assembly.stackHeight(); + AbstractAssembly::LabelID afterFunction = m_assembly.newLabelId(); + + if (m_evm15) + { + m_assembly.appendJumpTo(afterFunction, -stackHeightBefore); + m_assembly.appendBeginsub(functionEntryID(_function.name, function), _function.parameters.size()); + } + else + { + m_assembly.appendJumpTo(afterFunction, -stackHeightBefore + height); + m_assembly.appendLabel(functionEntryID(_function.name, function)); + } + m_stackAdjustment += localStackAdjustment; + + for (auto const& v: _function.returnVariables) + { + auto& var = boost::get<Scope::Variable>(varScope->identifiers.at(v.name)); + m_context->variableStackHeights[&var] = height++; + // Preset stack slots for return variables to zero. + m_assembly.appendConstant(u256(0)); + } + + CodeTransform( + m_assembly, + m_info, + m_yul, + m_evm15, + m_identifierAccess, + m_useNamedLabelsForFunctions, + localStackAdjustment, + m_context + )(_function.body); + + { + // The stack layout here is: + // <return label>? <arguments...> <return values...> + // But we would like it to be: + // <return values...> <return label>? + // So we have to append some SWAP and POP instructions. + + // This vector holds the desired target positions of all stack slots and is + // modified parallel to the actual stack. + vector<int> stackLayout; + if (!m_evm15) + stackLayout.push_back(_function.returnVariables.size()); // Move return label to the top + stackLayout += vector<int>(_function.parameters.size(), -1); // discard all arguments + for (size_t i = 0; i < _function.returnVariables.size(); ++i) + stackLayout.push_back(i); // Move return values down, but keep order. + + solAssert(stackLayout.size() <= 17, "Stack too deep"); + while (!stackLayout.empty() && stackLayout.back() != int(stackLayout.size() - 1)) + if (stackLayout.back() < 0) + { + m_assembly.appendInstruction(solidity::Instruction::POP); + stackLayout.pop_back(); + } + else + { + m_assembly.appendInstruction(swapInstruction(stackLayout.size() - stackLayout.back() - 1)); + swap(stackLayout[stackLayout.back()], stackLayout.back()); + } + for (int i = 0; size_t(i) < stackLayout.size(); ++i) + solAssert(i == stackLayout[i], "Error reshuffling stack."); + } + + if (m_evm15) + m_assembly.appendReturnsub(_function.returnVariables.size(), stackHeightBefore); + else + m_assembly.appendJump(stackHeightBefore - _function.returnVariables.size()); + m_stackAdjustment -= localStackAdjustment; + m_assembly.appendLabel(afterFunction); + checkStackHeight(&_function); +} + +void CodeTransform::operator()(ForLoop const& _forLoop) +{ + Scope* originalScope = m_scope; + // We start with visiting the block, but not finalizing it. + m_scope = m_info.scopes.at(&_forLoop.pre).get(); + int stackStartHeight = m_assembly.stackHeight(); + + visitStatements(_forLoop.pre.statements); + + // TODO: When we implement break and continue, the labels and the stack heights at that point + // have to be stored in a stack. + AbstractAssembly::LabelID loopStart = m_assembly.newLabelId(); + AbstractAssembly::LabelID loopEnd = m_assembly.newLabelId(); + AbstractAssembly::LabelID postPart = m_assembly.newLabelId(); + + m_assembly.setSourceLocation(_forLoop.location); + m_assembly.appendLabel(loopStart); + + visitExpression(*_forLoop.condition); + m_assembly.setSourceLocation(_forLoop.location); + m_assembly.appendInstruction(solidity::Instruction::ISZERO); + m_assembly.appendJumpToIf(loopEnd); + + (*this)(_forLoop.body); + + m_assembly.setSourceLocation(_forLoop.location); + m_assembly.appendLabel(postPart); + + (*this)(_forLoop.post); + + m_assembly.setSourceLocation(_forLoop.location); + m_assembly.appendJumpTo(loopStart); + m_assembly.appendLabel(loopEnd); + + finalizeBlock(_forLoop.pre, stackStartHeight); + m_scope = originalScope; +} + +void CodeTransform::operator()(Block const& _block) +{ + Scope* originalScope = m_scope; + m_scope = m_info.scopes.at(&_block).get(); + + int blockStartStackHeight = m_assembly.stackHeight(); + visitStatements(_block.statements); + + finalizeBlock(_block, blockStartStackHeight); + m_scope = originalScope; +} + +AbstractAssembly::LabelID CodeTransform::labelFromIdentifier(Identifier const& _identifier) +{ + AbstractAssembly::LabelID label = AbstractAssembly::LabelID(-1); + if (!m_scope->lookup(_identifier.name, Scope::NonconstVisitor( + [=](Scope::Variable&) { solAssert(false, "Expected label"); }, + [&](Scope::Label& _label) + { + label = labelID(_label); + }, + [=](Scope::Function&) { solAssert(false, "Expected label"); } + ))) + { + solAssert(false, "Identifier not found."); + } + return label; +} + +AbstractAssembly::LabelID CodeTransform::labelID(Scope::Label const& _label) +{ + if (!m_context->labelIDs.count(&_label)) + m_context->labelIDs[&_label] = m_assembly.newLabelId(); + return m_context->labelIDs[&_label]; +} + +AbstractAssembly::LabelID CodeTransform::functionEntryID(YulString _name, Scope::Function const& _function) +{ + if (!m_context->functionEntryIDs.count(&_function)) + { + AbstractAssembly::LabelID id = + m_useNamedLabelsForFunctions ? + m_assembly.namedLabel(_name.str()) : + m_assembly.newLabelId(); + m_context->functionEntryIDs[&_function] = id; + } + return m_context->functionEntryIDs[&_function]; +} + +void CodeTransform::visitExpression(Expression const& _expression) +{ + int height = m_assembly.stackHeight(); + boost::apply_visitor(*this, _expression); + expectDeposit(1, height); +} + +void CodeTransform::visitStatements(vector<Statement> const& _statements) +{ + for (auto const& statement: _statements) + boost::apply_visitor(*this, statement); +} + +void CodeTransform::finalizeBlock(Block const& _block, int blockStartStackHeight) +{ + m_assembly.setSourceLocation(_block.location); + + // pop variables + solAssert(m_info.scopes.at(&_block).get() == m_scope, ""); + for (size_t i = 0; i < m_scope->numberOfVariables(); ++i) + m_assembly.appendInstruction(solidity::Instruction::POP); + + int deposit = m_assembly.stackHeight() - blockStartStackHeight; + solAssert(deposit == 0, "Invalid stack height at end of block."); + checkStackHeight(&_block); +} + +void CodeTransform::generateMultiAssignment(vector<Identifier> const& _variableNames) +{ + solAssert(m_scope, ""); + for (auto const& variableName: _variableNames | boost::adaptors::reversed) + generateAssignment(variableName); +} + +void CodeTransform::generateAssignment(Identifier const& _variableName) +{ + solAssert(m_scope, ""); + auto var = m_scope->lookup(_variableName.name); + if (var) + { + Scope::Variable const& _var = boost::get<Scope::Variable>(*var); + if (int heightDiff = variableHeightDiff(_var, true)) + m_assembly.appendInstruction(solidity::swapInstruction(heightDiff - 1)); + m_assembly.appendInstruction(solidity::Instruction::POP); + } + else + { + solAssert( + m_identifierAccess.generateCode, + "Identifier not found and no external access available." + ); + m_identifierAccess.generateCode(_variableName, IdentifierContext::LValue, m_assembly); + } +} + +int CodeTransform::variableHeightDiff(solidity::assembly::Scope::Variable const& _var, bool _forSwap) const +{ + solAssert(m_context->variableStackHeights.count(&_var), ""); + int heightDiff = m_assembly.stackHeight() - m_context->variableStackHeights[&_var]; + if (heightDiff <= (_forSwap ? 1 : 0) || heightDiff > (_forSwap ? 17 : 16)) + { + solUnimplemented( + "Variable inaccessible, too deep inside stack (" + to_string(heightDiff) + ")" + ); + return 0; + } + else + return heightDiff; +} + +void CodeTransform::expectDeposit(int _deposit, int _oldHeight) const +{ + solAssert(m_assembly.stackHeight() == _oldHeight + _deposit, "Invalid stack deposit."); +} + +void CodeTransform::checkStackHeight(void const* _astElement) const +{ + solAssert(m_info.stackHeightInfo.count(_astElement), "Stack height for AST element not found."); + int stackHeightInAnalysis = m_info.stackHeightInfo.at(_astElement); + int stackHeightInCodegen = m_assembly.stackHeight() - m_stackAdjustment; + solAssert( + stackHeightInAnalysis == stackHeightInCodegen, + "Stack height mismatch between analysis and code generation phase: Analysis: " + + to_string(stackHeightInAnalysis) + + " code gen: " + + to_string(stackHeightInCodegen) + ); +} diff --git a/libyul/backends/evm/EVMCodeTransform.h b/libyul/backends/evm/EVMCodeTransform.h new file mode 100644 index 00000000..c0de8ad6 --- /dev/null +++ b/libyul/backends/evm/EVMCodeTransform.h @@ -0,0 +1,158 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see <http://www.gnu.org/licenses/>. +*/ +/** + * Common code generator for translating Yul / inline assembly to EVM and EVM1.5. + */ + +#include <libyul/backends/evm/EVMAssembly.h> + +#include <libyul/ASTDataForward.h> + +#include <libsolidity/inlineasm/AsmScope.h> + +#include <boost/variant.hpp> +#include <boost/optional.hpp> + +namespace dev +{ +namespace solidity +{ +class ErrorReporter; +namespace assembly +{ +struct AsmAnalysisInfo; +} +} +namespace yul +{ +class EVMAssembly; + +class CodeTransform: public boost::static_visitor<> +{ +public: + /// Create the code transformer. + /// @param _identifierAccess used to resolve identifiers external to the inline assembly + CodeTransform( + yul::AbstractAssembly& _assembly, + solidity::assembly::AsmAnalysisInfo& _analysisInfo, + bool _yul = false, + bool _evm15 = false, + ExternalIdentifierAccess const& _identifierAccess = ExternalIdentifierAccess(), + bool _useNamedLabelsForFunctions = false + ): CodeTransform( + _assembly, + _analysisInfo, + _yul, + _evm15, + _identifierAccess, + _useNamedLabelsForFunctions, + _assembly.stackHeight(), + std::make_shared<Context>() + ) + { + } + +protected: + struct Context + { + using Scope = solidity::assembly::Scope; + std::map<Scope::Label const*, AbstractAssembly::LabelID> labelIDs; + std::map<Scope::Function const*, AbstractAssembly::LabelID> functionEntryIDs; + std::map<Scope::Variable const*, int> variableStackHeights; + }; + + CodeTransform( + yul::AbstractAssembly& _assembly, + solidity::assembly::AsmAnalysisInfo& _analysisInfo, + bool _yul, + bool _evm15, + ExternalIdentifierAccess const& _identifierAccess, + bool _useNamedLabelsForFunctions, + int _stackAdjustment, + std::shared_ptr<Context> _context + ): + m_assembly(_assembly), + m_info(_analysisInfo), + m_yul(_yul), + m_evm15(_evm15), + m_useNamedLabelsForFunctions(_useNamedLabelsForFunctions), + m_identifierAccess(_identifierAccess), + m_stackAdjustment(_stackAdjustment), + m_context(_context) + {} + +public: + void operator()(Instruction const& _instruction); + void operator()(Literal const& _literal); + void operator()(Identifier const& _identifier); + void operator()(FunctionalInstruction const& _instr); + void operator()(FunctionCall const&); + void operator()(ExpressionStatement const& _statement); + void operator()(Label const& _label); + void operator()(StackAssignment const& _assignment); + void operator()(Assignment const& _assignment); + void operator()(VariableDeclaration const& _varDecl); + void operator()(If const& _if); + void operator()(Switch const& _switch); + void operator()(FunctionDefinition const&); + void operator()(ForLoop const&); + void operator()(Block const& _block); + +private: + AbstractAssembly::LabelID labelFromIdentifier(Identifier const& _identifier); + /// @returns the label ID corresponding to the given label, allocating a new one if + /// necessary. + AbstractAssembly::LabelID labelID(solidity::assembly::Scope::Label const& _label); + AbstractAssembly::LabelID functionEntryID(YulString _name, solidity::assembly::Scope::Function const& _function); + /// Generates code for an expression that is supposed to return a single value. + void visitExpression(Expression const& _expression); + + void visitStatements(std::vector<Statement> const& _statements); + + /// Pops all variables declared in the block and checks that the stack height is equal + /// to @a _blackStartStackHeight. + void finalizeBlock(Block const& _block, int _blockStartStackHeight); + + void generateMultiAssignment(std::vector<Identifier> const& _variableNames); + void generateAssignment(Identifier const& _variableName); + + /// Determines the stack height difference to the given variables. Throws + /// if it is not yet in scope or the height difference is too large. Returns + /// the (positive) stack height difference otherwise. + int variableHeightDiff(solidity::assembly::Scope::Variable const& _var, bool _forSwap) const; + + void expectDeposit(int _deposit, int _oldHeight) const; + + void checkStackHeight(void const* _astElement) const; + + yul::AbstractAssembly& m_assembly; + solidity::assembly::AsmAnalysisInfo& m_info; + solidity::assembly::Scope* m_scope = nullptr; + bool m_yul = false; + bool m_evm15 = false; + bool m_useNamedLabelsForFunctions = false; + ExternalIdentifierAccess m_identifierAccess; + /// Adjustment between the stack height as determined during the analysis phase + /// and the stack height in the assembly. This is caused by an initial stack being present + /// for inline assembly and different stack heights depending on the EVM backend used + /// (EVM 1.0 or 1.5). + int m_stackAdjustment = 0; + std::shared_ptr<Context> m_context; +}; + +} +} diff --git a/libyul/optimiser/ASTCopier.cpp b/libyul/optimiser/ASTCopier.cpp new file mode 100644 index 00000000..d0c8dd45 --- /dev/null +++ b/libyul/optimiser/ASTCopier.cpp @@ -0,0 +1,184 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see <http://www.gnu.org/licenses/>. +*/ +/** + * Creates an independent copy of an AST, renaming identifiers to be unique. + */ + +#include <libyul/optimiser/ASTCopier.h> + +#include <libyul/Exceptions.h> + +#include <libsolidity/inlineasm/AsmData.h> + +#include <libdevcore/Common.h> + +using namespace std; +using namespace dev; +using namespace dev::yul; + +Statement ASTCopier::operator()(Instruction const&) +{ + assertThrow(false, OptimizerException, "Invalid operation."); + return {}; +} + +Statement ASTCopier::operator()(ExpressionStatement const& _statement) +{ + return ExpressionStatement{ _statement.location, translate(_statement.expression) }; +} + +Statement ASTCopier::operator()(VariableDeclaration const& _varDecl) +{ + return VariableDeclaration{ + _varDecl.location, + translateVector(_varDecl.variables), + translate(_varDecl.value) + }; +} + +Statement ASTCopier::operator()(Assignment const& _assignment) +{ + return Assignment{ + _assignment.location, + translateVector(_assignment.variableNames), + translate(_assignment.value) + }; +} + +Statement ASTCopier::operator()(StackAssignment const&) +{ + assertThrow(false, OptimizerException, "Invalid operation."); + return {}; +} + +Statement ASTCopier::operator()(Label const&) +{ + assertThrow(false, OptimizerException, "Invalid operation."); + return {}; +} + +Expression ASTCopier::operator()(FunctionCall const& _call) +{ + return FunctionCall{ + _call.location, + translate(_call.functionName), + translateVector(_call.arguments) + }; +} + +Expression ASTCopier::operator()(FunctionalInstruction const& _instruction) +{ + return FunctionalInstruction{ + _instruction.location, + _instruction.instruction, + translateVector(_instruction.arguments) + }; +} + +Expression ASTCopier::operator()(Identifier const& _identifier) +{ + return Identifier{_identifier.location, translateIdentifier(_identifier.name)}; +} + +Expression ASTCopier::operator()(Literal const& _literal) +{ + return translate(_literal); +} + +Statement ASTCopier::operator()(If const& _if) +{ + return If{_if.location, translate(_if.condition), translate(_if.body)}; +} + +Statement ASTCopier::operator()(Switch const& _switch) +{ + return Switch{_switch.location, translate(_switch.expression), translateVector(_switch.cases)}; +} + +Statement ASTCopier::operator()(FunctionDefinition const& _function) +{ + YulString translatedName = translateIdentifier(_function.name); + + enterFunction(_function); + ScopeGuard g([&]() { this->leaveFunction(_function); }); + + return FunctionDefinition{ + _function.location, + translatedName, + translateVector(_function.parameters), + translateVector(_function.returnVariables), + translate(_function.body) + }; +} + +Statement ASTCopier::operator()(ForLoop const& _forLoop) +{ + enterScope(_forLoop.pre); + ScopeGuard g([&]() { this->leaveScope(_forLoop.pre); }); + + return ForLoop{ + _forLoop.location, + translate(_forLoop.pre), + translate(_forLoop.condition), + translate(_forLoop.post), + translate(_forLoop.body) + }; +} + +Statement ASTCopier::operator ()(Block const& _block) +{ + return translate(_block); +} + +Expression ASTCopier::translate(Expression const& _expression) +{ + return _expression.apply_visitor(static_cast<ExpressionCopier&>(*this)); +} + +Statement ASTCopier::translate(Statement const& _statement) +{ + return _statement.apply_visitor(static_cast<StatementCopier&>(*this)); +} + +Block ASTCopier::translate(Block const& _block) +{ + enterScope(_block); + ScopeGuard g([&]() { this->leaveScope(_block); }); + + return Block{_block.location, translateVector(_block.statements)}; +} + +Case ASTCopier::translate(Case const& _case) +{ + return Case{_case.location, translate(_case.value), translate(_case.body)}; +} + +Identifier ASTCopier::translate(Identifier const& _identifier) +{ + return Identifier{_identifier.location, translateIdentifier(_identifier.name)}; +} + +Literal ASTCopier::translate(Literal const& _literal) +{ + return _literal; +} + +TypedName ASTCopier::translate(TypedName const& _typedName) +{ + return TypedName{_typedName.location, translateIdentifier(_typedName.name), _typedName.type}; +} + diff --git a/libyul/optimiser/ASTCopier.h b/libyul/optimiser/ASTCopier.h new file mode 100644 index 00000000..b6aceee3 --- /dev/null +++ b/libyul/optimiser/ASTCopier.h @@ -0,0 +1,126 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see <http://www.gnu.org/licenses/>. +*/ +/** + * Creates an independent copy of an AST, renaming identifiers to be unique. + */ + +#pragma once + +#include <libyul/ASTDataForward.h> + +#include <libyul/YulString.h> + +#include <boost/variant.hpp> +#include <boost/optional.hpp> + +#include <vector> +#include <set> +#include <memory> + +namespace dev +{ +namespace yul +{ + +class ExpressionCopier: public boost::static_visitor<Expression> +{ +public: + virtual ~ExpressionCopier() = default; + virtual Expression operator()(Literal const& _literal) = 0; + virtual Expression operator()(Identifier const& _identifier) = 0; + virtual Expression operator()(FunctionalInstruction const& _instr) = 0; + virtual Expression operator()(FunctionCall const&) = 0; +}; + +class StatementCopier: public boost::static_visitor<Statement> +{ +public: + virtual ~StatementCopier() = default; + virtual Statement operator()(ExpressionStatement const& _statement) = 0; + virtual Statement operator()(Instruction const& _instruction) = 0; + virtual Statement operator()(Label const& _label) = 0; + virtual Statement operator()(StackAssignment const& _assignment) = 0; + virtual Statement operator()(Assignment const& _assignment) = 0; + virtual Statement operator()(VariableDeclaration const& _varDecl) = 0; + virtual Statement operator()(If const& _if) = 0; + virtual Statement operator()(Switch const& _switch) = 0; + virtual Statement operator()(FunctionDefinition const&) = 0; + virtual Statement operator()(ForLoop const&) = 0; + virtual Statement operator()(Block const& _block) = 0; +}; + +/** + * Creates a copy of a Yul AST potentially replacing identifier names. + * Base class to be extended. + */ +class ASTCopier: public ExpressionCopier, public StatementCopier +{ +public: + virtual ~ASTCopier() = default; + virtual Expression operator()(Literal const& _literal) override; + virtual Statement operator()(Instruction const& _instruction) override; + virtual Expression operator()(Identifier const& _identifier) override; + virtual Expression operator()(FunctionalInstruction const& _instr) override; + virtual Expression operator()(FunctionCall const&) override; + virtual Statement operator()(ExpressionStatement const& _statement) override; + virtual Statement operator()(Label const& _label) override; + virtual Statement operator()(StackAssignment const& _assignment) override; + virtual Statement operator()(Assignment const& _assignment) override; + virtual Statement operator()(VariableDeclaration const& _varDecl) override; + virtual Statement operator()(If const& _if) override; + virtual Statement operator()(Switch const& _switch) override; + virtual Statement operator()(FunctionDefinition const&) override; + virtual Statement operator()(ForLoop const&) override; + virtual Statement operator()(Block const& _block) override; + + virtual Expression translate(Expression const& _expression); + virtual Statement translate(Statement const& _statement); + +protected: + template <typename T> + std::vector<T> translateVector(std::vector<T> const& _values); + + template <typename T> + std::shared_ptr<T> translate(std::shared_ptr<T> const& _v) + { + return _v ? std::make_shared<T>(translate(*_v)) : nullptr; + } + Block translate(Block const& _block); + Case translate(Case const& _case); + Identifier translate(Identifier const& _identifier); + Literal translate(Literal const& _literal); + TypedName translate(TypedName const& _typedName); + + virtual void enterScope(Block const&) { } + virtual void leaveScope(Block const&) { } + virtual void enterFunction(FunctionDefinition const&) { } + virtual void leaveFunction(FunctionDefinition const&) { } + virtual YulString translateIdentifier(YulString _name) { return _name; } +}; + +template <typename T> +std::vector<T> ASTCopier::translateVector(std::vector<T> const& _values) +{ + std::vector<T> translated; + for (auto const& v: _values) + translated.emplace_back(translate(v)); + return translated; +} + + +} +} diff --git a/libyul/optimiser/ASTWalker.cpp b/libyul/optimiser/ASTWalker.cpp new file mode 100644 index 00000000..e29dda6b --- /dev/null +++ b/libyul/optimiser/ASTWalker.cpp @@ -0,0 +1,157 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see <http://www.gnu.org/licenses/>. +*/ +/** + * Generic AST walker. + */ + +#include <libyul/optimiser/ASTWalker.h> + +#include <libsolidity/inlineasm/AsmData.h> + +#include <boost/range/adaptor/reversed.hpp> + +using namespace std; +using namespace dev; +using namespace dev::yul; +using namespace dev::solidity; + + +void ASTWalker::operator()(FunctionalInstruction const& _instr) +{ + walkVector(_instr.arguments | boost::adaptors::reversed); +} + +void ASTWalker::operator()(FunctionCall const& _funCall) +{ + walkVector(_funCall.arguments | boost::adaptors::reversed); +} + +void ASTWalker::operator()(ExpressionStatement const& _statement) +{ + visit(_statement.expression); +} + +void ASTWalker::operator()(Assignment const& _assignment) +{ + for (auto const& name: _assignment.variableNames) + (*this)(name); + visit(*_assignment.value); +} + +void ASTWalker::operator()(VariableDeclaration const& _varDecl) +{ + if (_varDecl.value) + visit(*_varDecl.value); +} + +void ASTWalker::operator()(If const& _if) +{ + visit(*_if.condition); + (*this)(_if.body); +} + +void ASTWalker::operator()(Switch const& _switch) +{ + visit(*_switch.expression); + for (auto const& _case: _switch.cases) + { + if (_case.value) + (*this)(*_case.value); + (*this)(_case.body); + } +} + +void ASTWalker::operator()(FunctionDefinition const& _fun) +{ + (*this)(_fun.body); +} + +void ASTWalker::operator()(ForLoop const& _for) +{ + (*this)(_for.pre); + visit(*_for.condition); + (*this)(_for.body); + (*this)(_for.post); +} + +void ASTWalker::operator()(Block const& _block) +{ + walkVector(_block.statements); +} + +void ASTModifier::operator()(FunctionalInstruction& _instr) +{ + walkVector(_instr.arguments | boost::adaptors::reversed); +} + +void ASTModifier::operator()(FunctionCall& _funCall) +{ + walkVector(_funCall.arguments | boost::adaptors::reversed); +} + +void ASTModifier::operator()(ExpressionStatement& _statement) +{ + visit(_statement.expression); +} + +void ASTModifier::operator()(Assignment& _assignment) +{ + for (auto& name: _assignment.variableNames) + (*this)(name); + visit(*_assignment.value); +} + +void ASTModifier::operator()(VariableDeclaration& _varDecl) +{ + if (_varDecl.value) + visit(*_varDecl.value); +} + +void ASTModifier::operator()(If& _if) +{ + visit(*_if.condition); + (*this)(_if.body); +} + +void ASTModifier::operator()(Switch& _switch) +{ + visit(*_switch.expression); + for (auto& _case: _switch.cases) + { + if (_case.value) + (*this)(*_case.value); + (*this)(_case.body); + } +} + +void ASTModifier::operator()(FunctionDefinition& _fun) +{ + (*this)(_fun.body); +} + +void ASTModifier::operator()(ForLoop& _for) +{ + (*this)(_for.pre); + visit(*_for.condition); + (*this)(_for.post); + (*this)(_for.body); +} + +void ASTModifier::operator()(Block& _block) +{ + walkVector(_block.statements); +} diff --git a/libyul/optimiser/ASTWalker.h b/libyul/optimiser/ASTWalker.h new file mode 100644 index 00000000..38cb85ea --- /dev/null +++ b/libyul/optimiser/ASTWalker.h @@ -0,0 +1,123 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see <http://www.gnu.org/licenses/>. +*/ +/** + * Generic AST walker. + */ + +#pragma once + +#include <libyul/ASTDataForward.h> + +#include <libyul/Exceptions.h> +#include <libyul/YulString.h> + +#include <boost/variant.hpp> +#include <boost/optional.hpp> + +#include <vector> +#include <set> +#include <map> + +namespace dev +{ +namespace yul +{ + +/** + * Generic AST walker. + */ +class ASTWalker: public boost::static_visitor<> +{ +public: + virtual ~ASTWalker() = default; + virtual void operator()(Literal const&) {} + virtual void operator()(Instruction const&) { assertThrow(false, OptimizerException, ""); } + virtual void operator()(Identifier const&) {} + virtual void operator()(FunctionalInstruction const& _instr); + virtual void operator()(FunctionCall const& _funCall); + virtual void operator()(ExpressionStatement const& _statement); + virtual void operator()(Label const&) { assertThrow(false, OptimizerException, ""); } + virtual void operator()(StackAssignment const&) { assertThrow(false, OptimizerException, ""); } + virtual void operator()(Assignment const& _assignment); + virtual void operator()(VariableDeclaration const& _varDecl); + virtual void operator()(If const& _if); + virtual void operator()(Switch const& _switch); + virtual void operator()(FunctionDefinition const&); + virtual void operator()(ForLoop const&); + virtual void operator()(Block const& _block); + + virtual void visit(Statement const& _st) + { + boost::apply_visitor(*this, _st); + } + virtual void visit(Expression const& _e) + { + boost::apply_visitor(*this, _e); + } + +protected: + template <class T> + void walkVector(T const& _statements) + { + for (auto const& statement: _statements) + visit(statement); + } +}; + +/** + * Generic AST modifier (i.e. non-const version of ASTWalker). + */ +class ASTModifier: public boost::static_visitor<> +{ +public: + virtual ~ASTModifier() = default; + virtual void operator()(Literal&) {} + virtual void operator()(Instruction&) { assertThrow(false, OptimizerException, ""); } + virtual void operator()(Identifier&) {} + virtual void operator()(FunctionalInstruction& _instr); + virtual void operator()(FunctionCall& _funCall); + virtual void operator()(ExpressionStatement& _statement); + virtual void operator()(Label&) { assertThrow(false, OptimizerException, ""); } + virtual void operator()(StackAssignment&) { assertThrow(false, OptimizerException, ""); } + virtual void operator()(Assignment& _assignment); + virtual void operator()(VariableDeclaration& _varDecl); + virtual void operator()(If& _if); + virtual void operator()(Switch& _switch); + virtual void operator()(FunctionDefinition&); + virtual void operator()(ForLoop&); + virtual void operator()(Block& _block); + + virtual void visit(Statement& _st) + { + boost::apply_visitor(*this, _st); + } + virtual void visit(Expression& _e) + { + boost::apply_visitor(*this, _e); + } + +protected: + template <class T> + void walkVector(T&& _statements) + { + for (auto& st: _statements) + visit(st); + } +}; + +} +} diff --git a/libyul/optimiser/BlockFlattener.cpp b/libyul/optimiser/BlockFlattener.cpp new file mode 100644 index 00000000..04f3ad7f --- /dev/null +++ b/libyul/optimiser/BlockFlattener.cpp @@ -0,0 +1,41 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see <http://www.gnu.org/licenses/>. +*/ +#include <libyul/optimiser/BlockFlattener.h> +#include <libsolidity/inlineasm/AsmData.h> +#include <libdevcore/Visitor.h> +#include <libdevcore/CommonData.h> +#include <functional> + +using namespace std; +using namespace dev; +using namespace dev::yul; + +void BlockFlattener::operator()(Block& _block) +{ + ASTModifier::operator()(_block); + + iterateReplacing( + _block.statements, + [](Statement& _s) -> boost::optional<vector<Statement>> + { + if (_s.type() == typeid(Block)) + return std::move(boost::get<Block>(_s).statements); + else + return {}; + } + ); +} diff --git a/libyul/optimiser/BlockFlattener.h b/libyul/optimiser/BlockFlattener.h new file mode 100644 index 00000000..88c49dda --- /dev/null +++ b/libyul/optimiser/BlockFlattener.h @@ -0,0 +1,34 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see <http://www.gnu.org/licenses/>. +*/ +#pragma once + +#include <libyul/optimiser/ASTWalker.h> + +namespace dev +{ +namespace yul +{ + +class BlockFlattener: public ASTModifier +{ +public: + using ASTModifier::operator(); + void operator()(Block& _block) override; +}; + +} +} diff --git a/libyul/optimiser/CommonSubexpressionEliminator.cpp b/libyul/optimiser/CommonSubexpressionEliminator.cpp new file mode 100644 index 00000000..64605362 --- /dev/null +++ b/libyul/optimiser/CommonSubexpressionEliminator.cpp @@ -0,0 +1,72 @@ +/*( + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see <http://www.gnu.org/licenses/>. +*/ +/** + * Optimisation stage that replaces expressions known to be the current value of a variable + * in scope by a reference to that variable. + */ + +#include <libyul/optimiser/CommonSubexpressionEliminator.h> + +#include <libyul/optimiser/Metrics.h> +#include <libyul/optimiser/SyntacticalEquality.h> +#include <libyul/Exceptions.h> + +#include <libsolidity/inlineasm/AsmData.h> + +using namespace std; +using namespace dev; +using namespace dev::yul; + +void CommonSubexpressionEliminator::visit(Expression& _e) +{ + // We visit the inner expression first to first simplify inner expressions, + // which hopefully allows more matches. + // Note that the DataFlowAnalyzer itself only has code for visiting Statements, + // so this basically invokes the AST walker directly and thus post-visiting + // is also fine with regards to data flow analysis. + DataFlowAnalyzer::visit(_e); + + if (_e.type() == typeid(Identifier)) + { + Identifier& identifier = boost::get<Identifier>(_e); + YulString name = identifier.name; + if (m_value.count(name)) + { + assertThrow(m_value.at(name), OptimizerException, ""); + if (m_value.at(name)->type() == typeid(Identifier)) + { + YulString value = boost::get<Identifier>(*m_value.at(name)).name; + assertThrow(inScope(value), OptimizerException, ""); + _e = Identifier{locationOf(_e), value}; + } + } + } + else + { + // TODO this search is rather inefficient. + for (auto const& var: m_value) + { + assertThrow(var.second, OptimizerException, ""); + assertThrow(inScope(var.first), OptimizerException, ""); + if (SyntacticalEqualityChecker::equal(_e, *var.second)) + { + _e = Identifier{locationOf(_e), var.first}; + break; + } + } + } +} diff --git a/libyul/optimiser/CommonSubexpressionEliminator.h b/libyul/optimiser/CommonSubexpressionEliminator.h new file mode 100644 index 00000000..f8aa0ee1 --- /dev/null +++ b/libyul/optimiser/CommonSubexpressionEliminator.h @@ -0,0 +1,45 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see <http://www.gnu.org/licenses/>. +*/ +/** + * Optimisation stage that replaces expressions known to be the current value of a variable + * in scope by a reference to that variable. + */ + +#pragma once + +#include <libyul/optimiser/DataFlowAnalyzer.h> + +namespace dev +{ +namespace yul +{ + +/** + * Optimisation stage that replaces expressions known to be the current value of a variable + * in scope by a reference to that variable. + * + * Prerequisite: Disambiguator + */ +class CommonSubexpressionEliminator: public DataFlowAnalyzer +{ +protected: + using ASTModifier::visit; + virtual void visit(Expression& _e) override; +}; + +} +} diff --git a/libyul/optimiser/DataFlowAnalyzer.cpp b/libyul/optimiser/DataFlowAnalyzer.cpp new file mode 100644 index 00000000..134777d0 --- /dev/null +++ b/libyul/optimiser/DataFlowAnalyzer.cpp @@ -0,0 +1,215 @@ +/*( + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see <http://www.gnu.org/licenses/>. +*/ +/** + * Base class to perform data flow analysis during AST walks. + * Tracks assignments and is used as base class for both Rematerialiser and + * Common Subexpression Eliminator. + */ + +#include <libyul/optimiser/DataFlowAnalyzer.h> + +#include <libyul/optimiser/NameCollector.h> +#include <libyul/optimiser/Semantics.h> +#include <libyul/Exceptions.h> + +#include <libsolidity/inlineasm/AsmData.h> + +#include <libdevcore/CommonData.h> + +#include <boost/range/adaptor/reversed.hpp> + +using namespace std; +using namespace dev; +using namespace dev::yul; + +void DataFlowAnalyzer::operator()(Assignment& _assignment) +{ + set<YulString> names; + for (auto const& var: _assignment.variableNames) + names.emplace(var.name); + assertThrow(_assignment.value, OptimizerException, ""); + visit(*_assignment.value); + handleAssignment(names, _assignment.value.get()); +} + +void DataFlowAnalyzer::operator()(VariableDeclaration& _varDecl) +{ + set<YulString> names; + for (auto const& var: _varDecl.variables) + names.emplace(var.name); + m_variableScopes.back().variables += names; + if (_varDecl.value) + visit(*_varDecl.value); + handleAssignment(names, _varDecl.value.get()); +} + +void DataFlowAnalyzer::operator()(If& _if) +{ + ASTModifier::operator()(_if); + + Assignments assignments; + assignments(_if.body); + clearValues(assignments.names()); +} + +void DataFlowAnalyzer::operator()(Switch& _switch) +{ + visit(*_switch.expression); + set<YulString> assignedVariables; + for (auto& _case: _switch.cases) + { + (*this)(_case.body); + Assignments assignments; + assignments(_case.body); + assignedVariables += assignments.names(); + // This is a little too destructive, we could retain the old values. + clearValues(assignments.names()); + } + clearValues(assignedVariables); +} + +void DataFlowAnalyzer::operator()(FunctionDefinition& _fun) +{ + // Save all information. We might rather reinstantiate this class, + // but this could be difficult if it is subclassed. + map<YulString, Expression const*> value; + map<YulString, set<YulString>> references; + map<YulString, set<YulString>> referencedBy; + m_value.swap(value); + m_references.swap(references); + m_referencedBy.swap(referencedBy); + pushScope(true); + + for (auto const& parameter: _fun.parameters) + m_variableScopes.back().variables.emplace(parameter.name); + for (auto const& var: _fun.returnVariables) + m_variableScopes.back().variables.emplace(var.name); + ASTModifier::operator()(_fun); + + popScope(); + m_value.swap(value); + m_references.swap(references); + m_referencedBy.swap(referencedBy); +} + +void DataFlowAnalyzer::operator()(ForLoop& _for) +{ + // Special scope handling of the pre block. + pushScope(false); + for (auto& statement: _for.pre.statements) + visit(statement); + + Assignments assignments; + assignments(_for.body); + assignments(_for.post); + clearValues(assignments.names()); + + visit(*_for.condition); + (*this)(_for.body); + (*this)(_for.post); + + clearValues(assignments.names()); + popScope(); +} + +void DataFlowAnalyzer::operator()(Block& _block) +{ + size_t numScopes = m_variableScopes.size(); + pushScope(false); + ASTModifier::operator()(_block); + popScope(); + assertThrow(numScopes == m_variableScopes.size(), OptimizerException, ""); +} + +void DataFlowAnalyzer::handleAssignment(set<YulString> const& _variables, Expression* _value) +{ + clearValues(_variables); + + MovableChecker movableChecker; + if (_value) + movableChecker.visit(*_value); + if (_variables.size() == 1) + { + YulString name = *_variables.begin(); + // Expression has to be movable and cannot contain a reference + // to the variable that will be assigned to. + if (_value && movableChecker.movable() && !movableChecker.referencedVariables().count(name)) + m_value[name] = _value; + } + + auto const& referencedVariables = movableChecker.referencedVariables(); + for (auto const& name: _variables) + { + m_references[name] = referencedVariables; + for (auto const& ref: referencedVariables) + m_referencedBy[ref].emplace(name); + } +} + +void DataFlowAnalyzer::pushScope(bool _functionScope) +{ + m_variableScopes.emplace_back(_functionScope); +} + +void DataFlowAnalyzer::popScope() +{ + clearValues(std::move(m_variableScopes.back().variables)); + m_variableScopes.pop_back(); +} + +void DataFlowAnalyzer::clearValues(set<YulString> _variables) +{ + // All variables that reference variables to be cleared also have to be + // cleared, but not recursively, since only the value of the original + // variables changes. Example: + // let a := 1 + // let b := a + // let c := b + // let a := 2 + // add(b, c) + // In the last line, we can replace c by b, but not b by a. + // + // This cannot be easily tested since the substitutions will be done + // one by one on the fly, and the last line will just be add(1, 1) + + // Clear variables that reference variables to be cleared. + for (auto const& name: _variables) + for (auto const& ref: m_referencedBy[name]) + _variables.emplace(ref); + + // Clear the value and update the reference relation. + for (auto const& name: _variables) + m_value.erase(name); + for (auto const& name: _variables) + { + for (auto const& ref: m_references[name]) + m_referencedBy[ref].erase(name); + m_references[name].clear(); + } +} + +bool DataFlowAnalyzer::inScope(YulString _variableName) const +{ + for (auto const& scope: m_variableScopes | boost::adaptors::reversed) + { + if (scope.variables.count(_variableName)) + return true; + if (scope.isFunction) + return false; + } + return false; +} diff --git a/libyul/optimiser/DataFlowAnalyzer.h b/libyul/optimiser/DataFlowAnalyzer.h new file mode 100644 index 00000000..a0c21eee --- /dev/null +++ b/libyul/optimiser/DataFlowAnalyzer.h @@ -0,0 +1,91 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see <http://www.gnu.org/licenses/>. +*/ +/** + * Base class to perform data flow analysis during AST walks. + * Tracks assignments and is used as base class for both Rematerialiser and + * Common Subexpression Eliminator. + */ + +#pragma once + +#include <libyul/optimiser/ASTWalker.h> + +#include <libyul/YulString.h> + +#include <map> +#include <set> + +namespace dev +{ +namespace yul +{ + +/** + * Base class to perform data flow analysis during AST walks. + * Tracks assignments and is used as base class for both Rematerialiser and + * Common Subexpression Eliminator. + * + * Prerequisite: Disambiguator + */ +class DataFlowAnalyzer: public ASTModifier +{ +public: + using ASTModifier::operator(); + virtual void operator()(Assignment& _assignment) override; + virtual void operator()(VariableDeclaration& _varDecl) override; + virtual void operator()(If& _if) override; + virtual void operator()(Switch& _switch) override; + virtual void operator()(FunctionDefinition&) override; + virtual void operator()(ForLoop&) override; + virtual void operator()(Block& _block) override; + +protected: + /// Registers the assignment. + void handleAssignment(std::set<YulString> const& _names, Expression* _value); + + /// Creates a new inner scope. + void pushScope(bool _functionScope); + + /// Removes the innermost scope and clears all variables in it. + void popScope(); + + /// Clears information about the values assigned to the given variables, + /// for example at points where control flow is merged. + void clearValues(std::set<YulString> _names); + + /// Returns true iff the variable is in scope. + bool inScope(YulString _variableName) const; + + /// Current values of variables, always movable. + std::map<YulString, Expression const*> m_value; + /// m_references[a].contains(b) <=> the current expression assigned to a references b + std::map<YulString, std::set<YulString>> m_references; + /// m_referencedBy[b].contains(a) <=> the current expression assigned to a references b + std::map<YulString, std::set<YulString>> m_referencedBy; + + struct Scope + { + explicit Scope(bool _isFunction): isFunction(_isFunction) {} + std::set<YulString> variables; + bool isFunction; + }; + /// List of scopes. + std::vector<Scope> m_variableScopes; +}; + +} +} diff --git a/libyul/optimiser/Disambiguator.cpp b/libyul/optimiser/Disambiguator.cpp new file mode 100644 index 00000000..4303f412 --- /dev/null +++ b/libyul/optimiser/Disambiguator.cpp @@ -0,0 +1,78 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see <http://www.gnu.org/licenses/>. +*/ +/** + * Optimiser component that makes all identifiers unique. + */ + +#include <libyul/optimiser/Disambiguator.h> + +#include <libyul/Exceptions.h> + +#include <libsolidity/inlineasm/AsmData.h> +#include <libsolidity/inlineasm/AsmScope.h> + +using namespace std; +using namespace dev; +using namespace dev::yul; +using namespace dev::solidity; + +using Scope = dev::solidity::assembly::Scope; + +YulString Disambiguator::translateIdentifier(YulString _originalName) +{ + if ((m_externallyUsedIdentifiers.count(_originalName))) + return _originalName; + + assertThrow(!m_scopes.empty() && m_scopes.back(), OptimizerException, ""); + Scope::Identifier const* id = m_scopes.back()->lookup(_originalName); + assertThrow(id, OptimizerException, ""); + if (!m_translations.count(id)) + m_translations[id] = m_nameDispenser.newName(_originalName); + return m_translations.at(id); +} + +void Disambiguator::enterScope(Block const& _block) +{ + enterScopeInternal(*m_info.scopes.at(&_block)); +} + +void Disambiguator::leaveScope(Block const& _block) +{ + leaveScopeInternal(*m_info.scopes.at(&_block)); +} + +void Disambiguator::enterFunction(FunctionDefinition const& _function) +{ + enterScopeInternal(*m_info.scopes.at(m_info.virtualBlocks.at(&_function).get())); +} + +void Disambiguator::leaveFunction(FunctionDefinition const& _function) +{ + leaveScopeInternal(*m_info.scopes.at(m_info.virtualBlocks.at(&_function).get())); +} + +void Disambiguator::enterScopeInternal(Scope& _scope) +{ + m_scopes.push_back(&_scope); +} + +void Disambiguator::leaveScopeInternal(Scope& _scope) +{ + assertThrow(!m_scopes.empty(), OptimizerException, ""); + assertThrow(m_scopes.back() == &_scope, OptimizerException, ""); + m_scopes.pop_back(); +} diff --git a/libyul/optimiser/Disambiguator.h b/libyul/optimiser/Disambiguator.h new file mode 100644 index 00000000..bfb65682 --- /dev/null +++ b/libyul/optimiser/Disambiguator.h @@ -0,0 +1,73 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see <http://www.gnu.org/licenses/>. +*/ +/** + * Optimiser component that makes all identifiers unique. + */ + +#pragma once + +#include <libyul/ASTDataForward.h> + +#include <libyul/optimiser/ASTCopier.h> +#include <libyul/optimiser/NameDispenser.h> + +#include <libsolidity/inlineasm/AsmAnalysisInfo.h> + +#include <boost/variant.hpp> +#include <boost/optional.hpp> + +#include <set> + +namespace dev +{ +namespace yul +{ + +/** + * Creates a copy of a Yul AST replacing all identifiers by unique names. + */ +class Disambiguator: public ASTCopier +{ +public: + explicit Disambiguator( + solidity::assembly::AsmAnalysisInfo const& _analysisInfo, + std::set<YulString> const& _externallyUsedIdentifiers = {} + ): + m_info(_analysisInfo), m_externallyUsedIdentifiers(_externallyUsedIdentifiers), m_nameDispenser(m_externallyUsedIdentifiers) + { + } + +protected: + virtual void enterScope(Block const& _block) override; + virtual void leaveScope(Block const& _block) override; + virtual void enterFunction(FunctionDefinition const& _function) override; + virtual void leaveFunction(FunctionDefinition const& _function) override; + virtual YulString translateIdentifier(YulString _name) override; + + void enterScopeInternal(solidity::assembly::Scope& _scope); + void leaveScopeInternal(solidity::assembly::Scope& _scope); + + solidity::assembly::AsmAnalysisInfo const& m_info; + std::set<YulString> const& m_externallyUsedIdentifiers; + + std::vector<solidity::assembly::Scope*> m_scopes; + std::map<void const*, YulString> m_translations; + NameDispenser m_nameDispenser; +}; + +} +} diff --git a/libyul/optimiser/ExpressionInliner.cpp b/libyul/optimiser/ExpressionInliner.cpp new file mode 100644 index 00000000..07e88191 --- /dev/null +++ b/libyul/optimiser/ExpressionInliner.cpp @@ -0,0 +1,74 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see <http://www.gnu.org/licenses/>. +*/ +/** + * Optimiser component that performs function inlining inside expressions. + */ + +#include <libyul/optimiser/ExpressionInliner.h> + +#include <libyul/optimiser/InlinableExpressionFunctionFinder.h> +#include <libyul/optimiser/Substitution.h> +#include <libyul/optimiser/Semantics.h> + +#include <libsolidity/inlineasm/AsmData.h> + +#include <boost/algorithm/cxx11/all_of.hpp> + +using namespace std; +using namespace dev; +using namespace dev::yul; +using namespace dev::solidity; + +void ExpressionInliner::run() +{ + InlinableExpressionFunctionFinder funFinder; + funFinder(m_block); + m_inlinableFunctions = funFinder.inlinableFunctions(); + + (*this)(m_block); +} + + +void ExpressionInliner::operator()(FunctionDefinition& _fun) +{ + ASTModifier::operator()(_fun); +} + +void ExpressionInliner::visit(Expression& _expression) +{ + ASTModifier::visit(_expression); + if (_expression.type() == typeid(FunctionCall)) + { + FunctionCall& funCall = boost::get<FunctionCall>(_expression); + + bool movable = boost::algorithm::all_of( + funCall.arguments, + [=](Expression const& _arg) { return MovableChecker(_arg).movable(); } + ); + if (m_inlinableFunctions.count(funCall.functionName.name) && movable) + { + FunctionDefinition const& fun = *m_inlinableFunctions.at(funCall.functionName.name); + map<YulString, Expression const*> substitutions; + for (size_t i = 0; i < fun.parameters.size(); ++i) + substitutions[fun.parameters[i].name] = &funCall.arguments[i]; + _expression = Substitution(substitutions).translate(*boost::get<Assignment>(fun.body.statements.front()).value); + + // TODO Add metric! This metric should perform well on a pair of functions who + // call each other recursively. + } + } +} diff --git a/libyul/optimiser/ExpressionInliner.h b/libyul/optimiser/ExpressionInliner.h new file mode 100644 index 00000000..d903664f --- /dev/null +++ b/libyul/optimiser/ExpressionInliner.h @@ -0,0 +1,72 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see <http://www.gnu.org/licenses/>. +*/ +/** + * Optimiser component that performs function inlining. + */ +#pragma once + +#include <libyul/optimiser/ASTWalker.h> + +#include <libyul/ASTDataForward.h> + +#include <boost/variant.hpp> +#include <boost/optional.hpp> + +#include <set> + +namespace dev +{ +namespace yul +{ + +/** + * Optimiser component that modifies an AST in place, inlining functions that can be + * inlined inside functional expressions, i.e. functions that + * - return a single value + * - have a body like r := <functional expression> + * - neither reference themselves nor r in the right hand side + * + * Furthermore, the arguments of the function call cannot have any side-effects. + * + * This component can only be used on sources with unique names. + */ +class ExpressionInliner: public ASTModifier +{ +public: + ExpressionInliner(Block& _block): + m_block(_block) + {} + + void run(); + + using ASTModifier::operator(); + virtual void operator()(FunctionDefinition& _fun) override; + + virtual void visit(Expression& _expression) override; + +private: + std::map<YulString, FunctionDefinition const*> m_inlinableFunctions; + std::map<YulString, YulString> m_varReplacements; + /// Set of functions we are currently visiting inside. + std::set<YulString> m_currentFunctions; + + Block& m_block; +}; + + +} +} diff --git a/libyul/optimiser/ExpressionJoiner.cpp b/libyul/optimiser/ExpressionJoiner.cpp new file mode 100644 index 00000000..7e57a629 --- /dev/null +++ b/libyul/optimiser/ExpressionJoiner.cpp @@ -0,0 +1,150 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see <http://www.gnu.org/licenses/>. +*/ +/** + * Optimiser component that undoes what the ExpressionSplitter did, i.e. + * it more or less inlines variable declarations. + */ + +#include <libyul/optimiser/ExpressionJoiner.h> + +#include <libyul/optimiser/NameCollector.h> +#include <libyul/optimiser/Utilities.h> +#include <libyul/Exceptions.h> + +#include <libsolidity/inlineasm/AsmData.h> + +#include <libdevcore/CommonData.h> + +#include <boost/range/adaptor/reversed.hpp> + +using namespace std; +using namespace dev; +using namespace dev::yul; +using namespace dev::solidity; + +void ExpressionJoiner::operator()(FunctionalInstruction& _instruction) +{ + handleArguments(_instruction.arguments); +} + +void ExpressionJoiner::operator()(FunctionCall& _funCall) +{ + handleArguments(_funCall.arguments); +} + +void ExpressionJoiner::operator()(Block& _block) +{ + resetLatestStatementPointer(); + for (size_t i = 0; i < _block.statements.size(); ++i) + { + visit(_block.statements[i]); + m_currentBlock = &_block; + m_latestStatementInBlock = i; + } + + removeEmptyBlocks(_block); + resetLatestStatementPointer(); +} + +void ExpressionJoiner::visit(Expression& _e) +{ + if (_e.type() == typeid(Identifier)) + { + Identifier const& identifier = boost::get<Identifier>(_e); + if (isLatestStatementVarDeclJoinable(identifier)) + { + VariableDeclaration& varDecl = boost::get<VariableDeclaration>(*latestStatement()); + _e = std::move(*varDecl.value); + + // Delete the variable declaration (also get the moved-from structure back into a sane state) + *latestStatement() = Block(); + + decrementLatestStatementPointer(); + } + } + else + ASTModifier::visit(_e); +} + +void ExpressionJoiner::run(Block& _ast) +{ + ExpressionJoiner{_ast}(_ast); +} + +ExpressionJoiner::ExpressionJoiner(Block& _ast) +{ + m_references = ReferencesCounter::countReferences(_ast); +} + +void ExpressionJoiner::handleArguments(vector<Expression>& _arguments) +{ + // We have to fill from left to right, but we can only + // fill if everything to the right is just an identifier + // or a literal. + // Also we only descend into function calls if everything + // on the right is an identifier or literal. + + size_t i = _arguments.size(); + for (Expression const& arg: _arguments | boost::adaptors::reversed) + { + --i; + if (arg.type() != typeid(Identifier) && arg.type() != typeid(Literal)) + break; + } + // i points to the last element that is neither an identifier nor a literal, + // or to the first element if all of them are identifiers or literals. + + for (; i < _arguments.size(); ++i) + visit(_arguments.at(i)); +} + +void ExpressionJoiner::decrementLatestStatementPointer() +{ + if (!m_currentBlock) + return; + if (m_latestStatementInBlock > 0) + --m_latestStatementInBlock; + else + resetLatestStatementPointer(); +} + +void ExpressionJoiner::resetLatestStatementPointer() +{ + m_currentBlock = nullptr; + m_latestStatementInBlock = size_t(-1); +} + +Statement* ExpressionJoiner::latestStatement() +{ + if (!m_currentBlock) + return nullptr; + else + return &m_currentBlock->statements.at(m_latestStatementInBlock); +} + +bool ExpressionJoiner::isLatestStatementVarDeclJoinable(Identifier const& _identifier) +{ + Statement const* statement = latestStatement(); + if (!statement || statement->type() != typeid(VariableDeclaration)) + return false; + VariableDeclaration const& varDecl = boost::get<VariableDeclaration>(*statement); + if (varDecl.variables.size() != 1 || !varDecl.value) + return false; + assertThrow(varDecl.variables.size() == 1, OptimizerException, ""); + assertThrow(varDecl.value, OptimizerException, ""); + return varDecl.variables.at(0).name == _identifier.name && m_references[_identifier.name] == 1; +} diff --git a/libyul/optimiser/ExpressionJoiner.h b/libyul/optimiser/ExpressionJoiner.h new file mode 100644 index 00000000..0cc61981 --- /dev/null +++ b/libyul/optimiser/ExpressionJoiner.h @@ -0,0 +1,102 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see <http://www.gnu.org/licenses/>. +*/ +/** + * Optimiser component that undoes what the ExpressionSplitter did, i.e. + * it more or less inlines variable declarations. + */ +#pragma once + +#include <libyul/ASTDataForward.h> + +#include <libyul/optimiser/ASTWalker.h> + +#include <map> + +namespace dev +{ +namespace yul +{ + +class NameCollector; + + +/** + * Optimiser component that modifies an AST in place, turning sequences + * of variable declarations into complex expressions, if the variables + * are declared in the right order. This component does the opposite + * of ExpressionSplitter. + * Since the order of opcode or function evaluation is unchanged, + * this transformation does not need to care about conflicting opcodes. + * + * Code of the form + * + * let a1 := mload(y) + * let a2 := mul(x, 4) + * sstore(a2, a1) + * + * is transformed into + * + * sstore(mul(x, 4), mload(y)) + * + * The transformation is not applied to loop conditions, because those are + * evaluated with each loop. + * + * The component can be applied to sub-blocks of the AST, you do not + * need to pass a full AST. + * + * Prerequisites: Disambiguator + * + * Implementation note: We visit the AST, modifying it in place. + * The class starts counting references and will only replace variables + * that have exactly one reference. It keeps a "latest statement pointer" + * which always points to the statement right before the current statement. + * Any function call or opcode will reset this pointer. If an identifier + * is encountered that was declared in the "latest statement", it is replaced + * by the value of the declaration, the "latest statement" is replaced + * by an empty block and the pointer is decremented. + * A block also resets the latest statement pointer. + */ +class ExpressionJoiner: public ASTModifier +{ +public: + static void run(Block& _ast); + +private: + explicit ExpressionJoiner(Block& _ast); + + void operator()(Block& _block) override; + void operator()(FunctionalInstruction&) override; + void operator()(FunctionCall&) override; + + using ASTModifier::visit; + void visit(Expression& _e) override; + + void handleArguments(std::vector<Expression>& _arguments); + + void decrementLatestStatementPointer(); + void resetLatestStatementPointer(); + Statement* latestStatement(); + bool isLatestStatementVarDeclJoinable(Identifier const& _identifier); + +private: + Block* m_currentBlock = nullptr; ///< Pointer to current block holding the statement being visited. + size_t m_latestStatementInBlock = 0; ///< Offset to m_currentBlock's statements of the last visited statement. + std::map<YulString, size_t> m_references; ///< Holds reference counts to all variable declarations in current block. +}; + +} +} diff --git a/libyul/optimiser/ExpressionSimplifier.cpp b/libyul/optimiser/ExpressionSimplifier.cpp new file mode 100644 index 00000000..64e9d7e7 --- /dev/null +++ b/libyul/optimiser/ExpressionSimplifier.cpp @@ -0,0 +1,60 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see <http://www.gnu.org/licenses/>. +*/ +/** + * Optimiser component that uses the simplification rules to simplify expressions. + */ + +#include <libyul/optimiser/ExpressionSimplifier.h> + +#include <libyul/optimiser/SimplificationRules.h> +#include <libyul/optimiser/Semantics.h> +#include <libyul/optimiser/SSAValueTracker.h> + +#include <libsolidity/inlineasm/AsmData.h> + +#include <libdevcore/CommonData.h> + +using namespace std; +using namespace dev; +using namespace dev::yul; +using namespace dev::solidity; + + +void ExpressionSimplifier::visit(Expression& _expression) +{ + ASTModifier::visit(_expression); + while (auto match = SimplificationRules::findFirstMatch(_expression, m_ssaValues)) + { + // Do not apply the rule if it removes non-constant parts of the expression. + // TODO: The check could actually be less strict than "movable". + // We only require "Does not cause side-effects". + // Note: References to variables that are only assigned once are always movable, + // so if the value of the variable is not movable, the expression that references + // the variable still is. + + if (match->removesNonConstants && !MovableChecker(_expression).movable()) + return; + _expression = match->action().toExpression(locationOf(_expression)); + } +} + +void ExpressionSimplifier::run(Block& _ast) +{ + SSAValueTracker ssaValues; + ssaValues(_ast); + ExpressionSimplifier{ssaValues.values()}(_ast); +} diff --git a/libyul/optimiser/ExpressionSimplifier.h b/libyul/optimiser/ExpressionSimplifier.h new file mode 100644 index 00000000..5965a1bb --- /dev/null +++ b/libyul/optimiser/ExpressionSimplifier.h @@ -0,0 +1,55 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see <http://www.gnu.org/licenses/>. +*/ +/** + * Optimiser component that uses the simplification rules to simplify expressions. + */ + +#pragma once + +#include <libyul/ASTDataForward.h> + +#include <libyul/optimiser/ASTWalker.h> + +namespace dev +{ +namespace yul +{ + +/** + * Applies simplification rules to all expressions. + * The component will work best if the code is in SSA form, but + * this is not required for correctness. + * + * Prerequisite: Disambiguator. + */ +class ExpressionSimplifier: public ASTModifier +{ +public: + using ASTModifier::operator(); + virtual void visit(Expression& _expression); + + static void run(Block& _ast); +private: + explicit ExpressionSimplifier(std::map<YulString, Expression const*> _ssaValues): + m_ssaValues(std::move(_ssaValues)) + {} + + std::map<YulString, Expression const*> m_ssaValues; +}; + +} +} diff --git a/libyul/optimiser/ExpressionSplitter.cpp b/libyul/optimiser/ExpressionSplitter.cpp new file mode 100644 index 00000000..a4b7a909 --- /dev/null +++ b/libyul/optimiser/ExpressionSplitter.cpp @@ -0,0 +1,105 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see <http://www.gnu.org/licenses/>. +*/ +/** + * Optimiser component that turns complex expressions into multiple variable + * declarations. + */ + +#include <libyul/optimiser/ExpressionSplitter.h> + +#include <libyul/optimiser/ASTWalker.h> + +#include <libsolidity/inlineasm/AsmData.h> + +#include <libdevcore/CommonData.h> + +#include <boost/range/adaptor/reversed.hpp> + +using namespace std; +using namespace dev; +using namespace dev::yul; +using namespace dev::solidity; + +void ExpressionSplitter::operator()(FunctionalInstruction& _instruction) +{ + for (auto& arg: _instruction.arguments | boost::adaptors::reversed) + outlineExpression(arg); +} + +void ExpressionSplitter::operator()(FunctionCall& _funCall) +{ + for (auto& arg: _funCall.arguments | boost::adaptors::reversed) + outlineExpression(arg); +} + +void ExpressionSplitter::operator()(If& _if) +{ + outlineExpression(*_if.condition); + (*this)(_if.body); +} + +void ExpressionSplitter::operator()(Switch& _switch) +{ + outlineExpression(*_switch.expression); + for (auto& _case: _switch.cases) + // Do not visit the case expression, nothing to split there. + (*this)(_case.body); +} + +void ExpressionSplitter::operator()(ForLoop& _loop) +{ + (*this)(_loop.pre); + // Do not visit the condition because we cannot split expressions there. + (*this)(_loop.post); + (*this)(_loop.body); +} + +void ExpressionSplitter::operator()(Block& _block) +{ + vector<Statement> saved; + swap(saved, m_statementsToPrefix); + + function<boost::optional<vector<Statement>>(Statement&)> f = + [&](Statement& _statement) -> boost::optional<vector<Statement>> { + m_statementsToPrefix.clear(); + visit(_statement); + if (m_statementsToPrefix.empty()) + return {}; + m_statementsToPrefix.emplace_back(std::move(_statement)); + return std::move(m_statementsToPrefix); + }; + iterateReplacing(_block.statements, f); + + swap(saved, m_statementsToPrefix); +} + +void ExpressionSplitter::outlineExpression(Expression& _expr) +{ + if (_expr.type() == typeid(Identifier)) + return; + + visit(_expr); + + SourceLocation location = locationOf(_expr); + YulString var = m_nameDispenser.newName({}); + m_statementsToPrefix.emplace_back(VariableDeclaration{ + location, + {{TypedName{location, var, {}}}}, + make_shared<Expression>(std::move(_expr)) + }); + _expr = Identifier{location, var}; +} diff --git a/libyul/optimiser/ExpressionSplitter.h b/libyul/optimiser/ExpressionSplitter.h new file mode 100644 index 00000000..339acbf0 --- /dev/null +++ b/libyul/optimiser/ExpressionSplitter.h @@ -0,0 +1,86 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see <http://www.gnu.org/licenses/>. +*/ +/** + * Optimiser component that turns complex expressions into multiple variable + * declarations. + */ +#pragma once + +#include <libyul/ASTDataForward.h> + +#include <libyul/optimiser/ASTWalker.h> +#include <libyul/optimiser/NameDispenser.h> + +#include <vector> + +namespace dev +{ +namespace yul +{ + +class NameCollector; + + +/** + * Optimiser component that modifies an AST in place, turning complex + * expressions into simple expressions and multiple variable declarations. + * + * Code of the form + * + * sstore(mul(x, 4), mload(y)) + * + * is transformed into + * + * let a1 := mload(y) + * let a2 := mul(x, 4) + * sstore(a2, a1) + * + * The transformation is not applied to loop conditions, because the loop control flow + * does not allow "outlining" the inner expressions in all cases. + * + * The final program should be in a form such that with the exception of a loop condition, + * function calls can only appear in the right-hand side of a variable declaration, + * assignments or expression statements and all arguments have to be constants or variables. + */ +class ExpressionSplitter: public ASTModifier +{ +public: + explicit ExpressionSplitter(NameDispenser& _nameDispenser): + m_nameDispenser(_nameDispenser) + { } + + virtual void operator()(FunctionalInstruction&) override; + virtual void operator()(FunctionCall&) override; + virtual void operator()(If&) override; + virtual void operator()(Switch&) override; + virtual void operator()(ForLoop&) override; + virtual void operator()(Block& _block) override; + +private: + /// Replaces the expression by a variable if it is a function call or functional + /// instruction. The declaration of the variable is appended to m_statementsToPrefix. + /// Recurses via visit(). + void outlineExpression(Expression& _expr); + + /// List of statements that should go in front of the currently visited AST element, + /// at the statement level. + std::vector<Statement> m_statementsToPrefix; + NameDispenser& m_nameDispenser; +}; + +} +} diff --git a/libyul/optimiser/FullInliner.cpp b/libyul/optimiser/FullInliner.cpp new file mode 100644 index 00000000..c9057cf3 --- /dev/null +++ b/libyul/optimiser/FullInliner.cpp @@ -0,0 +1,223 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see <http://www.gnu.org/licenses/>. +*/ +/** + * Optimiser component that performs function inlining for arbitrary functions. + */ + +#include <libyul/optimiser/FullInliner.h> + +#include <libyul/optimiser/ASTCopier.h> +#include <libyul/optimiser/ASTWalker.h> +#include <libyul/optimiser/NameCollector.h> +#include <libyul/optimiser/Utilities.h> +#include <libyul/optimiser/Metrics.h> +#include <libyul/optimiser/SSAValueTracker.h> +#include <libyul/Exceptions.h> + +#include <libsolidity/inlineasm/AsmData.h> + +#include <libdevcore/CommonData.h> +#include <libdevcore/Visitor.h> + +#include <boost/range/adaptor/reversed.hpp> + +using namespace std; +using namespace dev; +using namespace dev::yul; +using namespace dev::solidity; + +FullInliner::FullInliner(Block& _ast, NameDispenser& _dispenser): + m_ast(_ast), m_nameDispenser(_dispenser) +{ + // Determine constants + SSAValueTracker tracker; + tracker(m_ast); + for (auto const& ssaValue: tracker.values()) + if (ssaValue.second && ssaValue.second->type() == typeid(Literal)) + m_constants.emplace(ssaValue.first); + + map<YulString, size_t> references = ReferencesCounter::countReferences(m_ast); + for (auto& statement: m_ast.statements) + { + if (statement.type() != typeid(FunctionDefinition)) + continue; + FunctionDefinition& fun = boost::get<FunctionDefinition>(statement); + m_functions[fun.name] = &fun; + // Always inline functions that are only called once. + if (references[fun.name] == 1) + m_alwaysInline.emplace(fun.name); + updateCodeSize(fun); + } +} + +void FullInliner::run() +{ + for (auto& statement: m_ast.statements) + if (statement.type() == typeid(Block)) + handleBlock({}, boost::get<Block>(statement)); + + // TODO it might be good to determine a visiting order: + // first handle functions that are called from many places. + for (auto const& fun: m_functions) + { + handleBlock(fun.second->name, fun.second->body); + updateCodeSize(*fun.second); + } +} + +void FullInliner::updateCodeSize(FunctionDefinition& fun) +{ + m_functionSizes[fun.name] = CodeSize::codeSize(fun.body); +} + +void FullInliner::handleBlock(YulString _currentFunctionName, Block& _block) +{ + InlineModifier{*this, m_nameDispenser, _currentFunctionName}(_block); +} + +bool FullInliner::shallInline(FunctionCall const& _funCall, YulString _callSite) +{ + // No recursive inlining + if (_funCall.functionName.name == _callSite) + return false; + + FunctionDefinition& calledFunction = function(_funCall.functionName.name); + if (m_alwaysInline.count(calledFunction.name)) + return true; + + // Constant arguments might provide a means for further optimization, so they cause a bonus. + bool constantArg = false; + for (auto const& argument: _funCall.arguments) + if (argument.type() == typeid(Literal) || ( + argument.type() == typeid(Identifier) && + m_constants.count(boost::get<Identifier>(argument).name) + )) + { + constantArg = true; + break; + } + + size_t size = m_functionSizes.at(calledFunction.name); + return (size < 10 || (constantArg && size < 50)); +} + + +void InlineModifier::operator()(Block& _block) +{ + function<boost::optional<vector<Statement>>(Statement&)> f = [&](Statement& _statement) -> boost::optional<vector<Statement>> { + visit(_statement); + return tryInlineStatement(_statement); + }; + iterateReplacing(_block.statements, f); +} + +boost::optional<vector<Statement>> InlineModifier::tryInlineStatement(Statement& _statement) +{ + // Only inline for expression statements, assignments and variable declarations. + Expression* e = boost::apply_visitor(GenericFallbackReturnsVisitor<Expression*, ExpressionStatement, Assignment, VariableDeclaration>( + [](ExpressionStatement& _s) { return &_s.expression; }, + [](Assignment& _s) { return _s.value.get(); }, + [](VariableDeclaration& _s) { return _s.value.get(); } + ), _statement); + if (e) + { + // Only inline direct function calls. + FunctionCall* funCall = boost::apply_visitor(GenericFallbackReturnsVisitor<FunctionCall*, FunctionCall&>( + [](FunctionCall& _e) { return &_e; } + ), *e); + if (funCall && m_driver.shallInline(*funCall, m_currentFunction)) + return performInline(_statement, *funCall); + } + return {}; +} + +vector<Statement> InlineModifier::performInline(Statement& _statement, FunctionCall& _funCall) +{ + vector<Statement> newStatements; + map<YulString, YulString> variableReplacements; + + FunctionDefinition& function = m_driver.function(_funCall.functionName.name); + + // helper function to create a new variable that is supposed to model + // an existing variable. + auto newVariable = [&](TypedName const& _existingVariable, Expression* _value) { + YulString newName = m_nameDispenser.newName(_existingVariable.name, function.name); + variableReplacements[_existingVariable.name] = newName; + VariableDeclaration varDecl{_funCall.location, {{_funCall.location, newName, _existingVariable.type}}, {}}; + if (_value) + varDecl.value = make_shared<Expression>(std::move(*_value)); + newStatements.emplace_back(std::move(varDecl)); + }; + + for (size_t i = 0; i < _funCall.arguments.size(); ++i) + newVariable(function.parameters[i], &_funCall.arguments[i]); + for (auto const& var: function.returnVariables) + newVariable(var, nullptr); + + Statement newBody = BodyCopier(m_nameDispenser, function.name, variableReplacements)(function.body); + newStatements += std::move(boost::get<Block>(newBody).statements); + + boost::apply_visitor(GenericFallbackVisitor<Assignment, VariableDeclaration>{ + [&](Assignment& _assignment) + { + for (size_t i = 0; i < _assignment.variableNames.size(); ++i) + newStatements.emplace_back(Assignment{ + _assignment.location, + {_assignment.variableNames[i]}, + make_shared<Expression>(Identifier{ + _assignment.location, + variableReplacements.at(function.returnVariables[i].name) + }) + }); + }, + [&](VariableDeclaration& _varDecl) + { + for (size_t i = 0; i < _varDecl.variables.size(); ++i) + newStatements.emplace_back(VariableDeclaration{ + _varDecl.location, + {std::move(_varDecl.variables[i])}, + make_shared<Expression>(Identifier{ + _varDecl.location, + variableReplacements.at(function.returnVariables[i].name) + }) + }); + } + // nothing to be done for expression statement + }, _statement); + return newStatements; +} + +Statement BodyCopier::operator()(VariableDeclaration const& _varDecl) +{ + for (auto const& var: _varDecl.variables) + m_variableReplacements[var.name] = m_nameDispenser.newName(var.name, m_varNamePrefix); + return ASTCopier::operator()(_varDecl); +} + +Statement BodyCopier::operator()(FunctionDefinition const& _funDef) +{ + assertThrow(false, OptimizerException, "Function hoisting has to be done before function inlining."); + return _funDef; +} + +YulString BodyCopier::translateIdentifier(YulString _name) +{ + if (m_variableReplacements.count(_name)) + return m_variableReplacements.at(_name); + else + return _name; +} diff --git a/libyul/optimiser/FullInliner.h b/libyul/optimiser/FullInliner.h new file mode 100644 index 00000000..66ce8e2f --- /dev/null +++ b/libyul/optimiser/FullInliner.h @@ -0,0 +1,156 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see <http://www.gnu.org/licenses/>. +*/ +/** + * Optimiser component that performs function inlining for arbitrary functions. + */ +#pragma once + +#include <libyul/ASTDataForward.h> + +#include <libyul/optimiser/ASTCopier.h> +#include <libyul/optimiser/ASTWalker.h> +#include <libyul/optimiser/NameDispenser.h> +#include <libyul/Exceptions.h> + +#include <libevmasm/SourceLocation.h> + +#include <boost/variant.hpp> +#include <boost/optional.hpp> + +#include <set> + +namespace dev +{ +namespace yul +{ + +class NameCollector; + + +/** + * Optimiser component that modifies an AST in place, inlining functions. + * Expressions are expected to be split, i.e. the component will only inline + * function calls that are at the root of the expression and that only contains + * variables as arguments. More specifically, it will inline + * - let x1, ..., xn := f(a1, ..., am) + * - x1, ..., xn := f(a1, ..., am) + * f(a1, ..., am) + * + * The transform changes code of the form + * + * function f(a, b) -> c { ... } + * let z := f(x, y) + * + * into + * + * function f(a, b) -> c { ... } + * + * let f_a := x + * let f_b := y + * let f_c + * code of f, with replacements: a -> f_a, b -> f_b, c -> f_c + * let z := f_c + * + * Prerequisites: Disambiguator, Function Hoister + * More efficient if run after: Expression Splitter + */ +class FullInliner: public ASTModifier +{ +public: + explicit FullInliner(Block& _ast, NameDispenser& _dispenser); + + void run(); + + /// Inlining heuristic. + /// @param _callSite the name of the function in which the function call is located. + bool shallInline(FunctionCall const& _funCall, YulString _callSite); + + FunctionDefinition& function(YulString _name) { return *m_functions.at(_name); } + +private: + void updateCodeSize(FunctionDefinition& fun); + void handleBlock(YulString _currentFunctionName, Block& _block); + + /// The AST to be modified. The root block itself will not be modified, because + /// we store pointers to functions. + Block& m_ast; + std::map<YulString, FunctionDefinition*> m_functions; + /// Names of functions to always inline. + std::set<YulString> m_alwaysInline; + /// Variables that are constants (used for inlining heuristic) + std::set<YulString> m_constants; + std::map<YulString, size_t> m_functionSizes; + NameDispenser& m_nameDispenser; +}; + +/** + * Class that walks the AST of a block that does not contain function definitions and perform + * the actual code modifications. + */ +class InlineModifier: public ASTModifier +{ +public: + InlineModifier(FullInliner& _driver, NameDispenser& _nameDispenser, YulString _functionName): + m_currentFunction(std::move(_functionName)), + m_driver(_driver), + m_nameDispenser(_nameDispenser) + { } + + virtual void operator()(Block& _block) override; + +private: + boost::optional<std::vector<Statement>> tryInlineStatement(Statement& _statement); + std::vector<Statement> performInline(Statement& _statement, FunctionCall& _funCall); + + YulString m_currentFunction; + FullInliner& m_driver; + NameDispenser& m_nameDispenser; +}; + +/** + * Creates a copy of a block that is supposed to be the body of a function. + * Applies replacements to referenced variables and creates new names for + * variable declarations. + */ +class BodyCopier: public ASTCopier +{ +public: + BodyCopier( + NameDispenser& _nameDispenser, + YulString _varNamePrefix, + std::map<YulString, YulString> const& _variableReplacements + ): + m_nameDispenser(_nameDispenser), + m_varNamePrefix(_varNamePrefix), + m_variableReplacements(_variableReplacements) + {} + + using ASTCopier::operator (); + + virtual Statement operator()(VariableDeclaration const& _varDecl) override; + virtual Statement operator()(FunctionDefinition const& _funDef) override; + + virtual YulString translateIdentifier(YulString _name) override; + + NameDispenser& m_nameDispenser; + YulString m_varNamePrefix; + std::map<YulString, YulString> m_variableReplacements; +}; + + +} +} diff --git a/libyul/optimiser/FunctionGrouper.cpp b/libyul/optimiser/FunctionGrouper.cpp new file mode 100644 index 00000000..3d2e5322 --- /dev/null +++ b/libyul/optimiser/FunctionGrouper.cpp @@ -0,0 +1,47 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see <http://www.gnu.org/licenses/>. +*/ +/** + * Optimiser component that changes the code of a block so that all non-function definition + * statements are moved to a block of their own followed by all function definitions. + */ + +#include <libyul/optimiser/FunctionGrouper.h> + +#include <libsolidity/inlineasm/AsmData.h> + +#include <boost/range/algorithm_ext/erase.hpp> + +using namespace std; +using namespace dev; +using namespace dev::yul; +using namespace dev::solidity; + + +void FunctionGrouper::operator()(Block& _block) +{ + vector<Statement> reordered; + reordered.emplace_back(Block{_block.location, {}}); + + for (auto&& statement: _block.statements) + { + if (statement.type() == typeid(FunctionDefinition)) + reordered.emplace_back(std::move(statement)); + else + boost::get<Block>(reordered.front()).statements.emplace_back(std::move(statement)); + } + _block.statements = std::move(reordered); +} diff --git a/libyul/optimiser/FunctionGrouper.h b/libyul/optimiser/FunctionGrouper.h new file mode 100644 index 00000000..63cfbfb1 --- /dev/null +++ b/libyul/optimiser/FunctionGrouper.h @@ -0,0 +1,46 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see <http://www.gnu.org/licenses/>. +*/ +/** + * Optimiser component that changes the code of a black so that all non-function definition + * instructions are moved to a block of their own followed by all function definitions. + */ + +#pragma once + +#include <libyul/ASTDataForward.h> + +namespace dev +{ +namespace yul +{ + +/** + * Moves all instructions in a block into a new block at the start of the block, followed by + * all function definitions. + * + * After this step, a block is of the form + * { { I...} F... } + * Where I are (non-function-definition) instructions and F are function definitions. + */ +class FunctionGrouper +{ +public: + void operator()(Block& _block); +}; + +} +} diff --git a/libyul/optimiser/FunctionHoister.cpp b/libyul/optimiser/FunctionHoister.cpp new file mode 100644 index 00000000..c196dead --- /dev/null +++ b/libyul/optimiser/FunctionHoister.cpp @@ -0,0 +1,51 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see <http://www.gnu.org/licenses/>. +*/ +/** + * Optimiser component that changes the code so that it consists of a block starting with + * a single block followed only by function definitions and with no functions defined + * anywhere else. + */ + +#include <libyul/optimiser/FunctionHoister.h> +#include <libyul/optimiser/Utilities.h> + +#include <libsolidity/inlineasm/AsmData.h> + +#include <libdevcore/CommonData.h> + +using namespace std; +using namespace dev; +using namespace dev::yul; +using namespace dev::solidity; + +void FunctionHoister::operator()(Block& _block) +{ + bool topLevel = m_isTopLevel; + m_isTopLevel = false; + for (auto&& statement: _block.statements) + { + boost::apply_visitor(*this, statement); + if (statement.type() == typeid(FunctionDefinition)) + { + m_functions.emplace_back(std::move(statement)); + statement = Block{_block.location, {}}; + } + } + removeEmptyBlocks(_block); + if (topLevel) + _block.statements += std::move(m_functions); +} diff --git a/libyul/optimiser/FunctionHoister.h b/libyul/optimiser/FunctionHoister.h new file mode 100644 index 00000000..823b9e2b --- /dev/null +++ b/libyul/optimiser/FunctionHoister.h @@ -0,0 +1,52 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see <http://www.gnu.org/licenses/>. +*/ +/** + * Optimiser component that changes the code so that all function definitions are at the top + * level block. + */ + +#pragma once + +#include <libyul/ASTDataForward.h> + +#include <libyul/optimiser/ASTWalker.h> + +namespace dev +{ +namespace yul +{ + +/** + * Moves all functions to the top-level scope. + * Applying this transformation to source code that has ambiguous identifiers might + * lead to invalid code. + * + * Prerequisites: Disambiguator + */ +class FunctionHoister: public ASTModifier +{ +public: + using ASTModifier::operator(); + virtual void operator()(Block& _block); + +private: + bool m_isTopLevel = true; + std::vector<Statement> m_functions; +}; + +} +} diff --git a/libyul/optimiser/InlinableExpressionFunctionFinder.cpp b/libyul/optimiser/InlinableExpressionFunctionFinder.cpp new file mode 100644 index 00000000..deaaee97 --- /dev/null +++ b/libyul/optimiser/InlinableExpressionFunctionFinder.cpp @@ -0,0 +1,70 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see <http://www.gnu.org/licenses/>. +*/ +/** + * Optimiser component that identifies functions to be inlined. + */ + +#include <libyul/optimiser/InlinableExpressionFunctionFinder.h> + +#include <libyul/optimiser/Utilities.h> + +#include <libsolidity/inlineasm/AsmData.h> + +using namespace std; +using namespace dev; +using namespace dev::yul; + +void InlinableExpressionFunctionFinder::operator()(Identifier const& _identifier) +{ + checkAllowed(_identifier.name); + ASTWalker::operator()(_identifier); +} + +void InlinableExpressionFunctionFinder::operator()(FunctionCall const& _funCall) +{ + checkAllowed(_funCall.functionName.name); + ASTWalker::operator()(_funCall); +} + +void InlinableExpressionFunctionFinder::operator()(FunctionDefinition const& _function) +{ + if (_function.returnVariables.size() == 1 && _function.body.statements.size() == 1) + { + YulString retVariable = _function.returnVariables.front().name; + Statement const& bodyStatement = _function.body.statements.front(); + if (bodyStatement.type() == typeid(Assignment)) + { + Assignment const& assignment = boost::get<Assignment>(bodyStatement); + if (assignment.variableNames.size() == 1 && assignment.variableNames.front().name == retVariable) + { + // FIXME: use code size metric here + + // We cannot overwrite previous settings, because this function definition + // would not be valid here if we were searching inside a functionally inlinable + // function body. + assertThrow(m_disallowedIdentifiers.empty() && !m_foundDisallowedIdentifier, OptimizerException, ""); + m_disallowedIdentifiers = set<YulString>{retVariable, _function.name}; + boost::apply_visitor(*this, *assignment.value); + if (!m_foundDisallowedIdentifier) + m_inlinableFunctions[_function.name] = &_function; + m_disallowedIdentifiers.clear(); + m_foundDisallowedIdentifier = false; + } + } + } + ASTWalker::operator()(_function.body); +} diff --git a/libyul/optimiser/InlinableExpressionFunctionFinder.h b/libyul/optimiser/InlinableExpressionFunctionFinder.h new file mode 100644 index 00000000..baf4bbfc --- /dev/null +++ b/libyul/optimiser/InlinableExpressionFunctionFinder.h @@ -0,0 +1,69 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see <http://www.gnu.org/licenses/>. +*/ +/** + * Optimiser component that identifies functions to be inlined. + */ + +#pragma once + +#include <libyul/ASTDataForward.h> +#include <libyul/optimiser/ASTWalker.h> + +#include <set> + +namespace dev +{ +namespace yul +{ + +/** + * Optimiser component that finds functions that can be + * inlined inside functional expressions, i.e. functions that + * - have a single return parameter r + * - have a body like r := <functional expression> + * - neither reference themselves nor r in the right hand side + * + * This component can only be used on sources with unique names. + */ +class InlinableExpressionFunctionFinder: public ASTWalker +{ +public: + + std::map<YulString, FunctionDefinition const*> const& inlinableFunctions() const + { + return m_inlinableFunctions; + } + + using ASTWalker::operator(); + virtual void operator()(Identifier const& _identifier) override; + virtual void operator()(FunctionCall const& _funCall) override; + virtual void operator()(FunctionDefinition const& _function) override; + +private: + void checkAllowed(YulString _name) + { + if (m_disallowedIdentifiers.count(_name)) + m_foundDisallowedIdentifier = true; + } + + bool m_foundDisallowedIdentifier = false; + std::set<YulString> m_disallowedIdentifiers; + std::map<YulString, FunctionDefinition const*> m_inlinableFunctions; +}; + +} +} diff --git a/libyul/optimiser/MainFunction.cpp b/libyul/optimiser/MainFunction.cpp new file mode 100644 index 00000000..f3306598 --- /dev/null +++ b/libyul/optimiser/MainFunction.cpp @@ -0,0 +1,54 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see <http://www.gnu.org/licenses/>. +*/ +/** + * Changes the topmost block to be a function with a specific name ("main") which has no + * inputs nor outputs. + */ + +#include <libyul/optimiser/MainFunction.h> + +#include <libyul/optimiser/NameCollector.h> +#include <libyul/Exceptions.h> + +#include <libsolidity/inlineasm/AsmData.h> + +#include <libdevcore/CommonData.h> + +using namespace std; +using namespace dev; +using namespace dev::yul; +using namespace dev::solidity; + +void MainFunction::operator()(Block& _block) +{ + assertThrow(_block.statements.size() >= 1, OptimizerException, ""); + assertThrow(_block.statements[0].type() == typeid(Block), OptimizerException, ""); + for (size_t i = 1; i < _block.statements.size(); ++i) + assertThrow(_block.statements.at(i).type() == typeid(FunctionDefinition), OptimizerException, ""); + /// @todo this should handle scopes properly and instead of an assertion it should rename the conflicting function + assertThrow(NameCollector(_block).names().count(YulString{"main"}) == 0, OptimizerException, ""); + + Block& block = boost::get<Block>(_block.statements[0]); + FunctionDefinition main{ + block.location, + YulString{"main"}, + {}, + {}, + std::move(block) + }; + _block.statements[0] = std::move(main); +} diff --git a/libyul/optimiser/MainFunction.h b/libyul/optimiser/MainFunction.h new file mode 100644 index 00000000..4a73283a --- /dev/null +++ b/libyul/optimiser/MainFunction.h @@ -0,0 +1,41 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see <http://www.gnu.org/licenses/>. +*/ +/** + * Changes the topmost block to be a function with a specific name ("main") which has no + * inputs nor outputs. + */ + +#pragma once + +#include <libyul/ASTDataForward.h> + +namespace dev +{ +namespace yul +{ + +/** + * Prerequisites: Function Grouper + */ +class MainFunction +{ +public: + void operator()(Block& _block); +}; + +} +} diff --git a/libyul/optimiser/Metrics.cpp b/libyul/optimiser/Metrics.cpp new file mode 100644 index 00000000..066c6b58 --- /dev/null +++ b/libyul/optimiser/Metrics.cpp @@ -0,0 +1,59 @@ +/*( + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see <http://www.gnu.org/licenses/>. +*/ +/** +* Module providing metrics for the optimizer. +*/ + +#include <libyul/optimiser/Metrics.h> + +#include <libsolidity/inlineasm/AsmData.h> + +using namespace dev; +using namespace dev::yul; + +size_t CodeSize::codeSize(Statement const& _statement) +{ + CodeSize cs; + cs.visit(_statement); + return cs.m_size; +} + +size_t CodeSize::codeSize(Expression const& _expression) +{ + CodeSize cs; + cs.visit(_expression); + return cs.m_size; +} + +size_t CodeSize::codeSize(Block const& _block) +{ + CodeSize cs; + cs(_block); + return cs.m_size; +} + +void CodeSize::visit(Statement const& _statement) +{ + ++m_size; + ASTWalker::visit(_statement); +} + +void CodeSize::visit(Expression const& _expression) +{ + ++m_size; + ASTWalker::visit(_expression); +} diff --git a/libyul/optimiser/Metrics.h b/libyul/optimiser/Metrics.h new file mode 100644 index 00000000..47c7ec79 --- /dev/null +++ b/libyul/optimiser/Metrics.h @@ -0,0 +1,52 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see <http://www.gnu.org/licenses/>. +*/ +/** + * Module providing metrics for the optimizer. + */ + +#pragma once + +#include <libyul/optimiser/ASTWalker.h> + +namespace dev +{ +namespace yul +{ + +class CodeSize: public ASTWalker +{ +public: + /// Returns a metric for the code size of an AST element. + /// More specifically, it returns the number of AST nodes. + static size_t codeSize(Statement const& _statement); + /// Returns a metric for the code size of an AST element. + /// More specifically, it returns the number of AST nodes. + static size_t codeSize(Expression const& _expression); + /// Returns a metric for the code size of an AST element. + /// More specifically, it returns the number of AST nodes. + static size_t codeSize(Block const& _block); + +private: + virtual void visit(Statement const& _statement) override; + virtual void visit(Expression const& _expression) override; + +private: + size_t m_size = 0; +}; + +} +} diff --git a/libyul/optimiser/NameCollector.cpp b/libyul/optimiser/NameCollector.cpp new file mode 100644 index 00000000..36f55b99 --- /dev/null +++ b/libyul/optimiser/NameCollector.cpp @@ -0,0 +1,74 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see <http://www.gnu.org/licenses/>. +*/ +/** + * Specific AST walker that collects all defined names. + */ + +#include <libyul/optimiser/NameCollector.h> + +#include <libsolidity/inlineasm/AsmData.h> + +using namespace std; +using namespace dev; +using namespace dev::yul; + +void NameCollector::operator()(VariableDeclaration const& _varDecl) +{ + for (auto const& var: _varDecl.variables) + m_names.emplace(var.name); +} + +void NameCollector::operator ()(FunctionDefinition const& _funDef) +{ + m_names.emplace(_funDef.name); + for (auto const arg: _funDef.parameters) + m_names.emplace(arg.name); + for (auto const ret: _funDef.returnVariables) + m_names.emplace(ret.name); + ASTWalker::operator ()(_funDef); +} + +void ReferencesCounter::operator()(Identifier const& _identifier) +{ + ++m_references[_identifier.name]; +} + +void ReferencesCounter::operator()(FunctionCall const& _funCall) +{ + ++m_references[_funCall.functionName.name]; + ASTWalker::operator()(_funCall); +} + +map<YulString, size_t> ReferencesCounter::countReferences(Block const& _block) +{ + ReferencesCounter counter; + counter(_block); + return counter.references(); +} + +map<YulString, size_t> ReferencesCounter::countReferences(Expression const& _expression) +{ + ReferencesCounter counter; + counter.visit(_expression); + return counter.references(); +} + +void Assignments::operator()(Assignment const& _assignment) +{ + for (auto const& var: _assignment.variableNames) + m_names.emplace(var.name); +} diff --git a/libyul/optimiser/NameCollector.h b/libyul/optimiser/NameCollector.h new file mode 100644 index 00000000..b76eec30 --- /dev/null +++ b/libyul/optimiser/NameCollector.h @@ -0,0 +1,86 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see <http://www.gnu.org/licenses/>. +*/ +/** + * Specific AST walkers that collect facts about identifiers and definitions. + */ + +#pragma once + +#include <libyul/optimiser/ASTWalker.h> + +#include <map> +#include <set> + +namespace dev +{ +namespace yul +{ + +/** + * Specific AST walker that collects all defined names. + */ +class NameCollector: public ASTWalker +{ +public: + explicit NameCollector(Block const& _block) + { + (*this)(_block); + } + + using ASTWalker::operator (); + virtual void operator()(VariableDeclaration const& _varDecl) override; + virtual void operator()(FunctionDefinition const& _funDef) override; + + std::set<YulString> names() const { return m_names; } +private: + std::set<YulString> m_names; +}; + +/** + * Specific AST walker that counts all references to all declarations. + */ +class ReferencesCounter: public ASTWalker +{ +public: + using ASTWalker::operator (); + virtual void operator()(Identifier const& _identifier); + virtual void operator()(FunctionCall const& _funCall); + + static std::map<YulString, size_t> countReferences(Block const& _block); + static std::map<YulString, size_t> countReferences(Expression const& _expression); + + std::map<YulString, size_t> const& references() const { return m_references; } +private: + std::map<YulString, size_t> m_references; +}; + +/** + * Specific AST walker that finds all variables that are assigned to. + */ +class Assignments: public ASTWalker +{ +public: + using ASTWalker::operator (); + virtual void operator()(Assignment const& _assignment) override; + + std::set<YulString> const& names() const { return m_names; } +private: + std::set<YulString> m_names; +}; + +} +} diff --git a/libyul/optimiser/NameDispenser.cpp b/libyul/optimiser/NameDispenser.cpp new file mode 100644 index 00000000..3c870fa5 --- /dev/null +++ b/libyul/optimiser/NameDispenser.cpp @@ -0,0 +1,62 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see <http://www.gnu.org/licenses/>. +*/ +/** + * Optimiser component that can create new unique names. + */ + +#include <libyul/optimiser/NameDispenser.h> + +#include <libyul/optimiser/NameCollector.h> + +#include <libsolidity/inlineasm/AsmData.h> + +using namespace std; +using namespace dev; +using namespace dev::yul; + +NameDispenser::NameDispenser(Block const& _ast): + NameDispenser(NameCollector(_ast).names()) +{ +} + +NameDispenser::NameDispenser(set<YulString> _usedNames): + m_usedNames(std::move(_usedNames)) +{ +} + +YulString NameDispenser::newName(YulString _nameHint, YulString _context) +{ + // Shortening rules: Use a suffix of _prefix and a prefix of _context. + YulString prefix = _nameHint; + + if (!_context.empty()) + prefix = YulString{_context.str().substr(0, 10) + "_" + prefix.str()}; + + return newNameInternal(prefix); +} + +YulString NameDispenser::newNameInternal(YulString _nameHint) +{ + YulString name = _nameHint; + while (name.empty() || m_usedNames.count(name)) + { + m_counter++; + name = YulString(_nameHint.str() + "_" + to_string(m_counter)); + } + m_usedNames.emplace(name); + return name; +} diff --git a/libyul/optimiser/NameDispenser.h b/libyul/optimiser/NameDispenser.h new file mode 100644 index 00000000..7311440b --- /dev/null +++ b/libyul/optimiser/NameDispenser.h @@ -0,0 +1,61 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see <http://www.gnu.org/licenses/>. +*/ +/** + * Optimiser component that can create new unique names. + */ +#pragma once + +#include <libyul/ASTDataForward.h> + +#include <libyul/YulString.h> + +#include <set> + +namespace dev +{ +namespace yul +{ + +/** + * Optimizer component that can be used to generate new names that + * do not conflict with existing names. + * + * Tries to keep names short and appends decimals to disambiguate. + */ +class NameDispenser +{ +public: + /// Initialize the name dispenser with all the names used in the given AST. + explicit NameDispenser(Block const& _ast); + /// Initialize the name dispenser with the given used names. + explicit NameDispenser(std::set<YulString> _usedNames); + + /// @returns a currently unused name that should be similar to _nameHint + /// and prefixed by _context if present. + /// If the resulting name would be too long, trims the context at the end + /// and the name hint at the start. + YulString newName(YulString _nameHint, YulString _context = {}); + +private: + YulString newNameInternal(YulString _nameHint); + + std::set<YulString> m_usedNames; + size_t m_counter = 0; +}; + +} +} diff --git a/libyul/optimiser/README.md b/libyul/optimiser/README.md new file mode 100644 index 00000000..c2575179 --- /dev/null +++ b/libyul/optimiser/README.md @@ -0,0 +1,159 @@ +Note that the Yul optimiser is still in research phase. Because of that, +the following description might not fully reflect the current or even +planned state of the optimiser. + +## Yul Optimiser + +The Yul optimiser consists of several stages and components that all transform +the AST in a semantically equivalent way. The goal is to end up either with code +that is shorter or at least only marginally longer but will allow further +optimisation steps. + +The optimiser currently follows a purely greedy strategy and does not do any +backtracking. + +## Disambiguator + +The disambiguator takes an AST and returns a fresh copy where all identifiers have +names unique to the input AST. This is a prerequisite for all other optimiser stages. +One of the benefits is that identifier lookup does not need to take scopes into account +and we can basically ignore the result of the analysis phase. + +All subsequent stages have the property that all names stay unique. This means if +a new identifier needs to be introduced, a new unique name is generated. + +## Function Hoister + +The function hoister moves all function definitions to the end of the topmost block. This is +a semantically equivalent transformation as long as it is performed after the +disambiguation stage. The reason is that moving a definition to a higher-level block cannot decrease +its visibility and it is impossible to reference variables defined in a different function. + +The benefit of this stage is that function definitions can be looked up more easily. + +## Function Grouper + +The function grouper has to be applied after the disambiguator and the function hoister. +Its effect is that all topmost elements that are not function definitions are moved +into a single block which is the first statement of the root block. + +After this step, a program has the following normal form: + + { I F... } + +Where I is a block that does not contain any function definitions (not even recursively) +and F is a list of function definitions such that no function contains a function definition. + +## Functional Inliner + +The functional inliner depends on the disambiguator, the function hoister and function grouper. +It performs function inlining such that the result of the inlining is an expression. This can +only be done if the body of the function to be inlined has the form ``{ r := E }`` where ``r`` +is the single return value of the function, ``E`` is an expression and all arguments in the +function call are so-called movable expressions. A movable expression is either a literal, a +variable or a function call (or EVM opcode) which does not have side-effects and also does not +depend on any side-effects. + +As an example, neither ``mload`` nor ``mstore`` would be allowed. + +## Expression Splitter + +The expression splitter turns expressions like ``add(mload(x), mul(mload(y), 0x20))`` +into a sequence of declarations of unique variables that are assigned sub-expressions +of that expression so that each function call has only variables or literals +as arguments. + +The above would be transformed into + + { + let _1 := mload(y) + let _2 := mul(_1, 0x20) + let _3 := mload(x) + let z := add(_3, _2) + } + +Note that this transformation does not change the order of opcodes or function calls. + +It is not applied to loop conditions, because the loop control flow does not allow +this "outlining" of the inner expressions in all cases. + +The final program should be in a form such that with the exception of loop conditions, +function calls can only appear in the right-hand side of a variable declaration, +assignments or expression statements and all arguments have to be constants or variables. + +The benefits of this form are that it is much easier to re-order the sequence of opcodes +and it is also easier to perform function call inlining. The drawback is that +such code is much harder to read for humans. + +## Expression Joiner + +This is the opposite operation of the expression splitter. It turns a sequence of +variable declarations that have exactly one reference into a complex expression. +This stage again fully preserves the order of function calls and opcode executions. +It does not make use of any information concerning the commutability of opcodes; +if moving the value of a variable to its place of use would change the order +of any function call or opcode execution, the transformation is not performed. + +Note that the component will not move the assigned value of a variable assignment +or a variable that is referenced more than once. + +## Common Subexpression Eliminator + +This step replaces a subexpression by the value of a pre-existing variable +that currently has the same value (only if the value is movable), based +on a syntactic comparison. + +This can be used to compute a local value numbering, especially if the +expression splitter is used before. + +The expression simplifier will be able to perform better replacements +if the common subexpression eliminator was run right before it. + +Prerequisites: Disambiguator + +## Full Function Inliner + +## Rematerialisation + +The rematerialisation stage tries to replace variable references by the expression that +was last assigned to the variable. This is of course only beneficial if this expression +is comparatively cheap to evaluate. Furthermore, it is only semantically equivalent if +the value of the expression did not change between the point of assignment and the +point of use. The main benefit of this stage is that it can save stack slots if it +leads to a variable being eliminated completely (see below), but it can also +save a DUP opcode on the EVM if the expression is very cheap. + +The algorithm only allows movable expressions (see above for a definition) in this case. +Expressions that contain other variables are also disallowed if one of those variables +have been assigned to in the meantime. This is also not applied to variables where +assignment and use span across loops and conditionals. + +## Unused Definition Pruner + +If a variable or function is not referenced, it is removed from the code. +If there are two assignments to a variable where the first one is a movable expression +and the variable is not used between the two assignments (and the second is not inside +a loop or conditional, the first one is not inside), the first assignment is removed. + +This step also removes movable expression statements. + + +## Function Unifier + +## Expression Simplifier + +This step can only be applied for the EVM-flavoured dialect of Yul. It applies +simple rules like ``x + 0 == x`` to simplify expressions. + +## Ineffective Statement Remover + +This step removes statements that have no side-effects. + +## WebAssembly specific + +### Main Function + +Changes the topmost block to be a function with a specific name ("main") which has no +inputs nor outputs. + +Depends on the Function Grouper. diff --git a/libyul/optimiser/RedundantAssignEliminator.cpp b/libyul/optimiser/RedundantAssignEliminator.cpp new file mode 100644 index 00000000..b7217074 --- /dev/null +++ b/libyul/optimiser/RedundantAssignEliminator.cpp @@ -0,0 +1,230 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see <http://www.gnu.org/licenses/>. +*/ +/** + * Optimiser component that removes assignments to variables that are not used + * until they go out of scope or are re-assigned. + */ + +#include <libyul/optimiser/RedundantAssignEliminator.h> + +#include <libyul/optimiser/Semantics.h> + +#include <libsolidity/inlineasm/AsmData.h> + +#include <libdevcore/CommonData.h> + +#include <boost/range/algorithm_ext/erase.hpp> + +using namespace std; +using namespace dev; +using namespace dev::yul; +using namespace dev::solidity; + +void RedundantAssignEliminator::operator()(Identifier const& _identifier) +{ + changeUndecidedTo(_identifier.name, State::Used); +} + +void RedundantAssignEliminator::operator()(VariableDeclaration const& _variableDeclaration) +{ + ASTWalker::operator()(_variableDeclaration); + + for (auto const& var: _variableDeclaration.variables) + m_declaredVariables.emplace(var.name); +} + +void RedundantAssignEliminator::operator()(Assignment const& _assignment) +{ + visit(*_assignment.value); + for (auto const& var: _assignment.variableNames) + changeUndecidedTo(var.name, State::Unused); + + if (_assignment.variableNames.size() == 1) + // Default-construct it in "Undecided" state if it does not yet exist. + m_assignments[_assignment.variableNames.front().name][&_assignment]; +} + +void RedundantAssignEliminator::operator()(If const& _if) +{ + visit(*_if.condition); + + RedundantAssignEliminator branch{*this}; + branch(_if.body); + + join(branch); +} + +void RedundantAssignEliminator::operator()(Switch const& _switch) +{ + visit(*_switch.expression); + + bool hasDefault = false; + vector<RedundantAssignEliminator> branches; + for (auto const& c: _switch.cases) + { + if (!c.value) + hasDefault = true; + branches.emplace_back(*this); + branches.back()(c.body); + } + + if (hasDefault) + { + *this = std::move(branches.back()); + branches.pop_back(); + } + for (auto& branch: branches) + join(branch); +} + +void RedundantAssignEliminator::operator()(FunctionDefinition const& _functionDefinition) +{ + (*this)(_functionDefinition.body); + + for (auto const& param: _functionDefinition.parameters) + { + changeUndecidedTo(param.name, State::Unused); + finalize(param.name); + } + for (auto const& retParam: _functionDefinition.returnVariables) + { + changeUndecidedTo(retParam.name, State::Used); + finalize(retParam.name); + } +} + +void RedundantAssignEliminator::operator()(ForLoop const& _forLoop) +{ + // This will set all variables that are declared in this + // block to "unused" when it is destroyed. + BlockScope scope(*this); + + // We need to visit the statements directly because of the + // scoping rules. + walkVector(_forLoop.pre.statements); + + // We just run the loop twice to account for the + // back edge. + // There need not be more runs because we only have three different states. + + visit(*_forLoop.condition); + + RedundantAssignEliminator zeroRuns{*this}; + + (*this)(_forLoop.body); + (*this)(_forLoop.post); + + visit(*_forLoop.condition); + + RedundantAssignEliminator oneRun{*this}; + + (*this)(_forLoop.body); + (*this)(_forLoop.post); + + visit(*_forLoop.condition); + + // Order does not matter because "max" is commutative and associative. + join(oneRun); + join(zeroRuns); +} + +void RedundantAssignEliminator::operator()(Block const& _block) +{ + // This will set all variables that are declared in this + // block to "unused" when it is destroyed. + BlockScope scope(*this); + + ASTWalker::operator()(_block); +} + +void RedundantAssignEliminator::run(Block& _ast) +{ + RedundantAssignEliminator rae; + rae(_ast); + + AssignmentRemover remover{rae.m_assignmentsToRemove}; + remover(_ast); +} + +template <class K, class V, class F> +void joinMap(std::map<K, V>& _a, std::map<K, V>&& _b, F _conflictSolver) +{ + // TODO Perhaps it is better to just create a sorted list + // and then use insert(begin, end) + + auto ita = _a.begin(); + auto aend = _a.end(); + auto itb = _b.begin(); + auto bend = _b.end(); + + for (; itb != bend; ++ita) + { + if (ita == aend) + ita = _a.insert(ita, std::move(*itb++)); + else if (ita->first < itb->first) + continue; + else if (itb->first < ita->first) + ita = _a.insert(ita, std::move(*itb++)); + else + { + _conflictSolver(ita->second, std::move(itb->second)); + ++itb; + } + } +} + +void RedundantAssignEliminator::join(RedundantAssignEliminator& _other) +{ + m_assignmentsToRemove.insert(begin(_other.m_assignmentsToRemove), end(_other.m_assignmentsToRemove)); + + joinMap(m_assignments, std::move(_other.m_assignments), []( + map<Assignment const*, State>& _assignmentHere, + map<Assignment const*, State>&& _assignmentThere + ) + { + return joinMap(_assignmentHere, std::move(_assignmentThere), State::join); + }); +} + +void RedundantAssignEliminator::changeUndecidedTo(YulString _variable, RedundantAssignEliminator::State _newState) +{ + for (auto& assignment: m_assignments[_variable]) + if (assignment.second == State{State::Undecided}) + assignment.second = _newState; +} + +void RedundantAssignEliminator::finalize(YulString _variable) +{ + for (auto& assignment: m_assignments[_variable]) + { + assertThrow(assignment.second != State::Undecided, OptimizerException, ""); + if (assignment.second == State{State::Unused} && MovableChecker{*assignment.first->value}.movable()) + // TODO the only point where we actually need this + // to be a set is for the for loop + m_assignmentsToRemove.insert(assignment.first); + } + m_assignments.erase(_variable); +} + +void AssignmentRemover::operator()(Block& _block) +{ + boost::range::remove_erase_if(_block.statements, [=](Statement const& _statement) -> bool { + return _statement.type() == typeid(Assignment) && m_toRemove.count(&boost::get<Assignment>(_statement)); + }); + + ASTModifier::operator()(_block); +} diff --git a/libyul/optimiser/RedundantAssignEliminator.h b/libyul/optimiser/RedundantAssignEliminator.h new file mode 100644 index 00000000..76106aae --- /dev/null +++ b/libyul/optimiser/RedundantAssignEliminator.h @@ -0,0 +1,193 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see <http://www.gnu.org/licenses/>. +*/ +/** + * Optimiser component that removes assignments to variables that are not used + * until they go out of scope or are re-assigned. + */ + +#pragma once + +#include <libyul/ASTDataForward.h> + +#include <libyul/optimiser/ASTWalker.h> + +#include <map> + +namespace dev +{ +namespace yul +{ + +/** + * Optimiser component that removes assignments to variables that are not used + * until they go out of scope or are re-assigned. This component + * respects the control-flow and takes it into account for removal. + * + * Example: + * + * { + * let a + * a := 1 + * a := 2 + * b := 2 + * if calldataload(0) + * { + * b := mload(a) + * } + * a := b + * } + * + * In the example, "a := 1" can be removed because the value from this assignment + * is not used in any control-flow branch (it is replaced right away). + * The assignment "a := 2" is also overwritten by "a := b" at the end, + * but there is a control-flow path (through the condition body) which uses + * the value from "a := 2" and thus, this assignment cannot be removed. + * + * Detailed rules: + * + * The AST is traversed twice: in an information gathering step and in the + * actual removal step. During information gathering, we maintain a + * mapping from assignment statements to the three states + * "unused", "undecided" and "used". + * When an assignment is visited, it is added to the mapping in the "undecided" state + * (see remark about for loops below) and every other assignment to the same variable + * that is still in the "undecided" state is changed to "unused". + * When a variable is referenced, the state of any assignment to that variable still + * in the "undecided" state is changed to "used". + * At points where control flow splits, a copy + * of the mapping is handed over to each branch. At points where control flow + * joins, the two mappings coming from the two branches are combined in the following way: + * Statements that are only in one mapping or have the same state are used unchanged. + * Conflicting values are resolved in the following way: + * "unused", "undecided" -> "undecided" + * "unused", "used" -> "used" + * "undecided, "used" -> "used". + * + * For for-loops, the condition, body and post-part are visited twice, taking + * the joining control-flow at the condition into account. + * In other words, we create three control flow paths: Zero runs of the loop, + * one run and two runs and then combine them at the end. + * Running at most twice is enough because there are only three different states. + * + * For switch statements that have a "default"-case, there is no control-flow + * part that skips the switch. + * + * When a variable goes out of scope, all statements still in the "undecided" + * state are changed to "unused", unless the variable is the return + * parameter of a function - there, the state changes to "used". + * + * In the second traversal, all assignments that are in the "unused" state are removed. + * + * + * This step is usually run right after the SSA transform to complete + * the generation of the pseudo-SSA. + * + * Prerequisite: Disambiguator. + */ +class RedundantAssignEliminator: public ASTWalker +{ +public: + RedundantAssignEliminator(RedundantAssignEliminator const&) = default; + RedundantAssignEliminator& operator=(RedundantAssignEliminator const&) = default; + RedundantAssignEliminator(RedundantAssignEliminator&&) = default; + RedundantAssignEliminator& operator=(RedundantAssignEliminator&&) = default; + + void operator()(Identifier const& _identifier) override; + void operator()(VariableDeclaration const& _variableDeclaration) override; + void operator()(Assignment const& _assignment) override; + void operator()(If const& _if) override; + void operator()(Switch const& _switch) override; + void operator()(FunctionDefinition const&) override; + void operator()(ForLoop const&) override; + void operator()(Block const& _block) override; + + static void run(Block& _ast); + +private: + RedundantAssignEliminator() {} + + class State + { + public: + enum Value { Unused, Undecided, Used }; + State(Value _value = Undecided): m_value(_value) {} + inline bool operator==(State _other) const { return m_value == _other.m_value; } + inline bool operator!=(State _other) const { return !operator==(_other); } + static inline void join(State& _a, State const& _b) + { + // Using "max" works here because of the order of the values in the enum. + _a.m_value = Value(std::max(int(_a.m_value), int(_b.m_value))); + } + private: + Value m_value = Undecided; + }; + + /** + * Takes care about storing the list of declared variables and + * sets them to "unused" when it is destroyed. + */ + class BlockScope + { + public: + explicit BlockScope(RedundantAssignEliminator& _rae): m_rae(_rae) + { + swap(m_rae.m_declaredVariables, m_outerDeclaredVariables); + } + ~BlockScope() + { + // This should actually store all declared variables + // into a different mapping + for (auto const& var: m_rae.m_declaredVariables) + m_rae.changeUndecidedTo(var, State::Unused); + for (auto const& var: m_rae.m_declaredVariables) + m_rae.finalize(var); + swap(m_rae.m_declaredVariables, m_outerDeclaredVariables); + } + + private: + RedundantAssignEliminator& m_rae; + std::set<YulString> m_outerDeclaredVariables; + }; + + /// Joins the assignment mapping with @a _other according to the rules laid out + /// above. + /// Will destroy @a _other. + void join(RedundantAssignEliminator& _other); + void changeUndecidedTo(YulString _variable, State _newState); + void finalize(YulString _variable); + + std::set<YulString> m_declaredVariables; + // TODO check that this does not cause nondeterminism! + // This could also be a pseudo-map from state to assignment. + std::map<YulString, std::map<Assignment const*, State>> m_assignments; + std::set<Assignment const*> m_assignmentsToRemove; +}; + +class AssignmentRemover: public ASTModifier +{ +public: + explicit AssignmentRemover(std::set<Assignment const*> const& _toRemove): + m_toRemove(_toRemove) + {} + void operator()(Block& _block) override; + +private: + std::set<Assignment const*> const& m_toRemove; +}; + +} +} diff --git a/libyul/optimiser/Rematerialiser.cpp b/libyul/optimiser/Rematerialiser.cpp new file mode 100644 index 00000000..38d50ef4 --- /dev/null +++ b/libyul/optimiser/Rematerialiser.cpp @@ -0,0 +1,50 @@ +/*( + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see <http://www.gnu.org/licenses/>. +*/ +/** + * Optimisation stage that replaces variables by their most recently assigned expressions. + */ + +#include <libyul/optimiser/Rematerialiser.h> + +#include <libyul/optimiser/Metrics.h> +#include <libyul/optimiser/ASTCopier.h> +#include <libyul/Exceptions.h> + +#include <libsolidity/inlineasm/AsmData.h> + +using namespace std; +using namespace dev; +using namespace dev::yul; + +void Rematerialiser::visit(Expression& _e) +{ + if (_e.type() == typeid(Identifier)) + { + Identifier& identifier = boost::get<Identifier>(_e); + if (m_value.count(identifier.name)) + { + YulString name = identifier.name; + for (auto const& ref: m_references[name]) + assertThrow(inScope(ref), OptimizerException, ""); + assertThrow(m_value.at(name), OptimizerException, ""); + auto const& value = *m_value.at(name); + if (CodeSize::codeSize(value) <= 7) + _e = (ASTCopier{}).translate(value); + } + } + DataFlowAnalyzer::visit(_e); +} diff --git a/libyul/optimiser/Rematerialiser.h b/libyul/optimiser/Rematerialiser.h new file mode 100644 index 00000000..f82465eb --- /dev/null +++ b/libyul/optimiser/Rematerialiser.h @@ -0,0 +1,44 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see <http://www.gnu.org/licenses/>. +*/ +/** + * Optimisation stage that replaces variables by their most recently assigned expressions. + */ + +#pragma once + +#include <libyul/optimiser/DataFlowAnalyzer.h> + +namespace dev +{ +namespace yul +{ + +/** + * Optimisation stage that replaces variables by their most recently assigned expressions. + * + * Prerequisite: Disambiguator + */ +class Rematerialiser: public DataFlowAnalyzer +{ +protected: + using ASTModifier::visit; + virtual void visit(Expression& _e) override; + +}; + +} +} diff --git a/libyul/optimiser/SSATransform.cpp b/libyul/optimiser/SSATransform.cpp new file mode 100644 index 00000000..f209ee7b --- /dev/null +++ b/libyul/optimiser/SSATransform.cpp @@ -0,0 +1,130 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see <http://www.gnu.org/licenses/>. +*/ +/** + * Optimiser component that turns subsequent assignments to variable declarations + * and assignments. + */ + +#include <libyul/optimiser/SSATransform.h> + +#include <libyul/optimiser/NameCollector.h> +#include <libyul/optimiser/NameDispenser.h> + +#include <libsolidity/inlineasm/AsmData.h> + +#include <libdevcore/CommonData.h> + + +using namespace std; +using namespace dev; +using namespace dev::yul; +using namespace dev::solidity; + +void SSATransform::operator()(Identifier& _identifier) +{ + if (m_currentVariableValues.count(_identifier.name)) + _identifier.name = m_currentVariableValues[_identifier.name]; +} + +void SSATransform::operator()(ForLoop& _for) +{ + // This will clear the current value in case of a reassignment inside the + // init part, although the new variable would still be in scope inside the whole loop. + // This small inefficiency is fine if we move the pre part of all for loops out + // of the for loop. + (*this)(_for.pre); + + Assignments assignments; + assignments(_for.body); + assignments(_for.post); + for (auto const& var: assignments.names()) + m_currentVariableValues.erase(var); + + visit(*_for.condition); + (*this)(_for.body); + (*this)(_for.post); +} + + +void SSATransform::operator()(Block& _block) +{ + set<YulString> variablesToClearAtEnd; + + // Creates a new variable (and returns its declaration) with value _value + // and replaces _value by a reference to that new variable. + auto replaceByNew = [&](SourceLocation _loc, YulString _varName, YulString _type, shared_ptr<Expression>& _value) -> VariableDeclaration + { + YulString newName = m_nameDispenser.newName(_varName); + m_currentVariableValues[_varName] = newName; + variablesToClearAtEnd.emplace(_varName); + shared_ptr<Expression> v = make_shared<Expression>(Identifier{_loc, newName}); + _value.swap(v); + return VariableDeclaration{_loc, {TypedName{_loc, std::move(newName), std::move(_type)}}, std::move(v)}; + }; + + iterateReplacing( + _block.statements, + [&](Statement& _s) -> boost::optional<vector<Statement>> + { + if (_s.type() == typeid(VariableDeclaration)) + { + VariableDeclaration& varDecl = boost::get<VariableDeclaration>(_s); + if (varDecl.value) + visit(*varDecl.value); + if (varDecl.variables.size() != 1 || !m_variablesToReplace.count(varDecl.variables.front().name)) + return {}; + // Replace "let a := v" by "let a_1 := v let a := v" + VariableDeclaration newVarDecl = replaceByNew( + varDecl.location, + varDecl.variables.front().name, + varDecl.variables.front().type, + varDecl.value + ); + return vector<Statement>{std::move(newVarDecl), std::move(varDecl)}; + } + else if (_s.type() == typeid(Assignment)) + { + Assignment& assignment = boost::get<Assignment>(_s); + visit(*assignment.value); + if (assignment.variableNames.size() != 1) + return {}; + assertThrow(m_variablesToReplace.count(assignment.variableNames.front().name), OptimizerException, ""); + // Replace "a := v" by "let a_1 := v a := v" + VariableDeclaration newVarDecl = replaceByNew( + assignment.location, + assignment.variableNames.front().name, + {}, // TODO determine type + assignment.value + ); + return vector<Statement>{std::move(newVarDecl), std::move(assignment)}; + } + else + visit(_s); + return {}; + } + ); + for (auto const& var: variablesToClearAtEnd) + m_currentVariableValues.erase(var); +} + +void SSATransform::run(Block& _ast, NameDispenser& _nameDispenser) +{ + Assignments assignments; + assignments(_ast); + SSATransform{_nameDispenser, assignments.names()}(_ast); +} + diff --git a/libyul/optimiser/SSATransform.h b/libyul/optimiser/SSATransform.h new file mode 100644 index 00000000..bb642549 --- /dev/null +++ b/libyul/optimiser/SSATransform.h @@ -0,0 +1,98 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see <http://www.gnu.org/licenses/>. +*/ +/** + * Optimiser component that turns subsequent assignments to variable declarations + * and assignments. + */ +#pragma once + +#include <libyul/ASTDataForward.h> + +#include <libyul/optimiser/ASTWalker.h> + +#include <vector> + +namespace dev +{ +namespace yul +{ + +class NameDispenser; + +/** + * Optimizer stage that tries to replace repeated assignments to + * existing variables by declarations of new variables as much as + * possible. + * The reassignments are still there, but all references to the + * reassigned variables are replaced by the newly declared variables. + * + * Example: + * { + * let a := 1 + * mstore(a, 2) + * a := 3 + * } + * is transformed to + * { + * let a_1 := 1 + * let a := a_1 + * mstore(a_1, 2) + * let a_3 := 3 + * a := a_3 + * } + * + * Exact semantics: + * + * For any variable a that is assigned to somewhere in the code (assignment with + * declaration does not count) perform the following transforms: + * - replace "let a := v" by "let a_1 := v let a := a_1" + * - replace "a := v" by "let a_1 := v a := a_1" + * Furthermore, always note the current variable/value assigned to a and replace each + * reference to a by this variable. + * The current value mapping is cleared for a variable a at the end of each block + * in which it was assigned and just after the for loop init block if it is assigned + * inside the for loop. + * + * After this stage, redundantAssignmentRemover is recommended to remove the unnecessary + * intermediate assignments. + * + * This stage provides best results if CSE is run right before it, because + * then it does not generate excessive amounts of variables. + * + * TODO Which transforms are required to keep this idempotent? + */ +class SSATransform: public ASTModifier +{ +public: + void operator()(Identifier&) override; + void operator()(ForLoop&) override; + void operator()(Block& _block) override; + + static void run(Block& _ast, NameDispenser& _nameDispenser); + +private: + explicit SSATransform(NameDispenser& _nameDispenser, std::set<YulString> const& _variablesToReplace): + m_nameDispenser(_nameDispenser), m_variablesToReplace(_variablesToReplace) + { } + + NameDispenser& m_nameDispenser; + std::set<YulString> const& m_variablesToReplace; + std::map<YulString, YulString> m_currentVariableValues; +}; + +} +} diff --git a/libyul/optimiser/SSAValueTracker.cpp b/libyul/optimiser/SSAValueTracker.cpp new file mode 100644 index 00000000..491117da --- /dev/null +++ b/libyul/optimiser/SSAValueTracker.cpp @@ -0,0 +1,53 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see <http://www.gnu.org/licenses/>. +*/ +/** + * Component that collects variables that are never assigned to and their + * initial values. + */ + +#include <libyul/optimiser/SSAValueTracker.h> + +#include <libsolidity/inlineasm/AsmData.h> + +using namespace std; +using namespace dev; +using namespace dev::yul; + +void SSAValueTracker::operator()(Assignment const& _assignment) +{ + for (auto const& var: _assignment.variableNames) + m_values.erase(var.name); +} + +void SSAValueTracker::operator()(VariableDeclaration const& _varDecl) +{ + if (_varDecl.variables.size() == 1) + setValue(_varDecl.variables.front().name, _varDecl.value.get()); + else + for (auto const& var: _varDecl.variables) + setValue(var.name, nullptr); +} + +void SSAValueTracker::setValue(YulString _name, Expression const* _value) +{ + assertThrow( + m_values.count(_name) == 0, + OptimizerException, + "Source needs to be disambiguated." + ); + m_values[_name] = _value; +} diff --git a/libyul/optimiser/SSAValueTracker.h b/libyul/optimiser/SSAValueTracker.h new file mode 100644 index 00000000..d1539c86 --- /dev/null +++ b/libyul/optimiser/SSAValueTracker.h @@ -0,0 +1,57 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see <http://www.gnu.org/licenses/>. +*/ +/** + * Component that collects variables that are never assigned to and their + * initial values. + */ + +#pragma once + +#include <libyul/optimiser/ASTWalker.h> + +#include <map> +#include <set> + +namespace dev +{ +namespace yul +{ + +/** + * Class that walks the AST and stores the initial value of each variable + * that is never assigned to. + * + * Prerequisite: Disambiguator + */ +class SSAValueTracker: public ASTWalker +{ +public: + using ASTWalker::operator(); + virtual void operator()(VariableDeclaration const& _varDecl) override; + virtual void operator()(Assignment const& _assignment) override; + + std::map<YulString, Expression const*> const& values() const { return m_values; } + Expression const* value(YulString _name) const { return m_values.at(_name); } + +private: + void setValue(YulString _name, Expression const* _value); + + std::map<YulString, Expression const*> m_values; +}; + +} +} diff --git a/libyul/optimiser/Semantics.cpp b/libyul/optimiser/Semantics.cpp new file mode 100644 index 00000000..3c49016e --- /dev/null +++ b/libyul/optimiser/Semantics.cpp @@ -0,0 +1,62 @@ +/*( + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see <http://www.gnu.org/licenses/>. +*/ +/** + * Specific AST walkers that collect semantical facts. + */ + +#include <libyul/optimiser/Semantics.h> + +#include <libyul/Exceptions.h> + +#include <libsolidity/inlineasm/AsmData.h> + +#include <libevmasm/SemanticInformation.h> + +#include <libdevcore/CommonData.h> + +using namespace std; +using namespace dev; +using namespace dev::yul; + +MovableChecker::MovableChecker(Expression const& _expression) +{ + visit(_expression); +} + +void MovableChecker::operator()(Identifier const& _identifier) +{ + ASTWalker::operator()(_identifier); + m_variableReferences.emplace(_identifier.name); +} + +void MovableChecker::operator()(FunctionalInstruction const& _instr) +{ + if (!eth::SemanticInformation::movable(_instr.instruction)) + m_movable = false; + else + ASTWalker::operator()(_instr); +} + +void MovableChecker::operator()(FunctionCall const&) +{ + m_movable = false; +} + +void MovableChecker::visit(Statement const&) +{ + assertThrow(false, OptimizerException, "Movability for statement requested."); +} diff --git a/libyul/optimiser/Semantics.h b/libyul/optimiser/Semantics.h new file mode 100644 index 00000000..620a91cb --- /dev/null +++ b/libyul/optimiser/Semantics.h @@ -0,0 +1,60 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see <http://www.gnu.org/licenses/>. +*/ +/** + * Specific AST walkers that collect semantical facts. + */ + +#pragma once + +#include <libyul/optimiser/ASTWalker.h> + +#include <set> + +namespace dev +{ +namespace yul +{ + +/** + * Specific AST walker that determines whether an expression is movable. + */ +class MovableChecker: public ASTWalker +{ +public: + MovableChecker() = default; + explicit MovableChecker(Expression const& _expression); + + virtual void operator()(Identifier const& _identifier) override; + virtual void operator()(FunctionalInstruction const& _functionalInstruction) override; + virtual void operator()(FunctionCall const& _functionCall) override; + + /// Disallow visiting anything apart from Expressions (this throws). + virtual void visit(Statement const&) override; + using ASTWalker::visit; + + bool movable() const { return m_movable; } + std::set<YulString> const& referencedVariables() const { return m_variableReferences; } + +private: + /// Which variables the current expression references. + std::set<YulString> m_variableReferences; + /// Is the current expression movable or not. + bool m_movable = true; +}; + +} +} diff --git a/libyul/optimiser/SimplificationRules.cpp b/libyul/optimiser/SimplificationRules.cpp new file mode 100644 index 00000000..5721042f --- /dev/null +++ b/libyul/optimiser/SimplificationRules.cpp @@ -0,0 +1,222 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see <http://www.gnu.org/licenses/>. +*/ +/** + * Module for applying replacement rules against Expressions. + */ + +#include <libyul/optimiser/SimplificationRules.h> + +#include <libyul/optimiser/Utilities.h> +#include <libyul/optimiser/ASTCopier.h> +#include <libyul/optimiser/Semantics.h> +#include <libyul/optimiser/SyntacticalEquality.h> + +#include <libsolidity/inlineasm/AsmData.h> + +#include <libevmasm/RuleList.h> + +using namespace std; +using namespace dev; +using namespace dev::yul; + + +SimplificationRule<Pattern> const* SimplificationRules::findFirstMatch( + Expression const& _expr, + map<YulString, Expression const*> const& _ssaValues +) +{ + if (_expr.type() != typeid(FunctionalInstruction)) + return nullptr; + + static SimplificationRules rules; + assertThrow(rules.isInitialized(), OptimizerException, "Rule list not properly initialized."); + + FunctionalInstruction const& instruction = boost::get<FunctionalInstruction>(_expr); + for (auto const& rule: rules.m_rules[uint8_t(instruction.instruction)]) + { + rules.resetMatchGroups(); + if (rule.pattern.matches(_expr, _ssaValues)) + return &rule; + } + return nullptr; +} + +bool SimplificationRules::isInitialized() const +{ + return !m_rules[uint8_t(solidity::Instruction::ADD)].empty(); +} + +void SimplificationRules::addRules(vector<SimplificationRule<Pattern>> const& _rules) +{ + for (auto const& r: _rules) + addRule(r); +} + +void SimplificationRules::addRule(SimplificationRule<Pattern> const& _rule) +{ + m_rules[uint8_t(_rule.pattern.instruction())].push_back(_rule); +} + +SimplificationRules::SimplificationRules() +{ + // Multiple occurrences of one of these inside one rule must match the same equivalence class. + // Constants. + Pattern A(PatternKind::Constant); + Pattern B(PatternKind::Constant); + Pattern C(PatternKind::Constant); + // Anything. + Pattern X; + Pattern Y; + A.setMatchGroup(1, m_matchGroups); + B.setMatchGroup(2, m_matchGroups); + C.setMatchGroup(3, m_matchGroups); + X.setMatchGroup(4, m_matchGroups); + Y.setMatchGroup(5, m_matchGroups); + + addRules(simplificationRuleList(A, B, C, X, Y)); + assertThrow(isInitialized(), OptimizerException, "Rule list not properly initialized."); +} + +Pattern::Pattern(solidity::Instruction _instruction, vector<Pattern> const& _arguments): + m_kind(PatternKind::Operation), + m_instruction(_instruction), + m_arguments(_arguments) +{ +} + +void Pattern::setMatchGroup(unsigned _group, map<unsigned, Expression const*>& _matchGroups) +{ + m_matchGroup = _group; + m_matchGroups = &_matchGroups; +} + +bool Pattern::matches(Expression const& _expr, map<YulString, Expression const*> const& _ssaValues) const +{ + Expression const* expr = &_expr; + + // Resolve the variable if possible. + // Do not do it for "Any" because we can check identity better for variables. + if (m_kind != PatternKind::Any && _expr.type() == typeid(Identifier)) + { + YulString varName = boost::get<Identifier>(_expr).name; + if (_ssaValues.count(varName)) + expr = _ssaValues.at(varName); + } + assertThrow(expr, OptimizerException, ""); + + if (m_kind == PatternKind::Constant) + { + if (expr->type() != typeid(Literal)) + return false; + Literal const& literal = boost::get<Literal>(*expr); + if (literal.kind != assembly::LiteralKind::Number) + return false; + if (m_data && *m_data != u256(literal.value.str())) + return false; + assertThrow(m_arguments.empty(), OptimizerException, ""); + } + else if (m_kind == PatternKind::Operation) + { + if (expr->type() != typeid(FunctionalInstruction)) + return false; + FunctionalInstruction const& instr = boost::get<FunctionalInstruction>(*expr); + if (m_instruction != instr.instruction) + return false; + assertThrow(m_arguments.size() == instr.arguments.size(), OptimizerException, ""); + for (size_t i = 0; i < m_arguments.size(); ++i) + if (!m_arguments[i].matches(instr.arguments.at(i), _ssaValues)) + return false; + } + else + { + assertThrow(m_arguments.empty(), OptimizerException, "\"Any\" should not have arguments."); + } + + if (m_matchGroup) + { + // We support matching multiple expressions that require the same value + // based on identical ASTs, which have to be movable. + + // TODO: add tests: + // - { let x := mload(0) let y := and(x, x) } + // - { let x := 4 let y := and(x, y) } + + // This code uses `_expr` again for "Any", because we want the comparison to be done + // on the variables and not their values. + // The assumption is that CSE or local value numbering has been done prior to this step. + + if (m_matchGroups->count(m_matchGroup)) + { + assertThrow(m_kind == PatternKind::Any, OptimizerException, "Match group repetition for non-any."); + Expression const* firstMatch = (*m_matchGroups)[m_matchGroup]; + assertThrow(firstMatch, OptimizerException, "Match set but to null."); + return + SyntacticalEqualityChecker::equal(*firstMatch, _expr) && + MovableChecker(_expr).movable(); + } + else if (m_kind == PatternKind::Any) + (*m_matchGroups)[m_matchGroup] = &_expr; + else + { + assertThrow(m_kind == PatternKind::Constant, OptimizerException, "Match group set for operation."); + // We do not use _expr here, because we want the actual number. + (*m_matchGroups)[m_matchGroup] = expr; + } + } + return true; +} + +solidity::Instruction Pattern::instruction() const +{ + assertThrow(m_kind == PatternKind::Operation, OptimizerException, ""); + return m_instruction; +} + +Expression Pattern::toExpression(SourceLocation const& _location) const +{ + if (matchGroup()) + return ASTCopier().translate(matchGroupValue()); + if (m_kind == PatternKind::Constant) + { + assertThrow(m_data, OptimizerException, "No match group and no constant value given."); + return Literal{_location, assembly::LiteralKind::Number, YulString{formatNumber(*m_data)}, {}}; + } + else if (m_kind == PatternKind::Operation) + { + vector<Expression> arguments; + for (auto const& arg: m_arguments) + arguments.emplace_back(arg.toExpression(_location)); + return FunctionalInstruction{_location, m_instruction, std::move(arguments)}; + } + assertThrow(false, OptimizerException, "Pattern of kind 'any', but no match group."); +} + +u256 Pattern::d() const +{ + Literal const& literal = boost::get<Literal>(matchGroupValue()); + assertThrow(literal.kind == assembly::LiteralKind::Number, OptimizerException, ""); + assertThrow(isValidDecimal(literal.value.str()) || isValidHex(literal.value.str()), OptimizerException, ""); + return u256(literal.value.str()); +} + +Expression const& Pattern::matchGroupValue() const +{ + assertThrow(m_matchGroup > 0, OptimizerException, ""); + assertThrow(!!m_matchGroups, OptimizerException, ""); + assertThrow((*m_matchGroups)[m_matchGroup], OptimizerException, ""); + return *(*m_matchGroups)[m_matchGroup]; +} diff --git a/libyul/optimiser/SimplificationRules.h b/libyul/optimiser/SimplificationRules.h new file mode 100644 index 00000000..b608ca91 --- /dev/null +++ b/libyul/optimiser/SimplificationRules.h @@ -0,0 +1,124 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see <http://www.gnu.org/licenses/>. +*/ +/** + * Module for applying replacement rules against Expressions. + */ + +#pragma once + +#include <libevmasm/ExpressionClasses.h> +#include <libevmasm/SimplificationRule.h> + +#include <libyul/ASTDataForward.h> + +#include <libsolidity/inlineasm/AsmData.h> + +#include <boost/noncopyable.hpp> + +#include <functional> +#include <vector> + +namespace dev +{ +namespace yul +{ + +class Pattern; + +/** + * Container for all simplification rules. + */ +class SimplificationRules: public boost::noncopyable +{ +public: + SimplificationRules(); + + /// @returns a pointer to the first matching pattern and sets the match + /// groups accordingly. + /// @param _ssaValues values of variables that are assigned exactly once. + static SimplificationRule<Pattern> const* findFirstMatch( + Expression const& _expr, + std::map<YulString, Expression const*> const& _ssaValues + ); + + /// Checks whether the rulelist is non-empty. This is usually enforced + /// by the constructor, but we had some issues with static initialization. + bool isInitialized() const; +private: + void addRules(std::vector<SimplificationRule<Pattern>> const& _rules); + void addRule(SimplificationRule<Pattern> const& _rule); + + void resetMatchGroups() { m_matchGroups.clear(); } + + std::map<unsigned, Expression const*> m_matchGroups; + std::vector<SimplificationRule<Pattern>> m_rules[256]; +}; + +enum class PatternKind +{ + Operation, + Constant, + Any +}; + +/** + * Pattern to match against an expression. + * Also stores matched expressions to retrieve them later, for constructing new expressions using + * ExpressionTemplate. + */ +class Pattern +{ +public: + /// Matches any expression. + Pattern(PatternKind _kind = PatternKind::Any): m_kind(_kind) {} + // Matches a specific constant value. + Pattern(unsigned _value): Pattern(u256(_value)) {} + // Matches a specific constant value. + Pattern(u256 const& _value): m_kind(PatternKind::Constant), m_data(std::make_shared<u256>(_value)) {} + // Matches a given instruction with given arguments + Pattern(solidity::Instruction _instruction, std::vector<Pattern> const& _arguments = {}); + /// Sets this pattern to be part of the match group with the identifier @a _group. + /// Inside one rule, all patterns in the same match group have to match expressions from the + /// same expression equivalence class. + void setMatchGroup(unsigned _group, std::map<unsigned, Expression const*>& _matchGroups); + unsigned matchGroup() const { return m_matchGroup; } + bool matches(Expression const& _expr, std::map<YulString, Expression const*> const& _ssaValues) const; + + std::vector<Pattern> arguments() const { return m_arguments; } + + /// @returns the data of the matched expression if this pattern is part of a match group. + u256 d() const; + + solidity::Instruction instruction() const; + + /// Turns this pattern into an actual expression. Should only be called + /// for patterns resulting from an action, i.e. with match groups assigned. + Expression toExpression(SourceLocation const& _location) const; + +private: + Expression const& matchGroupValue() const; + + PatternKind m_kind = PatternKind::Any; + solidity::Instruction m_instruction; ///< Only valid if m_kind is Operation + std::shared_ptr<u256> m_data; ///< Only valid if m_kind is Constant + std::vector<Pattern> m_arguments; + unsigned m_matchGroup = 0; + std::map<unsigned, Expression const*>* m_matchGroups = nullptr; +}; + +} +} diff --git a/libyul/optimiser/Substitution.cpp b/libyul/optimiser/Substitution.cpp new file mode 100644 index 00000000..9b3d4c03 --- /dev/null +++ b/libyul/optimiser/Substitution.cpp @@ -0,0 +1,39 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see <http://www.gnu.org/licenses/>. +*/ +/** + * Specific AST copier that replaces certain identifiers with expressions. + */ + +#include <libyul/optimiser/Substitution.h> + +#include <libsolidity/inlineasm/AsmData.h> + +using namespace std; +using namespace dev; +using namespace dev::yul; + +Expression Substitution::translate(Expression const& _expression) +{ + if (_expression.type() == typeid(Identifier)) + { + YulString name = boost::get<Identifier>(_expression).name; + if (m_substitutions.count(name)) + // No recursive substitution + return ASTCopier().translate(*m_substitutions.at(name)); + } + return ASTCopier::translate(_expression); +} diff --git a/libyul/optimiser/Substitution.h b/libyul/optimiser/Substitution.h new file mode 100644 index 00000000..59ee4620 --- /dev/null +++ b/libyul/optimiser/Substitution.h @@ -0,0 +1,50 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see <http://www.gnu.org/licenses/>. +*/ +/** + * Specific AST copier that replaces certain identifiers with expressions. + */ + +#pragma once + +#include <libyul/optimiser/ASTCopier.h> + +#include <libyul/YulString.h> + +#include <map> + +namespace dev +{ +namespace yul +{ + +/** + * Specific AST copier that replaces certain identifiers with expressions. + */ +class Substitution: public ASTCopier +{ +public: + Substitution(std::map<YulString, Expression const*> const& _substitutions): + m_substitutions(_substitutions) + {} + virtual Expression translate(Expression const& _expression) override; + +private: + std::map<YulString, Expression const*> const& m_substitutions; +}; + +} +} diff --git a/libyul/optimiser/Suite.cpp b/libyul/optimiser/Suite.cpp new file mode 100644 index 00000000..7d52a5a8 --- /dev/null +++ b/libyul/optimiser/Suite.cpp @@ -0,0 +1,120 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see <http://www.gnu.org/licenses/>. +*/ +/** + * Optimiser suite that combines all steps and also provides the settings for the heuristics. + */ + +#include <libyul/optimiser/Suite.h> + +#include <libyul/optimiser/Disambiguator.h> +#include <libyul/optimiser/FunctionGrouper.h> +#include <libyul/optimiser/FunctionHoister.h> +#include <libyul/optimiser/ExpressionSplitter.h> +#include <libyul/optimiser/ExpressionJoiner.h> +#include <libyul/optimiser/ExpressionInliner.h> +#include <libyul/optimiser/FullInliner.h> +#include <libyul/optimiser/Rematerialiser.h> +#include <libyul/optimiser/UnusedPruner.h> +#include <libyul/optimiser/ExpressionSimplifier.h> +#include <libyul/optimiser/CommonSubexpressionEliminator.h> +#include <libyul/optimiser/SSATransform.h> +#include <libyul/optimiser/RedundantAssignEliminator.h> +#include <libyul/optimiser/VarDeclPropagator.h> + +#include <libsolidity/inlineasm/AsmAnalysisInfo.h> +#include <libsolidity/inlineasm/AsmData.h> + +#include <libsolidity/inlineasm/AsmPrinter.h> + +#include <libdevcore/CommonData.h> + +using namespace std; +using namespace dev; +using namespace dev::yul; + +void OptimiserSuite::run( + Block& _ast, + solidity::assembly::AsmAnalysisInfo const& _analysisInfo, + set<YulString> const& _externallyUsedIdentifiers +) +{ + set<YulString> reservedIdentifiers = _externallyUsedIdentifiers; + + Block ast = boost::get<Block>(Disambiguator(_analysisInfo, reservedIdentifiers)(_ast)); + + (FunctionHoister{})(ast); + (FunctionGrouper{})(ast); + + NameDispenser dispenser{ast}; + + for (size_t i = 0; i < 4; i++) + { + ExpressionSplitter{dispenser}(ast); + SSATransform::run(ast, dispenser); + RedundantAssignEliminator::run(ast); + VarDeclPropagator{}(ast); + RedundantAssignEliminator::run(ast); + + CommonSubexpressionEliminator{}(ast); + ExpressionSimplifier::run(ast); + SSATransform::run(ast, dispenser); + RedundantAssignEliminator::run(ast); + RedundantAssignEliminator::run(ast); + UnusedPruner::runUntilStabilised(ast, reservedIdentifiers); + CommonSubexpressionEliminator{}(ast); + UnusedPruner::runUntilStabilised(ast, reservedIdentifiers); + SSATransform::run(ast, dispenser); + RedundantAssignEliminator::run(ast); + RedundantAssignEliminator::run(ast); + + ExpressionJoiner::run(ast); + ExpressionJoiner::run(ast); + ExpressionInliner(ast).run(); + UnusedPruner::runUntilStabilised(ast); + + ExpressionSplitter{dispenser}(ast); + SSATransform::run(ast, dispenser); + RedundantAssignEliminator::run(ast); + RedundantAssignEliminator::run(ast); + CommonSubexpressionEliminator{}(ast); + FullInliner{ast, dispenser}.run(); + VarDeclPropagator{}(ast); + SSATransform::run(ast, dispenser); + RedundantAssignEliminator::run(ast); + VarDeclPropagator{}(ast); + RedundantAssignEliminator::run(ast); + ExpressionSimplifier::run(ast); + CommonSubexpressionEliminator{}(ast); + SSATransform::run(ast, dispenser); + RedundantAssignEliminator::run(ast); + VarDeclPropagator{}(ast); + RedundantAssignEliminator::run(ast); + UnusedPruner::runUntilStabilised(ast, reservedIdentifiers); + } + ExpressionJoiner::run(ast); + VarDeclPropagator{}(ast); + UnusedPruner::runUntilStabilised(ast); + ExpressionJoiner::run(ast); + UnusedPruner::runUntilStabilised(ast); + ExpressionJoiner::run(ast); + VarDeclPropagator{}(ast); + UnusedPruner::runUntilStabilised(ast); + ExpressionJoiner::run(ast); + UnusedPruner::runUntilStabilised(ast); + + _ast = std::move(ast); +} diff --git a/libyul/optimiser/Suite.h b/libyul/optimiser/Suite.h new file mode 100644 index 00000000..5b564c56 --- /dev/null +++ b/libyul/optimiser/Suite.h @@ -0,0 +1,55 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see <http://www.gnu.org/licenses/>. +*/ +/** + * Optimiser suite that combines all steps and also provides the settings for the heuristics. + */ + +#pragma once + +#include <libyul/ASTDataForward.h> +#include <libyul/YulString.h> + +#include <set> + +namespace dev +{ +namespace solidity +{ +namespace assembly +{ +struct AsmAnalysisInfo; +} +} +namespace yul +{ + +/** + * Optimiser suite that combines all steps and also provides the settings for the heuristics + */ +class OptimiserSuite +{ +public: + static void run( + Block& _ast, + solidity::assembly::AsmAnalysisInfo const& _analysisInfo, + + std::set<YulString> const& _externallyUsedIdentifiers = {} + ); +}; + +} +} diff --git a/libyul/optimiser/SyntacticalEquality.cpp b/libyul/optimiser/SyntacticalEquality.cpp new file mode 100644 index 00000000..66912383 --- /dev/null +++ b/libyul/optimiser/SyntacticalEquality.cpp @@ -0,0 +1,77 @@ +/*( + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see <http://www.gnu.org/licenses/>. +*/ +/** + * Component that can compare ASTs for equality on a syntactic basis. + */ + +#include <libyul/optimiser/SyntacticalEquality.h> + +#include <libyul/Exceptions.h> + +#include <libsolidity/inlineasm/AsmData.h> + +#include <libdevcore/CommonData.h> + +using namespace std; +using namespace dev; +using namespace dev::yul; + +bool SyntacticalEqualityChecker::equal(Expression const& _e1, Expression const& _e2) +{ + if (_e1.type() != _e2.type()) + return false; + // TODO This somehow calls strcmp - WHERE? + + // TODO This should be replaced by some kind of AST walker as soon as it gets + // more complex. + if (_e1.type() == typeid(FunctionalInstruction)) + { + auto const& e1 = boost::get<FunctionalInstruction>(_e1); + auto const& e2 = boost::get<FunctionalInstruction>(_e2); + return + e1.instruction == e2.instruction && + equalVector(e1.arguments, e2.arguments); + } + else if (_e1.type() == typeid(FunctionCall)) + { + auto const& e1 = boost::get<FunctionCall>(_e1); + auto const& e2 = boost::get<FunctionCall>(_e2); + return + equal(e1.functionName, e2.functionName) && + equalVector(e1.arguments, e2.arguments); + } + else if (_e1.type() == typeid(Identifier)) + return boost::get<Identifier>(_e1).name == boost::get<Identifier>(_e2).name; + else if (_e1.type() == typeid(Literal)) + { + auto const& e1 = boost::get<Literal>(_e1); + auto const& e2 = boost::get<Literal>(_e2); + return e1.kind == e2.kind && e1.value == e2.value && e1.type == e2.type; + } + else + { + assertThrow(false, OptimizerException, "Invalid expression"); + } + return false; +} + +bool SyntacticalEqualityChecker::equalVector(vector<Expression> const& _e1, vector<Expression> const& _e2) +{ + return _e1.size() == _e2.size() && + std::equal(begin(_e1), end(_e1), begin(_e2), SyntacticalEqualityChecker::equal); + +} diff --git a/libyul/optimiser/SyntacticalEquality.h b/libyul/optimiser/SyntacticalEquality.h new file mode 100644 index 00000000..e9fbebe0 --- /dev/null +++ b/libyul/optimiser/SyntacticalEquality.h @@ -0,0 +1,50 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see <http://www.gnu.org/licenses/>. +*/ +/** + * Component that can compare ASTs for equality on a syntactic basis. + */ + +#pragma once + +#include <libyul/ASTDataForward.h> + +#include <vector> + +namespace dev +{ +namespace yul +{ + +/** + * Component that can compare ASTs for equality on a syntactic basis. + * Ignores source locations but requires exact matches otherwise. + * + * TODO: Only implemented for Expressions for now. + * A future version might also recognize renamed variables and thus could be used to + * remove duplicate functions. + */ +class SyntacticalEqualityChecker +{ +public: + static bool equal(Expression const& _e1, Expression const& _e2); + +protected: + static bool equalVector(std::vector<Expression> const& _e1, std::vector<Expression> const& _e2); +}; + +} +} diff --git a/libyul/optimiser/UnusedPruner.cpp b/libyul/optimiser/UnusedPruner.cpp new file mode 100644 index 00000000..71e86798 --- /dev/null +++ b/libyul/optimiser/UnusedPruner.cpp @@ -0,0 +1,129 @@ +/*( + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see <http://www.gnu.org/licenses/>. +*/ +/** + * Optimisation stage that removes unused variables and functions. + */ + +#include <libyul/optimiser/UnusedPruner.h> + +#include <libyul/optimiser/NameCollector.h> +#include <libyul/optimiser/Semantics.h> +#include <libyul/optimiser/Utilities.h> +#include <libyul/Exceptions.h> + +#include <libsolidity/inlineasm/AsmData.h> + +#include <boost/algorithm/cxx11/none_of.hpp> + +using namespace std; +using namespace dev; +using namespace dev::yul; + +UnusedPruner::UnusedPruner(Block& _ast, set<YulString> const& _externallyUsedFunctions) +{ + ReferencesCounter counter; + counter(_ast); + + m_references = counter.references(); + for (auto const& f: _externallyUsedFunctions) + ++m_references[f]; +} + +void UnusedPruner::operator()(Block& _block) +{ + for (auto&& statement: _block.statements) + if (statement.type() == typeid(FunctionDefinition)) + { + FunctionDefinition& funDef = boost::get<FunctionDefinition>(statement); + if (!used(funDef.name)) + { + subtractReferences(ReferencesCounter::countReferences(funDef.body)); + statement = Block{std::move(funDef.location), {}}; + } + } + else if (statement.type() == typeid(VariableDeclaration)) + { + VariableDeclaration& varDecl = boost::get<VariableDeclaration>(statement); + // Multi-variable declarations are special. We can only remove it + // if all vairables are unused and the right-hand-side is either + // movable or it return a single value. In the latter case, we + // replace `let a := f()` by `pop(f())` (in pure Yul, this will be + // `drop(f())`). + if (boost::algorithm::none_of( + varDecl.variables, + [=](TypedName const& _typedName) { return used(_typedName.name); } + )) + { + if (!varDecl.value) + statement = Block{std::move(varDecl.location), {}}; + else if (MovableChecker(*varDecl.value).movable()) + { + subtractReferences(ReferencesCounter::countReferences(*varDecl.value)); + statement = Block{std::move(varDecl.location), {}}; + } + else if (varDecl.variables.size() == 1) + // In pure Yul, this should be replaced by a function call to `drop` + // instead of `pop`. + statement = ExpressionStatement{varDecl.location, FunctionalInstruction{ + varDecl.location, + solidity::Instruction::POP, + {*std::move(varDecl.value)} + }}; + } + } + else if (statement.type() == typeid(ExpressionStatement)) + { + ExpressionStatement& exprStmt = boost::get<ExpressionStatement>(statement); + if (MovableChecker(exprStmt.expression).movable()) + { + // pop(x) should be movable! + subtractReferences(ReferencesCounter::countReferences(exprStmt.expression)); + statement = Block{std::move(exprStmt.location), {}}; + } + } + + removeEmptyBlocks(_block); + + ASTModifier::operator()(_block); +} + +void UnusedPruner::runUntilStabilised(Block& _ast, set<YulString> const& _externallyUsedFunctions) +{ + while (true) + { + UnusedPruner pruner(_ast, _externallyUsedFunctions); + pruner(_ast); + if (!pruner.shouldRunAgain()) + return; + } +} + +bool UnusedPruner::used(YulString _name) const +{ + return m_references.count(_name) && m_references.at(_name) > 0; +} + +void UnusedPruner::subtractReferences(map<YulString, size_t> const& _subtrahend) +{ + for (auto const& ref: _subtrahend) + { + assertThrow(m_references.count(ref.first), OptimizerException, ""); + assertThrow(m_references.at(ref.first) >= ref.second, OptimizerException, ""); + m_references[ref.first] -= ref.second; + m_shouldRunAgain = true; + } +} diff --git a/libyul/optimiser/UnusedPruner.h b/libyul/optimiser/UnusedPruner.h new file mode 100644 index 00000000..b5aea3dd --- /dev/null +++ b/libyul/optimiser/UnusedPruner.h @@ -0,0 +1,65 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see <http://www.gnu.org/licenses/>. +*/ +/** + * Optimisation stage that removes unused variables and functions. + */ + +#pragma once + +#include <libyul/optimiser/ASTWalker.h> +#include <libyul/YulString.h> + +#include <map> +#include <set> + +namespace dev +{ +namespace yul +{ + +/** + * Optimisation stage that removes unused variables and functions and also + * removes movable expression statements. + * + * Note that this does not remove circular references. + * + * Prerequisite: Disambiguator + */ +class UnusedPruner: public ASTModifier +{ +public: + explicit UnusedPruner(Block& _ast, std::set<YulString> const& _externallyUsedFunctions = {}); + + using ASTModifier::operator(); + virtual void operator()(Block& _block) override; + + // @returns true iff the code changed in the previous run. + bool shouldRunAgain() const { return m_shouldRunAgain; } + + // Run the pruner until the code does not change anymore. + static void runUntilStabilised(Block& _ast, std::set<YulString> const& _externallyUsedFunctions = {}); + +private: + bool used(YulString _name) const; + void subtractReferences(std::map<YulString, size_t> const& _subtrahend); + + bool m_shouldRunAgain = false; + std::map<YulString, size_t> m_references; +}; + +} +} diff --git a/libyul/optimiser/Utilities.cpp b/libyul/optimiser/Utilities.cpp new file mode 100644 index 00000000..df01ed39 --- /dev/null +++ b/libyul/optimiser/Utilities.cpp @@ -0,0 +1,39 @@ +/*( + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see <http://www.gnu.org/licenses/>. +*/ +/** + * Some useful snippets for the optimiser. + */ + +#include <libyul/optimiser/Utilities.h> + +#include <libsolidity/inlineasm/AsmData.h> + +#include <libdevcore/CommonData.h> + +#include <boost/range/algorithm_ext/erase.hpp> + +using namespace std; +using namespace dev; +using namespace dev::yul; + +void dev::yul::removeEmptyBlocks(Block& _block) +{ + auto isEmptyBlock = [](Statement const& _st) -> bool { + return _st.type() == typeid(Block) && boost::get<Block>(_st).statements.empty(); + }; + boost::range::remove_erase_if(_block.statements, isEmptyBlock); +} diff --git a/libyul/optimiser/Utilities.h b/libyul/optimiser/Utilities.h new file mode 100644 index 00000000..5b18a27c --- /dev/null +++ b/libyul/optimiser/Utilities.h @@ -0,0 +1,34 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see <http://www.gnu.org/licenses/>. +*/ +/** + * Small useful snippets for the optimiser. + */ + +#pragma once + +#include <libyul/ASTDataForward.h> + +namespace dev +{ +namespace yul +{ + +/// Removes statements that are just empty blocks (non-recursive). +void removeEmptyBlocks(Block& _block); + +} +} diff --git a/libyul/optimiser/VarDeclPropagator.cpp b/libyul/optimiser/VarDeclPropagator.cpp new file mode 100644 index 00000000..537b7020 --- /dev/null +++ b/libyul/optimiser/VarDeclPropagator.cpp @@ -0,0 +1,129 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <libyul/optimiser/VarDeclPropagator.h> +#include <libsolidity/inlineasm/AsmData.h> +#include <libdevcore/CommonData.h> +#include <boost/range/algorithm_ext/erase.hpp> +#include <algorithm> +#include <map> + +using namespace std; +using namespace dev; +using namespace dev::yul; + +using dev::solidity::assembly::TypedName; +using dev::solidity::assembly::TypedNameList; + +void VarDeclPropagator::operator()(Block& _block) +{ + map<YulString, TypedName> outerEmptyVarDecls; + map<YulString, TypedName> outerLazyInitializedVarDecls; + swap(m_emptyVarDecls, outerEmptyVarDecls); + swap(m_lazyInitializedVarDecls, outerLazyInitializedVarDecls); + + ASTModifier::operator()(_block); + + iterateReplacing( + _block.statements, + [this](Statement& _stmt) -> boost::optional<vector<Statement>> + { + if (_stmt.type() == typeid(VariableDeclaration)) + { + VariableDeclaration& varDecl = boost::get<VariableDeclaration>(_stmt); + boost::remove_erase_if( + varDecl.variables, + [&](TypedName const& _typedName) { return m_lazyInitializedVarDecls.count(_typedName.name); } + ); + if (varDecl.variables.empty()) + return vector<Statement>{}; + else + return {}; + } + else if (_stmt.type() == typeid(Assignment)) + { + Assignment& assignment = boost::get<Assignment>(_stmt); + if (isFullyLazyInitialized(assignment.variableNames)) + return vector<Statement>{recreateVariableDeclaration(assignment)}; + else + return {}; + } + else + return {}; + } + ); + + swap(m_emptyVarDecls, outerEmptyVarDecls); + swap(m_lazyInitializedVarDecls, outerLazyInitializedVarDecls); +} + +void VarDeclPropagator::operator()(VariableDeclaration& _varDecl) +{ + if (_varDecl.value) + visit(*_varDecl.value); + else + for (TypedName const& typedName: _varDecl.variables) + m_emptyVarDecls[typedName.name] = typedName; +} + +void VarDeclPropagator::operator()(Assignment& _assignment) +{ + visit(*_assignment.value); + + if (allVarNamesUninitialized(_assignment.variableNames)) + for (Identifier const& ident: _assignment.variableNames) + m_lazyInitializedVarDecls[ident.name] = m_emptyVarDecls[ident.name]; + + for (Identifier& name: _assignment.variableNames) + (*this)(name); +} + +void VarDeclPropagator::operator()(Identifier& _ident) +{ + m_emptyVarDecls.erase(_ident.name); +} + +bool VarDeclPropagator::allVarNamesUninitialized(vector<Identifier> const& _variableNames) const +{ + return all_of( + begin(_variableNames), + end(_variableNames), + [&](Identifier const& _ident) -> bool { return m_emptyVarDecls.count(_ident.name); } + ); +} + +bool VarDeclPropagator::isFullyLazyInitialized(vector<Identifier> const& _variableNames) const +{ + return all_of( + begin(_variableNames), + end(_variableNames), + [&](Identifier const& ident) -> bool { return m_lazyInitializedVarDecls.count(ident.name); } + ); +} + +VariableDeclaration VarDeclPropagator::recreateVariableDeclaration(Assignment& _assignment) +{ + TypedNameList variables; + + for (Identifier const& varName: _assignment.variableNames) + { + variables.emplace_back(move(m_lazyInitializedVarDecls.at(varName.name))); + m_lazyInitializedVarDecls.erase(varName.name); + } + + return VariableDeclaration{move(_assignment.location), move(variables), std::move(_assignment.value)}; +} diff --git a/libyul/optimiser/VarDeclPropagator.h b/libyul/optimiser/VarDeclPropagator.h new file mode 100644 index 00000000..4522d23a --- /dev/null +++ b/libyul/optimiser/VarDeclPropagator.h @@ -0,0 +1,63 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see <http://www.gnu.org/licenses/>. +*/ + +#pragma once + +#include <libyul/ASTDataForward.h> +#include <libyul/optimiser/ASTWalker.h> +#include <libyul/Exceptions.h> +#include <libsolidity/inlineasm/AsmDataForward.h> +#include <vector> +#include <set> +#include <map> + +namespace dev +{ +namespace yul +{ + +/** + * Rewrites Assignment statements into VariableDeclaration when the assignment's LHS + * variables had no value yet. + * + * It recursively walks through the AST and moves each declaration of variables to + * the first assignment within the same block (if possible).. + */ +class VarDeclPropagator: public ASTModifier +{ +public: + using ASTModifier::operator(); + void operator()(Block& _block) override; + void operator()(VariableDeclaration& _varDecl) override; + void operator()(Assignment& _assignment) override; + void operator()(Identifier& _ident) override; + +private: + bool allVarNamesUninitialized(std::vector<Identifier> const& _variableNames) const; + bool isFullyLazyInitialized(std::vector<Identifier> const& _variableNames) const; + VariableDeclaration recreateVariableDeclaration(Assignment& _assignment); + +private: + /// Holds a list of variables from current Block that have no value assigned yet. + std::map<YulString, TypedName> m_emptyVarDecls; + + /// Holds a list variables (and their TypedName) within the current block. + std::map<YulString, TypedName> m_lazyInitializedVarDecls; +}; + +} +} diff --git a/lllc/main.cpp b/lllc/main.cpp index 0ca3ff13..f863451d 100644 --- a/lllc/main.cpp +++ b/lllc/main.cpp @@ -32,7 +32,7 @@ using namespace std; using namespace dev; using namespace dev::solidity; -using namespace dev::eth; +using namespace dev::lll; static string const VersionString = string(ETH_PROJECT_VERSION) + @@ -49,6 +49,7 @@ static void help() << " -a,--assembly Only parse and compile; show assembly." << endl << " -t,--parse-tree Only parse; show parse tree." << endl << " -o,--optimise Turn on/off the optimiser; off by default." << endl + << " -d,--disassemble Disassemble input into an opcode stream." << endl << " -h,--help Show this help message and exit." << endl << " -V,--version Show the version and exit." << endl; exit(0); diff --git a/scripts/Dockerfile b/scripts/Dockerfile index 654a9f29..2b2de1e2 100644 --- a/scripts/Dockerfile +++ b/scripts/Dockerfile @@ -1,18 +1,39 @@ -FROM alpine +FROM alpine AS build MAINTAINER chriseth <chris@ethereum.org> #Official solidity docker image #Establish working directory as solidity WORKDIR /solidity + +# Build dependencies +ADD /scripts/install_deps.sh /solidity/scripts/install_deps.sh +RUN ./scripts/install_deps.sh + #Copy working directory on travis to the image COPY / $WORKDIR -#Install dependencies, eliminate annoying warnings, and build release, delete all remaining points and statically link. -RUN ./scripts/install_deps.sh && sed -i -E -e 's/include <sys\/poll.h>/include <poll.h>/' /usr/include/boost/asio/detail/socket_types.hpp &&\ -cmake -DCMAKE_BUILD_TYPE=Release -DTESTS=0 -DSOLC_LINK_STATIC=1 &&\ -make solc && install -s solc/solc /usr/bin &&\ -cd / && rm -rf solidity &&\ -apk del sed build-base git make cmake gcc g++ musl-dev curl-dev boost-dev &&\ -rm -rf /var/cache/apk/* +# Number of parallel jobs during build +# or 0 for auto-computing (max(1, CPU_core_count * 2/3), a greedy value) +ARG BUILD_CONCURRENCY="0" + +#Install dependencies, eliminate annoying warnings +RUN sed -i -E -e 's/include <sys\/poll.h>/include <poll.h>/' /usr/include/boost/asio/detail/socket_types.hpp +RUN cmake -DCMAKE_BUILD_TYPE=Release -DTESTS=0 -DSOLC_LINK_STATIC=1 +RUN make solc \ + -j$(awk "BEGIN { \ + if (${BUILD_CONCURRENCY} != 0) { \ + print(${BUILD_CONCURRENCY}); \ + } else { \ + x=($(grep -c ^processor /proc/cpuinfo) * 2/3); \ + if (x > 1) { \ + printf(\"%d\n\", x); \ + } else { \ + print(1); \ + } \ + } \ + }") +RUN strip solc/solc -ENTRYPOINT ["/usr/bin/solc"]
\ No newline at end of file +FROM scratch +COPY --from=build /solidity/solc/solc /usr/bin/solc +ENTRYPOINT ["/usr/bin/solc"] diff --git a/scripts/bytecodecompare/storebytecode.bat b/scripts/bytecodecompare/storebytecode.bat index e64e9276..ef20a320 100644 --- a/scripts/bytecodecompare/storebytecode.bat +++ b/scripts/bytecodecompare/storebytecode.bat @@ -39,4 +39,5 @@ set REPORT=%DIRECTORY%/windows.txt cp ../report.txt %REPORT% git add %REPORT% git commit -a -m "Added report." +git pull --rebase git push origin 2>&1 diff --git a/scripts/bytecodecompare/storebytecode.sh b/scripts/bytecodecompare/storebytecode.sh index 557e3275..ccf6e60e 100755 --- a/scripts/bytecodecompare/storebytecode.sh +++ b/scripts/bytecodecompare/storebytecode.sh @@ -40,14 +40,16 @@ TMPDIR=$(mktemp -d) if [[ "$SOLC_EMSCRIPTEN" = "On" ]] then - cp "$REPO_ROOT/build/libsolc/soljson.js" . - npm install solc + # npm install solc + git clone --depth 1 https://github.com/ethereum/solc-js.git solc-js + ( cd solc-js; npm install ) + cp "$REPO_ROOT/build/libsolc/soljson.js" solc-js/ cat > solc <<EOF #!/usr/bin/env node var process = require('process') var fs = require('fs') -var compiler = require('solc/wrapper.js')(require('./soljson.js')) +var compiler = require('./solc-js/wrapper.js')(require('./solc-js/soljson.js')) for (var optimize of [false, true]) { @@ -57,7 +59,15 @@ for (var optimize of [false, true]) { var inputs = {} inputs[filename] = fs.readFileSync(filename).toString() - var result = compiler.compile({sources: inputs}, optimize) + var input = { + language: 'Solidity', + sources: inputs, + settings: { + optimizer: { enabled: optimize }, + outputSelection: { '*': { '*': ['evm.bytecode.object', 'metadata'] } } + } + } + var result = JSON.parse(compiler.compile(JSON.stringify(input))) if (!('contracts' in result) || Object.keys(result['contracts']).length === 0) { console.log(filename + ': ERROR') @@ -66,7 +76,7 @@ for (var optimize of [false, true]) { for (var contractName in result['contracts']) { - console.log(contractName + ' ' + result['contracts'][contractName].bytecode) + console.log(contractName + ' ' + result['contracts'][contractName].evm.bytecode.object) console.log(contractName + ' ' + result['contracts'][contractName].metadata) } } diff --git a/scripts/check_style.sh b/scripts/check_style.sh new file mode 100755 index 00000000..4f716d66 --- /dev/null +++ b/scripts/check_style.sh @@ -0,0 +1,29 @@ +#!/usr/bin/env bash + +( +REPO_ROOT="$(dirname "$0")"/.. +cd $REPO_ROOT + +WHITESPACE=$(git grep -n -I -E "^.*[[:space:]]+$" | grep -v "test/libsolidity/ASTJSON\|test/compilationTests/zeppelin/LICENSE") + +if [[ "$WHITESPACE" != "" ]] +then + echo "Error: Trailing whitespace found:" >&2 + echo "$WHITESPACE" >&2 + exit 1 +fi + +FORMATERROR=$( +( +git grep -nIE "\<(if|for)\(" -- '*.h' '*.cpp' +git grep -nIE "\<if\>\s*\(.*\)\s*\{\s*$" -- '*.h' '*.cpp' +) | egrep -v "^[a-zA-Z\./]*:[0-9]*:\s*\/(\/|\*)" | egrep -v "^test/" +) + +if [[ "$FORMATERROR" != "" ]] +then + echo "Error: Format error for if/for:" >&2 + echo "$FORMATERROR" >&2 + exit 1 +fi +) diff --git a/scripts/codespell_whitelist.txt b/scripts/codespell_whitelist.txt new file mode 100644 index 00000000..0f5013cb --- /dev/null +++ b/scripts/codespell_whitelist.txt @@ -0,0 +1,4 @@ +iff +nd +assignend +uint diff --git a/scripts/create_source_tarball.sh b/scripts/create_source_tarball.sh index 4e930707..632c1daa 100755 --- a/scripts/create_source_tarball.sh +++ b/scripts/create_source_tarball.sh @@ -23,7 +23,6 @@ REPO_ROOT="$(dirname "$0")"/.. mkdir "$SOLDIR" # Store the current source git checkout-index -a --prefix="$SOLDIR" - git submodule foreach 'git checkout-index -a --prefix="'"$SOLDIR"'/$path/"' # Store the commit hash echo "$commithash" > "$SOLDIR/commit_hash.txt" if [ -e prerelease.txt -a ! -s prerelease.txt ] diff --git a/scripts/docs.sh b/scripts/docs.sh index 42400bc7..2c08a82b 100755 --- a/scripts/docs.sh +++ b/scripts/docs.sh @@ -28,5 +28,6 @@ set -e cd docs +pip install -r requirements.txt sphinx-build -nW -b html -d _build/doctrees . _build/html cd .. diff --git a/scripts/extract_test_cases.py b/scripts/extract_test_cases.py index 07ef9a96..47c53f3c 100755 --- a/scripts/extract_test_cases.py +++ b/scripts/extract_test_cases.py @@ -1,4 +1,4 @@ -#!/usr/bin/python +#!/usr/bin/env python2 # # This script reads C++ or RST source files and writes all # multi-line strings into individual files. diff --git a/scripts/install_deps.sh b/scripts/install_deps.sh index fa5551bf..b107f7c9 100755 --- a/scripts/install_deps.sh +++ b/scripts/install_deps.sh @@ -87,9 +87,12 @@ case $(uname -s) in 10.13) echo "Installing solidity dependencies on macOS 10.13 High Sierra." ;; + 10.14) + echo "Installing solidity dependencies on macOS 10.14 Mojave." + ;; *) echo "Unsupported macOS version." - echo "We only support Mavericks, Yosemite, El Capitan, Sierra and High Sierra." + echo "We only support Mavericks, Yosemite, El Capitan, Sierra, High Sierra and Mojave." exit 1 ;; esac @@ -133,19 +136,18 @@ case $(uname -s) in # Arch Linux #------------------------------------------------------------------------------ - Arch) + Arch*|ManjaroLinux) #Arch echo "Installing solidity dependencies on Arch Linux." # All our dependencies can be found in the Arch Linux official repositories. # See https://wiki.archlinux.org/index.php/Official_repositories - # Also adding ethereum-git to allow for testing with the `eth` client sudo pacman -Syu \ base-devel \ boost \ cmake \ git \ - ethereum-git \ + cvc4 ;; #------------------------------------------------------------------------------ @@ -160,7 +162,7 @@ case $(uname -s) in # See https://pkgs.alpinelinux.org/ apk update - apk add boost-dev build-base cmake + apk add boost-dev build-base cmake git ;; @@ -329,7 +331,7 @@ case $(uname -s) in "$install_z3" if [ "$CI" = true ]; then # install Z3 from PPA if the distribution does not provide it - if ! dpkg -l libz3-dev > /dev/null 2>&1 + if ! dpkg -l libz3-dev > /dev/null 2>&1 then sudo apt-add-repository -y ppa:hvr/z3 sudo apt-get -y update diff --git a/scripts/install_obsolete_jsoncpp_1_7_4.sh b/scripts/install_obsolete_jsoncpp_1_7_4.sh new file mode 100755 index 00000000..0ae7b34c --- /dev/null +++ b/scripts/install_obsolete_jsoncpp_1_7_4.sh @@ -0,0 +1,16 @@ +#!/usr/bin/env sh +set -e + +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" + mkdir -p build + cd build + cmake -DARCHIVE_INSTALL_DIR=. -G "Unix Makefiles" .. + make + make install +) +rm -rf $TEMPDIR diff --git a/scripts/isolate_tests.py b/scripts/isolate_tests.py index 5bf577d3..8a9aa0a7 100755 --- a/scripts/isolate_tests.py +++ b/scripts/isolate_tests.py @@ -1,4 +1,4 @@ -#!/usr/bin/python +#!/usr/bin/env python2 # # This script reads C++ or RST source files and writes all # multi-line strings into individual files. @@ -10,7 +10,7 @@ import sys import re import os import hashlib -from os.path import join +from os.path import join, isfile def extract_test_cases(path): lines = open(path, 'rb').read().splitlines() @@ -35,47 +35,42 @@ def extract_test_cases(path): return tests # Contract sources are indented by 4 spaces. -# Look for `pragma solidity` and abort a line not indented properly. -# If the comment `// This will not compile` is above the pragma, -# the code is skipped. +# Look for `pragma solidity`, `contract`, `library` or `interface` +# and abort a line not indented properly. def extract_docs_cases(path): - # Note: this code works, because splitlines() removes empty new lines - # and thus even if the empty new lines are missing indentation - lines = open(path, 'rb').read().splitlines() - - ignore = False inside = False tests = [] - for l in lines: - if inside: - # Abort if indentation is missing - m = re.search(r'^[^ ]+', l) - if m: - inside = False - else: - tests[-1] += l + '\n' - else: - m = re.search(r'^ // This will not compile', l) - if m: - ignore = True + # Collect all snippets of indented blocks + for l in open(path, 'rb').read().splitlines(): + if l != '': + if not inside and l.startswith(' '): + # start new test + tests += [''] + inside = l.startswith(' ') + if inside: + tests[-1] += l + '\n' + # Filter all tests that do not contain Solidity + return [ + test for test in tests + if re.search(r'^ [ ]*(pragma solidity|contract |library |interface )', test, re.MULTILINE) + ] - if ignore: - # Abort if indentation is missing - m = re.search(r'^[^ ]+', l) - if m: - ignore = False - else: - m = re.search(r'^ pragma solidity .*[0-9]+\.[0-9]+\.[0-9]+;$', l) - if m: - inside = True - tests += [l] +def write_cases(f, tests): + cleaned_filename = f.replace(".","_").replace("-","_").replace(" ","_").lower() + for test in tests: + open('test_%s_%s.sol' % (hashlib.sha256(test).hexdigest(), cleaned_filename), 'wb').write(test) - return tests -def write_cases(tests): - for test in tests: - open('test_%s.sol' % hashlib.sha256(test).hexdigest(), 'wb').write(test) +def extract_and_write(f, path): + if docs: + cases = extract_docs_cases(path) + else: + if f.endswith('.sol'): + cases = [open(path, 'r').read()] + else: + cases = extract_test_cases(path) + write_cases(f, cases) if __name__ == '__main__': path = sys.argv[1] @@ -83,18 +78,14 @@ if __name__ == '__main__': if len(sys.argv) > 2 and sys.argv[2] == 'docs': docs = True - for root, subdirs, files in os.walk(path): - if '_build' in subdirs: - subdirs.remove('_build') - if 'compilationTests' in subdirs: - subdirs.remove('compilationTests') - for f in files: - path = join(root, f) - if docs: - cases = extract_docs_cases(path) - else: - if f.endswith(".sol"): - cases = [open(path, "r").read()] - else: - cases = extract_test_cases(path) - write_cases(cases) + if isfile(path): + extract_and_write(path, path) + else: + for root, subdirs, files in os.walk(path): + if '_build' in subdirs: + subdirs.remove('_build') + if 'compilationTests' in subdirs: + subdirs.remove('compilationTests') + for f in files: + path = join(root, f) + extract_and_write(f, path) diff --git a/scripts/release_ppa.sh b/scripts/release_ppa.sh index b1601336..36a8ef7f 100755 --- a/scripts/release_ppa.sh +++ b/scripts/release_ppa.sh @@ -22,7 +22,7 @@ ## method = ftp ## incoming = ~ethereum/ethereum-dev ## login = anonymous -## +## ## [ethereum] ## fqdn = ppa.launchpad.net ## method = ftp @@ -50,11 +50,11 @@ else ppafilesurl=https://launchpad.net/~ethereum/+archive/ubuntu/ethereum/+files fi -keyid=703F83D0 +keyid=70D110489D66E2F6 email=builds@ethereum.org packagename=solc -for distribution in trusty vivid xenial zesty artful bionic +for distribution in trusty xenial bionic cosmic do cd /tmp/ rm -rf $distribution diff --git a/scripts/tests.sh b/scripts/tests.sh index d63c1fe4..c284c05c 100755 --- a/scripts/tests.sh +++ b/scripts/tests.sh @@ -30,7 +30,11 @@ set -e REPO_ROOT="$(dirname "$0")"/.. +WORKDIR=`mktemp -d` IPC_ENABLED=true +ALETH_PID= +CMDLINE_PID= + if [[ "$OSTYPE" == "darwin"* ]] then SMT_FLAGS="--no-smt" @@ -41,6 +45,49 @@ then fi fi +safe_kill() { + local PID=${1} + local NAME=${2:-${1}} + local n=1 + + # only proceed if $PID does exist + kill -0 $PID 2>/dev/null || return + + echo "Sending SIGTERM to ${NAME} (${PID}) ..." + kill $PID + + # wait until process terminated gracefully + while kill -0 $PID 2>/dev/null && [[ $n -le 4 ]]; do + echo "Waiting ($n) ..." + sleep 1 + n=$[n + 1] + done + + # process still alive? then hard-kill + if kill -0 $PID 2>/dev/null; then + echo "Sending SIGKILL to ${NAME} (${PID}) ..." + kill -9 $PID + fi +} + +cleanup() { + # ensure failing commands don't cause termination during cleanup (especially within safe_kill) + set +e + + if [[ "$IPC_ENABLED" = true ]] && [[ -n "${ALETH_PID}" ]] + then + safe_kill $ALETH_PID $ALETH_PATH + fi + if [[ -n "$CMDLINE_PID" ]] + then + safe_kill $CMDLINE_PID "Commandline tests" + fi + + echo "Cleaning up working directory ${WORKDIR} ..." + rm -rf "$WORKDIR" || true +} +trap cleanup INT TERM + if [ "$1" = --junit_report ] then if [ -z "$2" ] @@ -53,66 +100,82 @@ else log_directory="" fi -function printError() { echo "$(tput setaf 1)$1$(tput sgr0)"; } -function printTask() { echo "$(tput bold)$(tput setaf 2)$1$(tput sgr0)"; } - +if [ "$CIRCLECI" ] +then + function printTask() { echo "$(tput bold)$(tput setaf 2)$1$(tput setaf 7)"; } + function printError() { echo "$(tput setaf 1)$1$(tput setaf 7)"; } +else + function printTask() { echo "$(tput bold)$(tput setaf 2)$1$(tput sgr0)"; } + function printError() { echo "$(tput setaf 1)$1$(tput sgr0)"; } +fi printTask "Running commandline tests..." -"$REPO_ROOT/test/cmdlineTests.sh" & -CMDLINE_PID=$! # Only run in parallel if this is run on CI infrastructure -if [ -z "$CI" ] +if [[ -n "$CI" ]] then - if ! wait $CMDLINE_PID + "$REPO_ROOT/test/cmdlineTests.sh" & + CMDLINE_PID=$! +else + if ! $REPO_ROOT/test/cmdlineTests.sh then printError "Commandline tests FAILED" exit 1 fi fi -function download_eth() +function download_aleth() { if [[ "$OSTYPE" == "darwin"* ]]; then - ETH_PATH="$REPO_ROOT/eth" + ALETH_PATH="$REPO_ROOT/aleth" elif [ -z $CI ]; then - ETH_PATH="eth" + ALETH_PATH="aleth" else + # Any time the hash is updated here, the "Running compiler tests" section should also be updated. mkdir -p /tmp/test if grep -i trusty /etc/lsb-release >/dev/null 2>&1 then - # built from 5ac09111bd0b6518365fe956e1bdb97a2db82af1 at 2018-04-05 - ETH_BINARY=eth_2018-04-05_trusty - ETH_HASH="1e5e178b005e5b51f9d347df4452875ba9b53cc6" + # built from d661ac4fec0aeffbedcdc195f67f5ded0c798278 at 2018-06-20 + ALETH_BINARY=aleth_2018-06-20_trusty + ALETH_HASH="54b8a5455e45b295e3a962f353ff8f1580ed106c" else - # built from 5ac09111bd0b6518365fe956e1bdb97a2db82af1 at 2018-04-05 - ETH_BINARY=eth_2018-04-05_artful - ETH_HASH="eb2d0df022753bb2b442ba73e565a9babf6828d6" + # built from d661ac4fec0aeffbedcdc195f67f5ded0c798278 at 2018-06-20 + ALETH_BINARY=aleth_2018-06-20_artful + ALETH_HASH="02e6c4b3d98299885e73f7db6c9e3fbe3d66d444" fi - wget -q -O /tmp/test/eth https://github.com/ethereum/cpp-ethereum/releases/download/solidityTester/$ETH_BINARY - test "$(shasum /tmp/test/eth)" = "$ETH_HASH /tmp/test/eth" + ALETH_PATH="/tmp/test/aleth" + wget -q -O $ALETH_PATH https://github.com/ethereum/cpp-ethereum/releases/download/solidityTester/$ALETH_BINARY + test "$(shasum $ALETH_PATH)" = "$ALETH_HASH $ALETH_PATH" sync - chmod +x /tmp/test/eth + chmod +x $ALETH_PATH sync # Otherwise we might get a "text file busy" error - ETH_PATH="/tmp/test/eth" fi } # $1: data directory # echos the PID -function run_eth() +function run_aleth() { - $ETH_PATH --test -d "$1" >/dev/null 2>&1 & + $ALETH_PATH --test -d "${WORKDIR}" >/dev/null 2>&1 & echo $! # Wait until the IPC endpoint is available. - while [ ! -S "$1"/geth.ipc ] ; do sleep 1; done + while [ ! -S "${WORKDIR}/geth.ipc" ] ; do sleep 1; done sleep 2 } +function check_aleth() { + printTask "Running IPC tests with $ALETH_PATH..." + if ! hash $ALETH_PATH 2>/dev/null; then + printError "$ALETH_PATH not found" + exit 1 + fi +} + if [ "$IPC_ENABLED" = true ]; then - download_eth - ETH_PID=$(run_eth /tmp/test) + download_aleth + check_aleth + ALETH_PID=$(run_aleth) fi progress="--show-progress" @@ -145,19 +208,15 @@ do log=--logger=JUNIT,test_suite,$log_directory/noopt_$vm.xml $testargs_no_opt fi fi - "$REPO_ROOT"/build/test/soltest $progress $log -- --testpath "$REPO_ROOT"/test "$optimize" --evm-version "$vm" $SMT_FLAGS $IPC_FLAGS --ipcpath /tmp/test/geth.ipc + "$REPO_ROOT"/build/test/soltest $progress $log -- --testpath "$REPO_ROOT"/test "$optimize" --evm-version "$vm" $SMT_FLAGS $IPC_FLAGS --ipcpath "${WORKDIR}/geth.ipc" done done -if ! wait $CMDLINE_PID +if [[ -n $CMDLINE_PID ]] && ! wait $CMDLINE_PID then printError "Commandline tests FAILED" + CMDLINE_PID= exit 1 fi -if [ "$IPC_ENABLED" = true ] -then - pkill "$ETH_PID" || true - sleep 4 - pgrep "$ETH_PID" && pkill -9 "$ETH_PID" || true -fi +cleanup diff --git a/scripts/update_bugs_by_version.py b/scripts/update_bugs_by_version.py index cbedf1a5..655ffe23 100755 --- a/scripts/update_bugs_by_version.py +++ b/scripts/update_bugs_by_version.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 # # This script is used to generate the list of bugs per compiler version # from the list of bugs. @@ -35,7 +35,7 @@ for v in versions: continue versions[v]['bugs'] += [bug['name']] -new_contents = json.dumps(versions, sort_keys=True, indent=4) +new_contents = json.dumps(versions, sort_keys=True, indent=4, separators=(',', ': ')) with open(path + '/../docs/bugs_by_version.json', 'r') as bugs_by_version: old_contents = bugs_by_version.read() with open(path + '/../docs/bugs_by_version.json', 'w') as bugs_by_version: diff --git a/solc/CommandLineInterface.cpp b/solc/CommandLineInterface.cpp index 1f04c68a..844cef90 100644 --- a/solc/CommandLineInterface.cpp +++ b/solc/CommandLineInterface.cpp @@ -80,7 +80,6 @@ static string const g_strAstJson = "ast-json"; 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_strCloneBinary = "clone-bin"; static string const g_strCombinedJson = "combined-json"; static string const g_strCompactJSON = "compact-format"; static string const g_strContracts = "contracts"; @@ -88,12 +87,11 @@ static string const g_strEVM = "evm"; static string const g_strEVM15 = "evm15"; static string const g_strEVMVersion = "evm-version"; static string const g_streWasm = "ewasm"; -static string const g_strFormal = "formal"; static string const g_strGas = "gas"; static string const g_strHelp = "help"; static string const g_strInputFile = "input-file"; static string const g_strInterface = "interface"; -static string const g_strJulia = "julia"; +static string const g_strYul = "yul"; static string const g_strLicense = "license"; static string const g_strLibraries = "libraries"; static string const g_strLink = "link"; @@ -129,14 +127,12 @@ static string const g_argAstCompactJson = g_strAstCompactJson; static string const g_argAstJson = g_strAstJson; static string const g_argBinary = g_strBinary; static string const g_argBinaryRuntime = g_strBinaryRuntime; -static string const g_argCloneBinary = g_strCloneBinary; static string const g_argCombinedJson = g_strCombinedJson; static string const g_argCompactJSON = g_strCompactJSON; -static string const g_argFormal = g_strFormal; static string const g_argGas = g_strGas; static string const g_argHelp = g_strHelp; static string const g_argInputFile = g_strInputFile; -static string const g_argJulia = g_strJulia; +static string const g_argYul = g_strYul; static string const g_argLibraries = g_strLibraries; static string const g_argLink = g_strLink; static string const g_argMachine = g_strMachine; @@ -163,7 +159,6 @@ static set<string> const g_combinedJsonArgs g_strAst, g_strBinary, g_strBinaryRuntime, - g_strCloneBinary, g_strCompactJSON, g_strInterface, g_strMetadata, @@ -215,8 +210,6 @@ static bool needsHumanTargetedStdout(po::variables_map const& _args) g_argAstJson, g_argBinary, g_argBinaryRuntime, - g_argCloneBinary, - g_argFormal, g_argMetadata, g_argNatspecUser, g_argNatspecDev, @@ -233,31 +226,21 @@ void CommandLineInterface::handleBinary(string const& _contract) if (m_args.count(g_argBinary)) { if (m_args.count(g_argOutputDir)) - createFile(m_compiler->filesystemFriendlyName(_contract) + ".bin", m_compiler->object(_contract).toHex()); + createFile(m_compiler->filesystemFriendlyName(_contract) + ".bin", objectWithLinkRefsHex(m_compiler->object(_contract))); else { cout << "Binary: " << endl; - cout << m_compiler->object(_contract).toHex() << endl; - } - } - if (m_args.count(g_argCloneBinary)) - { - if (m_args.count(g_argOutputDir)) - createFile(m_compiler->filesystemFriendlyName(_contract) + ".clone_bin", m_compiler->cloneObject(_contract).toHex()); - else - { - cout << "Clone Binary: " << endl; - cout << m_compiler->cloneObject(_contract).toHex() << endl; + cout << objectWithLinkRefsHex(m_compiler->object(_contract)) << endl; } } if (m_args.count(g_argBinaryRuntime)) { if (m_args.count(g_argOutputDir)) - createFile(m_compiler->filesystemFriendlyName(_contract) + ".bin-runtime", m_compiler->runtimeObject(_contract).toHex()); + createFile(m_compiler->filesystemFriendlyName(_contract) + ".bin-runtime", objectWithLinkRefsHex(m_compiler->runtimeObject(_contract))); else { cout << "Binary of the runtime part: " << endl; - cout << m_compiler->runtimeObject(_contract).toHex() << endl; + cout << objectWithLinkRefsHex(m_compiler->runtimeObject(_contract)) << endl; } } } @@ -278,7 +261,7 @@ void CommandLineInterface::handleBytecode(string const& _contract) { if (m_args.count(g_argOpcodes)) handleOpcode(_contract); - if (m_args.count(g_argBinary) || m_args.count(g_argCloneBinary) || m_args.count(g_argBinaryRuntime)) + if (m_args.count(g_argBinary) || m_args.count(g_argBinaryRuntime)) handleBinary(_contract); } @@ -404,14 +387,23 @@ bool CommandLineInterface::readInputFilesAndConfigureRemappings() { bool ignoreMissing = m_args.count(g_argIgnoreMissingFiles); bool addStdin = false; - if (!m_args.count(g_argInputFile)) - addStdin = true; - else + if (m_args.count(g_argInputFile)) for (string path: m_args[g_argInputFile].as<vector<string>>()) { auto eq = find(path.begin(), path.end(), '='); if (eq != path.end()) - path = string(eq + 1, path.end()); + { + if (auto r = CompilerStack::parseRemapping(path)) + { + m_remappings.emplace_back(std::move(*r)); + path = string(eq + 1, path.end()); + } + else + { + cerr << "Invalid remapping: \"" << path << "\"." << endl; + return false; + } + } else if (path == "-") addStdin = true; else @@ -421,11 +413,11 @@ bool CommandLineInterface::readInputFilesAndConfigureRemappings() { if (!ignoreMissing) { - cerr << "\"" << infile << "\" is not found" << endl; + cerr << infile << " is not found." << endl; return false; } else - cerr << "\"" << infile << "\" is not found. Skipping." << endl; + cerr << infile << " is not found. Skipping." << endl; continue; } @@ -434,22 +426,27 @@ bool CommandLineInterface::readInputFilesAndConfigureRemappings() { if (!ignoreMissing) { - cerr << "\"" << infile << "\" is not a valid file" << endl; + cerr << infile << " is not a valid file." << endl; return false; } else - cerr << "\"" << infile << "\" is not a valid file. Skipping." << endl; + cerr << infile << " is not a valid file. Skipping." << endl; continue; } - m_sourceCodes[infile.string()] = dev::readFileAsString(infile.string()); + m_sourceCodes[infile.generic_string()] = dev::readFileAsString(infile.string()); path = boost::filesystem::canonical(infile).string(); } m_allowedDirectories.push_back(boost::filesystem::path(path).remove_filename()); } if (addStdin) m_sourceCodes[g_stdinFileName] = dev::readStandardInput(); + if (m_sourceCodes.size() == 0) + { + cerr << "No input files given. If you wish to use the standard input please specify \"-\" explicitly." << endl; + return false; + } return true; } @@ -485,9 +482,23 @@ bool CommandLineInterface::parseLibraryOption(string const& _input) string addrString(lib.begin() + colon + 1, lib.end()); boost::trim(libName); boost::trim(addrString); + if (addrString.substr(0, 2) == "0x") + addrString = addrString.substr(2); + if (addrString.empty()) + { + cerr << "Empty address provided for library \"" << libName << "\": " << endl; + cerr << "Note that there should not be any whitespace after the colon." << endl; + return false; + } + else if (addrString.length() != 40) + { + cerr << "Invalid length for address for library \"" << libName << "\": " << addrString.length() << " instead of 40 characters." << endl; + return false; + } if (!passesAddressChecksum(addrString, false)) { - cerr << "Invalid checksum on library address \"" << libName << "\": " << addrString << endl; + cerr << "Invalid checksum on address for library \"" << libName << "\": " << addrString << endl; + cerr << "The correct checksum is " << dev::getChecksummedAddress(addrString) << endl; return false; } bytes binAddr = fromHex(addrString); @@ -572,7 +583,7 @@ Allowed options)", g_argLibraries.c_str(), po::value<vector<string>>()->value_name("libs"), "Direct string or file containing library addresses. Syntax: " - "<libraryName>: <address> [, or whitespace] ...\n" + "<libraryName>:<address> [, or whitespace] ...\n" "Address is interpreted as a hex string optionally prefixed by 0x." ) ( @@ -597,8 +608,8 @@ Allowed options)", "Switch to assembly mode, ignoring all options except --machine and assumes input is assembly." ) ( - g_argJulia.c_str(), - "Switch to JULIA mode, ignoring all options except --machine and assumes input is JULIA." + g_argYul.c_str(), + "Switch to Yul mode, ignoring all options except --machine and assumes input is Yul." ) ( g_argStrictAssembly.c_str(), @@ -607,7 +618,7 @@ Allowed options)", ( g_argMachine.c_str(), po::value<string>()->value_name(boost::join(g_machineArgs, ",")), - "Target machine in assembly or JULIA mode." + "Target machine in assembly or Yul mode." ) ( g_argLink.c_str(), @@ -631,13 +642,11 @@ Allowed options)", (g_argOpcodes.c_str(), "Opcodes of the contracts.") (g_argBinary.c_str(), "Binary of the contracts in hex.") (g_argBinaryRuntime.c_str(), "Binary of the runtime part of the contracts in hex.") - (g_argCloneBinary.c_str(), "Binary of the clone contracts in hex.") (g_argAbi.c_str(), "ABI specification of the contracts.") (g_argSignatureHashes.c_str(), "Function signature hashes of the contracts.") (g_argNatspecUser.c_str(), "Natspec user documentation of all contracts.") (g_argNatspecDev.c_str(), "Natspec developer documentation of all contracts.") - (g_argMetadata.c_str(), "Combined Metadata JSON whose Swarm hash is stored on-chain.") - (g_argFormal.c_str(), "Translated source suitable for formal analysis. (Deprecated)"); + (g_argMetadata.c_str(), "Combined Metadata JSON whose Swarm hash is stored on-chain."); desc.add(outputComponents); po::options_description allOptions = desc; @@ -725,7 +734,7 @@ bool CommandLineInterface::processInput() return ReadCallback::Result{false, "Not a valid file."}; auto contents = dev::readFileAsString(canonicalPath.string()); - m_sourceCodes[path.string()] = contents; + m_sourceCodes[path.generic_string()] = contents; return ReadCallback::Result{true, contents}; } catch (Exception const& _exception) @@ -741,13 +750,15 @@ bool CommandLineInterface::processInput() if (m_args.count(g_argAllowPaths)) { vector<string> paths; - for (string const& path: boost::split(paths, m_args[g_argAllowPaths].as<string>(), boost::is_any_of(","))) { + for (string const& path: boost::split(paths, m_args[g_argAllowPaths].as<string>(), 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() == ".") { + if (filesystem_path.filename() == ".") + { filesystem_path.remove_filename(); } m_allowedDirectories.push_back(filesystem_path); @@ -782,13 +793,13 @@ bool CommandLineInterface::processInput() m_evmVersion = *versionOption; } - if (m_args.count(g_argAssemble) || m_args.count(g_argStrictAssembly) || m_args.count(g_argJulia)) + if (m_args.count(g_argAssemble) || m_args.count(g_argStrictAssembly) || m_args.count(g_argYul)) { // switch to assembly mode m_onlyAssemble = true; using Input = AssemblyStack::Language; using Machine = AssemblyStack::Machine; - Input inputLanguage = m_args.count(g_argJulia) ? Input::JULIA : (m_args.count(g_argStrictAssembly) ? Input::StrictAssembly : Input::Assembly); + Input inputLanguage = m_args.count(g_argYul) ? Input::Yul : (m_args.count(g_argStrictAssembly) ? Input::StrictAssembly : Input::Assembly); Machine targetMachine = Machine::EVM; if (m_args.count(g_argMachine)) { @@ -824,7 +835,7 @@ bool CommandLineInterface::processInput() if (m_args.count(g_argMetadataLiteral) > 0) m_compiler->useMetadataLiteralSources(true); if (m_args.count(g_argInputFile)) - m_compiler->setRemappings(m_args[g_argInputFile].as<vector<string>>()); + m_compiler->setRemappings(m_remappings); for (auto const& sourceCode: m_sourceCodes) m_compiler->addSource(sourceCode.first, sourceCode.second); if (m_args.count(g_argLibraries)) @@ -911,8 +922,6 @@ void CommandLineInterface::handleCombinedJSON() contractData[g_strBinary] = m_compiler->object(contractName).toHex(); if (requests.count(g_strBinaryRuntime)) contractData[g_strBinaryRuntime] = m_compiler->runtimeObject(contractName).toHex(); - if (requests.count(g_strCloneBinary)) - contractData[g_strCloneBinary] = m_compiler->cloneObject(contractName).toHex(); if (requests.count(g_strOpcodes)) contractData[g_strOpcodes] = solidity::disassemble(m_compiler->object(contractName).bytecode); if (requests.count(g_strAsm)) @@ -985,12 +994,15 @@ void CommandLineInterface::handleAst(string const& _argStr) for (auto const& sourceCode: m_sourceCodes) asts.push_back(&m_compiler->ast(sourceCode.first)); map<ASTNode const*, eth::GasMeter::GasConsumption> gasCosts; - // FIXME: shouldn't this be done for every contract? - if (m_compiler->runtimeAssemblyItems(m_compiler->lastContractName())) - gasCosts = GasEstimator::breakToStatementLevel( - GasEstimator(m_evmVersion).structuralEstimation(*m_compiler->runtimeAssemblyItems(m_compiler->lastContractName()), asts), + for (auto const& contract : m_compiler->contractNames()) + { + auto ret = GasEstimator::breakToStatementLevel( + GasEstimator(m_evmVersion).structuralEstimation(*m_compiler->runtimeAssemblyItems(contract), asts), asts ); + for (auto const& it: ret) + gasCosts[it.first] += it.second; + } bool legacyFormat = !m_args.count(g_argAstCompactJson); if (m_args.count(g_argOutputDir)) @@ -1056,8 +1068,12 @@ bool CommandLineInterface::link() { string const& name = library.first; // Library placeholders are 40 hex digits (20 bytes) that start and end with '__'. - // This leaves 36 characters for the library name, while too short library names are - // padded on the right with '_' and too long names are truncated. + // This leaves 36 characters for the library identifier. The identifier used to + // be just the cropped or '_'-padded library name, but this changed to + // the cropped hex representation of the hash of the library name. + // We support both ways of linking here. + librariesReplacements["__" + eth::LinkerObject::libraryPlaceholder(name) + "__"] = library.second; + string replacement = "__"; for (size_t i = 0; i < placeholderSize - 4; ++i) replacement.push_back(i < name.size() ? name[i] : '_'); @@ -1087,6 +1103,11 @@ bool CommandLineInterface::link() cerr << "Reference \"" << name << "\" in file \"" << src.first << "\" still unresolved." << endl; it += placeholderSize; } + // Remove hints for resolved libraries. + for (auto const& library: m_libraries) + boost::algorithm::erase_all(src.second, "\n" + libraryPlaceholderHint(library.first)); + while (!src.second.empty() && *prev(src.second.end()) == '\n') + src.second.resize(src.second.size() - 1); } return true; } @@ -1097,7 +1118,32 @@ void CommandLineInterface::writeLinkedFiles() if (src.first == g_stdinFileName) cout << src.second << endl; else - writeFile(src.first, src.second); + { + ofstream outFile(src.first); + outFile << src.second; + if (!outFile) + { + cerr << "Could not write to file " << src.first << ". Aborting." << endl; + return; + } + } +} + +string CommandLineInterface::libraryPlaceholderHint(string const& _libraryName) +{ + return "// " + eth::LinkerObject::libraryPlaceholder(_libraryName) + " -> " + _libraryName; +} + +string CommandLineInterface::objectWithLinkRefsHex(eth::LinkerObject const& _obj) +{ + string out = _obj.toHex(); + if (!_obj.linkReferences.empty()) + { + out += "\n"; + for (auto const& linkRef: _obj.linkReferences) + out += "\n" + libraryPlaceholderHint(linkRef.second); + } + return out; } bool CommandLineInterface::assemble( @@ -1233,9 +1279,6 @@ void CommandLineInterface::outputCompilationResults() handleNatspec(true, contract); handleNatspec(false, contract); } // end of contracts iteration - - if (m_args.count(g_argFormal)) - cerr << "Support for the Why3 output was removed." << endl; } } diff --git a/solc/CommandLineInterface.h b/solc/CommandLineInterface.h index 45ec1eb5..aa49383a 100644 --- a/solc/CommandLineInterface.h +++ b/solc/CommandLineInterface.h @@ -54,6 +54,10 @@ public: private: bool link(); void writeLinkedFiles(); + /// @returns the ``// <identifier> -> name`` hint for library placeholders. + static std::string libraryPlaceholderHint(std::string const& _libraryName); + /// @returns the full object with library placeholder hints in hex. + static std::string objectWithLinkRefsHex(eth::LinkerObject const& _obj); bool assemble(AssemblyStack::Language _language, AssemblyStack::Machine _targetMachine); @@ -97,6 +101,8 @@ private: boost::program_options::variables_map m_args; /// map of input files to source code strings std::map<std::string, std::string> m_sourceCodes; + /// list of remappings + std::vector<dev::solidity::CompilerStack::Remapping> m_remappings; /// list of allowed directories to read files from std::vector<boost::filesystem::path> m_allowedDirectories; /// map of library names to addresses diff --git a/std/StandardToken.sol b/std/StandardToken.sol deleted file mode 100644 index c2fc3a66..00000000 --- a/std/StandardToken.sol +++ /dev/null @@ -1,59 +0,0 @@ -pragma solidity ^0.4.22; - -import "./Token.sol"; - -contract StandardToken is Token { - uint256 supply; - mapping (address => uint256) balance; - mapping (address => - mapping (address => uint256)) m_allowance; - - constructor(address _initialOwner, uint256 _supply) public { - supply = _supply; - balance[_initialOwner] = _supply; - } - - function balanceOf(address _account) view public returns (uint) { - return balance[_account]; - } - - function totalSupply() view public returns (uint) { - return supply; - } - - function transfer(address _to, uint256 _value) public returns (bool success) { - return doTransfer(msg.sender, _to, _value); - } - - function transferFrom(address _from, address _to, uint256 _value) public returns (bool) { - if (m_allowance[_from][msg.sender] >= _value) { - if (doTransfer(_from, _to, _value)) { - m_allowance[_from][msg.sender] -= _value; - } - return true; - } else { - return false; - } - } - - function doTransfer(address _from, address _to, uint _value) internal returns (bool success) { - if (balance[_from] >= _value && balance[_to] + _value >= balance[_to]) { - balance[_from] -= _value; - balance[_to] += _value; - emit Transfer(_from, _to, _value); - return true; - } else { - return false; - } - } - - function approve(address _spender, uint256 _value) public returns (bool success) { - m_allowance[msg.sender][_spender] = _value; - emit Approval(msg.sender, _spender, _value); - return true; - } - - function allowance(address _owner, address _spender) view public returns (uint256) { - return m_allowance[_owner][_spender]; - } -} diff --git a/std/Token.sol b/std/Token.sol deleted file mode 100644 index 7348a8f5..00000000 --- a/std/Token.sol +++ /dev/null @@ -1,13 +0,0 @@ -pragma solidity ^0.4.0; - -contract Token { - event Transfer(address indexed _from, address indexed _to, uint256 _value); - event Approval(address indexed _owner, address indexed _spender, uint256 _value); - - function totalSupply() view public returns (uint256 supply); - function balanceOf(address _owner) view public returns (uint256 balance); - function transfer(address _to, uint256 _value) public returns (bool success); - function transferFrom(address _from, address _to, uint256 _value) public returns (bool success); - function approve(address _spender, uint256 _value) public returns (bool success); - function allowance(address _owner, address _spender) view public returns (uint256 remaining); -} diff --git a/std/mortal.sol b/std/mortal.sol deleted file mode 100644 index c43f1e4f..00000000 --- a/std/mortal.sol +++ /dev/null @@ -1,10 +0,0 @@ -pragma solidity ^0.4.0; - -import "./owned.sol"; - -contract mortal is owned { - function kill() public { - if (msg.sender == owner) - selfdestruct(owner); - } -} diff --git a/std/owned.sol b/std/owned.sol deleted file mode 100644 index 75007f3e..00000000 --- a/std/owned.sol +++ /dev/null @@ -1,15 +0,0 @@ -pragma solidity ^0.4.22; - -contract owned { - address owner; - - modifier onlyowner() { - if (msg.sender == owner) { - _; - } - } - - constructor() public { - owner = msg.sender; - } -} diff --git a/std/std.sol b/std/std.sol deleted file mode 100644 index 4d65bef2..00000000 --- a/std/std.sol +++ /dev/null @@ -1,6 +0,0 @@ -pragma solidity ^0.4.0; - -import "./owned.sol"; -import "./mortal.sol"; -import "./Token.sol"; -import "./StandardToken.sol"; diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 522856cc..91c1b200 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -7,8 +7,8 @@ file(GLOB libdevcore_sources "libdevcore/*.cpp") file(GLOB libdevcore_headers "libdevcore/*.h") file(GLOB libevmasm_sources "libevmasm/*.cpp") file(GLOB libevmasm_headers "libevmasm/*.h") -file(GLOB libjulia_sources "libjulia/*.cpp") -file(GLOB libjulia_headers "libjulia/*.h") +file(GLOB libyul_sources "libyul/*.cpp") +file(GLOB libyul_headers "libyul/*.h") file(GLOB liblll_sources "liblll/*.cpp") file(GLOB liblll_headers "liblll/*.h") file(GLOB libsolidity_sources "libsolidity/*.cpp") @@ -18,10 +18,14 @@ add_executable(soltest ${sources} ${headers} ${contracts_sources} ${contracts_headers} ${libdevcore_sources} ${libdevcore_headers} ${libevmasm_sources} ${libevmasm_headers} - ${libjulia_sources} ${libjulia_headers} + ${libyul_sources} ${libyul_headers} ${liblll_sources} ${liblll_headers} ${libsolidity_sources} ${libsolidity_headers} ) target_link_libraries(soltest PRIVATE libsolc solidity lll evmasm devcore ${Boost_UNIT_TEST_FRAMEWORK_LIBRARIES}) +if (NOT Boost_USE_STATIC_LIBS) + target_compile_definitions(soltest PUBLIC -DBOOST_TEST_DYN_LINK) +endif() + add_subdirectory(tools) diff --git a/test/Common.cpp b/test/Common.cpp new file mode 100644 index 00000000..a80c55d4 --- /dev/null +++ b/test/Common.cpp @@ -0,0 +1,50 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <test/Common.h> + +#include <boost/filesystem.hpp> + +namespace fs = boost::filesystem; + +namespace dev +{ +namespace test +{ + +boost::filesystem::path discoverTestPath() +{ + auto const searchPath = + { + fs::current_path() / ".." / ".." / ".." / "test", + fs::current_path() / ".." / ".." / "test", + fs::current_path() / ".." / "test", + fs::current_path() / "test", + fs::current_path() + }; + for (auto const& basePath: searchPath) + { + fs::path syntaxTestPath = basePath / "libsolidity" / "syntaxTests"; + if (fs::exists(syntaxTestPath) && fs::is_directory(syntaxTestPath)) + return basePath; + } + return {}; +} + + +} +} diff --git a/test/Common.h b/test/Common.h new file mode 100644 index 00000000..e87faa0e --- /dev/null +++ b/test/Common.h @@ -0,0 +1,35 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see <http://www.gnu.org/licenses/>. +*/ + +#pragma once + +#include <boost/filesystem/path.hpp> + +namespace dev +{ +namespace test +{ + +/// Tries to find a path that contains the directories "libsolidity/syntaxTests" +/// and returns it if found. +/// The routine searches in the current directory, and inside the "test" directory +/// starting from the current directory and up to three levels up. +/// @returns the path of the first match or an empty path if not found. +boost::filesystem::path discoverTestPath(); + +} +} diff --git a/test/ExecutionFramework.cpp b/test/ExecutionFramework.cpp index a24f78fb..3922d5e9 100644 --- a/test/ExecutionFramework.cpp +++ b/test/ExecutionFramework.cpp @@ -85,6 +85,22 @@ std::pair<bool, string> ExecutionFramework::compareAndCreateMessage( return make_pair(false, message); } +u256 ExecutionFramework::gasLimit() const +{ + auto latestBlock = m_rpc.eth_getBlockByNumber("latest", false); + return u256(latestBlock["gasLimit"].asString()); +} + +u256 ExecutionFramework::gasPrice() const +{ + return u256(m_rpc.eth_gasPrice()); +} + +u256 ExecutionFramework::blockHash(u256 const& _blockNumber) const +{ + return u256(m_rpc.eth_getBlockByNumber(toHex(_blockNumber, HexPrefix::Add), false)["hash"].asString()); +} + void ExecutionFramework::sendMessage(bytes const& _data, bool _isCreation, u256 const& _value) { if (m_showMessages) @@ -106,9 +122,9 @@ void ExecutionFramework::sendMessage(bytes const& _data, bool _isCreation, u256 if (!_isCreation) { d.to = dev::toString(m_contractAddress); - BOOST_REQUIRE(m_rpc.eth_getCode(d.to, "latest").size() > 2); + BOOST_REQUIRE(m_rpc.eth_getCode(d.to, "pending").size() > 2); // Use eth_call to get the output - m_output = fromHex(m_rpc.eth_call(d, "latest"), WhenError::Throw); + m_output = fromHex(m_rpc.eth_call(d, "pending"), WhenError::Throw); } string txHash = m_rpc.eth_sendTransaction(d); @@ -142,6 +158,11 @@ void ExecutionFramework::sendMessage(bytes const& _data, bool _isCreation, u256 entry.data = fromHex(log.data, WhenError::Throw); m_logs.push_back(entry); } + + if (!receipt.status.empty()) + m_transactionSuccessful = (receipt.status == "1"); + else + m_transactionSuccessful = (m_gas != m_gasUsed); } void ExecutionFramework::sendEther(Address const& _to, u256 const& _value) diff --git a/test/ExecutionFramework.h b/test/ExecutionFramework.h index 4525cbf9..d6de95a3 100644 --- a/test/ExecutionFramework.h +++ b/test/ExecutionFramework.h @@ -28,7 +28,7 @@ #include <libsolidity/interface/EVMVersion.h> #include <libdevcore/FixedHash.h> -#include <libdevcore/SHA3.h> +#include <libdevcore/Keccak256.h> #include <functional> @@ -72,6 +72,7 @@ public: ) { compileAndRunWithoutCheck(_sourceCode, _value, _contractName, _arguments, _libraryAddresses); + BOOST_REQUIRE(m_transactionSuccessful); BOOST_REQUIRE(!m_output.empty()); return m_output; } @@ -146,11 +147,11 @@ public: static std::pair<bool, std::string> compareAndCreateMessage(bytes const& _result, bytes const& _expectation); - static bytes encode(bool _value) { return encode(byte(_value)); } + static bytes encode(bool _value) { return encode(uint8_t(_value)); } static bytes encode(int _value) { return encode(u256(_value)); } static bytes encode(size_t _value) { return encode(u256(_value)); } static bytes encode(char const* _value) { return encode(std::string(_value)); } - static bytes encode(byte _value) { return bytes(31, 0) + bytes{_value}; } + static bytes encode(uint8_t _value) { return bytes(31, 0) + bytes{_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. @@ -193,6 +194,13 @@ public: return encodeArgs(u256(0x20), u256(_arg.size()), _arg); } + u256 gasLimit() const; + u256 gasPrice() const; + u256 blockHash(u256 const& _blockNumber) const; + u256 const& blockNumber() const { + return m_blockNumber; + } + private: template <class CppFunction, class... Args> auto callCppAndEncodeResult(CppFunction const& _cppFunction, Args const&... _arguments) @@ -234,6 +242,7 @@ protected: unsigned m_optimizeRuns = 200; bool m_optimize = false; bool m_showMessages = false; + bool m_transactionSuccessful = true; Address m_sender; Address m_contractAddress; u256 m_blockNumber; diff --git a/test/Options.cpp b/test/Options.cpp index ff4a7c98..da08eb37 100644 --- a/test/Options.cpp +++ b/test/Options.cpp @@ -21,13 +21,17 @@ #include <test/Options.h> +#include <test/Common.h> + #include <libsolidity/interface/EVMVersion.h> #include <libsolidity/interface/Exceptions.h> #include <boost/test/framework.hpp> +#include <boost/filesystem.hpp> using namespace std; using namespace dev::test; +namespace fs = boost::filesystem; Options const& Options::get() { @@ -70,6 +74,9 @@ Options::Options() if (testPath.empty()) if (auto path = getenv("ETH_TEST_PATH")) testPath = path; + + if (testPath.empty()) + testPath = discoverTestPath(); } void Options::validate() const diff --git a/test/Options.h b/test/Options.h index 9bc69876..0e8a51da 100644 --- a/test/Options.h +++ b/test/Options.h @@ -24,6 +24,7 @@ #include <boost/test/unit_test.hpp> #include <boost/filesystem.hpp> #include <boost/version.hpp> +#include <boost/noncopyable.hpp> #include <functional> diff --git a/test/RPCSession.cpp b/test/RPCSession.cpp index f4eae865..9ac24972 100644 --- a/test/RPCSession.cpp +++ b/test/RPCSession.cpp @@ -163,6 +163,11 @@ RPCSession::TransactionReceipt RPCSession::eth_getTransactionReceipt(string cons receipt.gasUsed = result["gasUsed"].asString(); receipt.contractAddress = result["contractAddress"].asString(); receipt.blockNumber = result["blockNumber"].asString(); + if (m_receiptHasStatusField) + { + BOOST_REQUIRE(!result["status"].isNull()); + receipt.status = result["status"].asString(); + } for (auto const& log: result["logs"]) { LogEntry entry; @@ -202,6 +207,11 @@ string RPCSession::eth_getStorageRoot(string const& _address, string const& _blo return rpcCall("eth_getStorageRoot", { quote(address), quote(_blockNumber) }).asString(); } +string RPCSession::eth_gasPrice() +{ + return rpcCall("eth_gasPrice").asString(); +} + void RPCSession::personal_unlockAccount(string const& _address, string const& _password, int _duration) { BOOST_REQUIRE_MESSAGE( @@ -225,7 +235,10 @@ void RPCSession::test_setChainParams(vector<string> const& _accounts) if (test::Options::get().evmVersion() >= solidity::EVMVersion::spuriousDragon()) forks += "\"EIP158ForkBlock\": \"0x00\",\n"; if (test::Options::get().evmVersion() >= solidity::EVMVersion::byzantium()) + { forks += "\"byzantiumForkBlock\": \"0x00\",\n"; + m_receiptHasStatusField = true; + } if (test::Options::get().evmVersion() >= solidity::EVMVersion::constantinople()) forks += "\"constantinopleForkBlock\": \"0x00\",\n"; static string const c_configString = R"( @@ -244,8 +257,11 @@ void RPCSession::test_setChainParams(vector<string> const& _accounts) "timestamp": "0x00", "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000", "extraData": "0x", - "gasLimit": "0x1000000000000" - }, + "gasLimit": "0x1000000000000", + "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "nonce": "0x0000000000000042", + "difficulty": "131072" + }, "accounts": { "0000000000000000000000000000000000000001": { "wei": "1", "precompiled": { "name": "ecrecover", "linear": { "base": 3000, "word": 0 } } }, "0000000000000000000000000000000000000002": { "wei": "1", "precompiled": { "name": "sha256", "linear": { "base": 60, "word": 12 } } }, diff --git a/test/RPCSession.h b/test/RPCSession.h index 63f1dd21..6e1391b4 100644 --- a/test/RPCSession.h +++ b/test/RPCSession.h @@ -99,6 +99,8 @@ public: std::string contractAddress; std::vector<LogEntry> logEntries; std::string blockNumber; + /// note: pre-byzantium the status field will be empty + std::string status; }; static RPCSession& instance(std::string const& _path); @@ -111,6 +113,7 @@ public: std::string eth_sendTransaction(std::string const& _transaction); std::string eth_getBalance(std::string const& _address, std::string const& _blockNumber); std::string eth_getStorageRoot(std::string const& _address, std::string const& _blockNumber); + std::string eth_gasPrice(); std::string personal_newAccount(std::string const& _password); void personal_unlockAccount(std::string const& _address, std::string const& _password, int _duration); void test_setChainParams(std::vector<std::string> const& _accounts); @@ -136,6 +139,7 @@ private: unsigned m_maxMiningTime = 6000000; // 600 seconds unsigned m_sleepTime = 10; // 10 milliseconds unsigned m_successfulMineRuns = 0; + bool m_receiptHasStatusField = false; std::vector<std::string> m_accounts; }; diff --git a/test/boostTest.cpp b/test/boostTest.cpp index f16973b5..34eeaec9 100644 --- a/test/boostTest.cpp +++ b/test/boostTest.cpp @@ -28,7 +28,7 @@ #pragma warning(push) #pragma warning(disable:4535) // calling _set_se_translator requires /EHa #endif -#include <boost/test/included/unit_test.hpp> +#include <boost/test/unit_test.hpp> #if defined(_MSC_VER) #pragma warning(pop) #endif @@ -36,9 +36,30 @@ #pragma GCC diagnostic pop #include <test/Options.h> +#include <test/libsolidity/ASTJSONTest.h> #include <test/libsolidity/SyntaxTest.h> +#include <test/libyul/YulOptimizerTest.h> + +#include <boost/algorithm/string.hpp> +#include <boost/algorithm/string/predicate.hpp> +#include <boost/filesystem.hpp> using namespace boost::unit_test; +using namespace dev::solidity::test; +namespace fs = boost::filesystem; +using namespace std; + +#if BOOST_VERSION < 105900 +test_case *make_test_case( + function<void()> const& _fn, + string const& _name, + string const& /* _filename */, + size_t /* _line */ +) +{ + return make_test_case(_fn, _name); +} +#endif namespace { @@ -49,6 +70,56 @@ void removeTestSuite(std::string const& _name) assert(id != INV_TEST_UNIT_ID); master.remove(id); } + +int registerTests( + boost::unit_test::test_suite& _suite, + boost::filesystem::path const& _basepath, + boost::filesystem::path const& _path, + TestCase::TestCaseCreator _testCaseCreator +) +{ + int numTestsAdded = 0; + fs::path fullpath = _basepath / _path; + if (fs::is_directory(fullpath)) + { + test_suite* sub_suite = BOOST_TEST_SUITE(_path.filename().string()); + for (auto const& entry: boost::iterator_range<fs::directory_iterator>( + fs::directory_iterator(fullpath), + fs::directory_iterator() + )) + if (fs::is_directory(entry.path()) || TestCase::isTestFilename(entry.path().filename())) + numTestsAdded += registerTests(*sub_suite, _basepath, _path / entry.path().filename(), _testCaseCreator); + _suite.add(sub_suite); + } + else + { + static vector<unique_ptr<string>> filenames; + + filenames.emplace_back(new string(_path.string())); + _suite.add(make_test_case( + [fullpath, _testCaseCreator] + { + BOOST_REQUIRE_NO_THROW({ + try + { + stringstream errorStream; + if (!_testCaseCreator(fullpath.string())->run(errorStream)) + BOOST_ERROR("Test expectation mismatch.\n" + errorStream.str()); + } + catch (boost::exception const& _e) + { + BOOST_ERROR("Exception during extracted test: " << boost::diagnostic_information(_e)); + } + }); + }, + _path.stem().string(), + *filenames.back(), + 0 + )); + numTestsAdded = 1; + } + return numTestsAdded; +} } test_suite* init_unit_test_suite( int /*argc*/, char* /*argv*/[] ) @@ -56,11 +127,31 @@ test_suite* init_unit_test_suite( int /*argc*/, char* /*argv*/[] ) master_test_suite_t& master = framework::master_test_suite(); master.p_name.value = "SolidityTests"; dev::test::Options::get().validate(); - solAssert(dev::solidity::test::SyntaxTest::registerTests( + solAssert(registerTests( master, dev::test::Options::get().testPath / "libsolidity", - "syntaxTests" + "syntaxTests", + SyntaxTest::create ) > 0, "no syntax tests found"); + solAssert(registerTests( + master, + dev::test::Options::get().testPath / "libsolidity", + "ASTJSON", + ASTJSONTest::create + ) > 0, "no JSON AST tests found"); + solAssert(registerTests( + master, + dev::test::Options::get().testPath / "libyul", + "yulOptimizerTests", + dev::yul::test::YulOptimizerTest::create + ) > 0, "no Yul Optimizer tests found"); + if (!dev::test::Options::get().disableSMT) + solAssert(registerTests( + master, + dev::test::Options::get().testPath / "libsolidity", + "smtCheckerTests", + SyntaxTest::create + ) > 0, "no SMT checker tests found"); if (dev::test::Options::get().disableIPC) { for (auto suite: { @@ -83,3 +174,18 @@ test_suite* init_unit_test_suite( int /*argc*/, char* /*argv*/[] ) return 0; } + +// BOOST_TEST_DYN_LINK should be defined if user want to link against shared boost test library +#ifdef BOOST_TEST_DYN_LINK + +// Because we want to have customized initialization function and support shared boost libraries at the same time, +// we are forced to customize the entry point. +// see: https://www.boost.org/doc/libs/1_67_0/libs/test/doc/html/boost_test/adv_scenarios/shared_lib_customizations/init_func.html + +int main(int argc, char* argv[]) +{ + auto init_unit_test = []() -> bool { init_unit_test_suite(0, nullptr); return true; }; + return boost::unit_test::unit_test_main(init_unit_test, argc, argv); +} + +#endif diff --git a/test/cmdlineErrorReports/too_long_line.sol b/test/cmdlineErrorReports/too_long_line.sol new file mode 100644 index 00000000..7df1057a --- /dev/null +++ b/test/cmdlineErrorReports/too_long_line.sol @@ -0,0 +1,4 @@ +contract C { + function ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff(announcementType Type, string Announcement, string Link, bool Oppositable, string _str, uint256 _uint, address _addr) onlyOwner external { +} +} diff --git a/test/cmdlineErrorReports/too_long_line.sol.ref b/test/cmdlineErrorReports/too_long_line.sol.ref new file mode 100644 index 00000000..55cd1935 --- /dev/null +++ b/test/cmdlineErrorReports/too_long_line.sol.ref @@ -0,0 +1,6 @@ +too_long_line.sol:1:1: Warning: Source file does not specify required compiler version! +contract C { +^ (Relevant source part starts here and spans across multiple lines). +too_long_line.sol:2:164: Error: Identifier not found or not unique. + ... ffffffffffffffffffffffffffffffffff(announcementType Type, string Announcement, string ... + ^--------------^ diff --git a/test/cmdlineErrorReports/too_long_line_both_sides_short.sol b/test/cmdlineErrorReports/too_long_line_both_sides_short.sol new file mode 100644 index 00000000..062f0292 --- /dev/null +++ b/test/cmdlineErrorReports/too_long_line_both_sides_short.sol @@ -0,0 +1,5 @@ +contract C { + function f(announcementTypeXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX Type, + string Announcement, string Link, bool Oppositable, string _str, uint256 _uint, address _addr) onlyOwner external { +} +} diff --git a/test/cmdlineErrorReports/too_long_line_both_sides_short.sol.ref b/test/cmdlineErrorReports/too_long_line_both_sides_short.sol.ref new file mode 100644 index 00000000..9a5ebfba --- /dev/null +++ b/test/cmdlineErrorReports/too_long_line_both_sides_short.sol.ref @@ -0,0 +1,6 @@ +too_long_line_both_sides_short.sol:1:1: Warning: Source file does not specify required compiler version! +contract C { +^ (Relevant source part starts here and spans across multiple lines). +too_long_line_both_sides_short.sol:2:15: Error: Identifier not found or not unique. + function f(announcementTypeXXXXXXXXXXXXXXXXXXX ... XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX Type, + ^-------------------------------------------------------------------------^ diff --git a/test/cmdlineErrorReports/too_long_line_edge_in.sol b/test/cmdlineErrorReports/too_long_line_edge_in.sol new file mode 100644 index 00000000..6f181c83 --- /dev/null +++ b/test/cmdlineErrorReports/too_long_line_edge_in.sol @@ -0,0 +1,4 @@ +contract C { + function ffffffffffffffffffffff(announcementTypeTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTT Ty, string A) onlyOwner external { +} +} diff --git a/test/cmdlineErrorReports/too_long_line_edge_in.sol.ref b/test/cmdlineErrorReports/too_long_line_edge_in.sol.ref new file mode 100644 index 00000000..ad3b7805 --- /dev/null +++ b/test/cmdlineErrorReports/too_long_line_edge_in.sol.ref @@ -0,0 +1,6 @@ +too_long_line_edge_in.sol:1:1: Warning: Source file does not specify required compiler version! +contract C { +^ (Relevant source part starts here and spans across multiple lines). +too_long_line_edge_in.sol:2:36: Error: Identifier not found or not unique. + function ffffffffffffffffffffff(announcementTypeTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTT Ty, string A) onlyOwner external { + ^----------------------------------------------------------------------------------------------^ diff --git a/test/cmdlineErrorReports/too_long_line_edge_out.sol b/test/cmdlineErrorReports/too_long_line_edge_out.sol new file mode 100644 index 00000000..29d3cee6 --- /dev/null +++ b/test/cmdlineErrorReports/too_long_line_edge_out.sol @@ -0,0 +1,4 @@ +contract C { + function fffffffffffffffffffffff(announcementTypeTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTT Typ, string A) onlyOwner external { +} +} diff --git a/test/cmdlineErrorReports/too_long_line_edge_out.sol.ref b/test/cmdlineErrorReports/too_long_line_edge_out.sol.ref new file mode 100644 index 00000000..d8495c11 --- /dev/null +++ b/test/cmdlineErrorReports/too_long_line_edge_out.sol.ref @@ -0,0 +1,6 @@ +too_long_line_edge_out.sol:1:1: Warning: Source file does not specify required compiler version! +contract C { +^ (Relevant source part starts here and spans across multiple lines). +too_long_line_edge_out.sol:2:37: Error: Identifier not found or not unique. + ... function fffffffffffffffffffffff(announcementTypeTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTT Typ, string A) onlyOwner external ... + ^----------------------------------------------------------------------------------------------^ diff --git a/test/cmdlineErrorReports/too_long_line_left_short.sol b/test/cmdlineErrorReports/too_long_line_left_short.sol new file mode 100644 index 00000000..2accfcce --- /dev/null +++ b/test/cmdlineErrorReports/too_long_line_left_short.sol @@ -0,0 +1,4 @@ +contract C { + function f(announcementType Type, string Announcement, string Link, bool Oppositable, string _str, uint256 _uint, address _addr) onlyOwner external { +} +} diff --git a/test/cmdlineErrorReports/too_long_line_left_short.sol.ref b/test/cmdlineErrorReports/too_long_line_left_short.sol.ref new file mode 100644 index 00000000..00b6be5c --- /dev/null +++ b/test/cmdlineErrorReports/too_long_line_left_short.sol.ref @@ -0,0 +1,6 @@ +too_long_line_left_short.sol:1:1: Warning: Source file does not specify required compiler version! +contract C { +^ (Relevant source part starts here and spans across multiple lines). +too_long_line_left_short.sol:2:15: Error: Identifier not found or not unique. + function f(announcementType Type, string Announcement, string ... + ^--------------^ diff --git a/test/cmdlineErrorReports/too_long_line_right_short.sol b/test/cmdlineErrorReports/too_long_line_right_short.sol new file mode 100644 index 00000000..936b3961 --- /dev/null +++ b/test/cmdlineErrorReports/too_long_line_right_short.sol @@ -0,0 +1,5 @@ +contract C { + function ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff(announcementType Type, + string Announcement, string Link, bool Oppositable, string _str, uint256 _uint, address _addr) onlyOwner external { +} +} diff --git a/test/cmdlineErrorReports/too_long_line_right_short.sol.ref b/test/cmdlineErrorReports/too_long_line_right_short.sol.ref new file mode 100644 index 00000000..88072d95 --- /dev/null +++ b/test/cmdlineErrorReports/too_long_line_right_short.sol.ref @@ -0,0 +1,6 @@ +too_long_line_right_short.sol:1:1: Warning: Source file does not specify required compiler version! +contract C { +^ (Relevant source part starts here and spans across multiple lines). +too_long_line_right_short.sol:2:164: Error: Identifier not found or not unique. + ... ffffffffffffffffffffffffffffffffff(announcementType Type, + ^--------------^ diff --git a/test/cmdlineTests.sh b/test/cmdlineTests.sh index 1137c7b0..b0b545f3 100755 --- a/test/cmdlineTests.sh +++ b/test/cmdlineTests.sh @@ -32,33 +32,59 @@ REPO_ROOT=$(cd $(dirname "$0")/.. && pwd) echo $REPO_ROOT SOLC="$REPO_ROOT/build/solc/solc" -FULLARGS="--optimize --ignore-missing --combined-json abi,asm,ast,bin,bin-runtime,clone-bin,compact-format,devdoc,hashes,interface,metadata,opcodes,srcmap,srcmap-runtime,userdoc" +FULLARGS="--optimize --ignore-missing --combined-json abi,asm,ast,bin,bin-runtime,compact-format,devdoc,hashes,interface,metadata,opcodes,srcmap,srcmap-runtime,userdoc" echo "Checking that the bug list is up to date..." "$REPO_ROOT"/scripts/update_bugs_by_version.py -echo "Checking that StandardToken.sol, owned.sol and mortal.sol produce bytecode..." -output=$("$REPO_ROOT"/build/solc/solc --bin "$REPO_ROOT"/std/*.sol 2>/dev/null | grep "ffff" | wc -l) -test "${output//[[:blank:]]/}" = "3" +if [ "$CIRCLECI" ] +then + function printTask() { echo "$(tput bold)$(tput setaf 2)$1$(tput setaf 7)"; } + function printError() { echo "$(tput setaf 1)$1$(tput setaf 7)"; } +else + function printTask() { echo "$(tput bold)$(tput setaf 2)$1$(tput sgr0)"; } + function printError() { echo "$(tput setaf 1)$1$(tput sgr0)"; } +fi -function printTask() { echo "$(tput bold)$(tput setaf 2)$1$(tput sgr0)"; } - -function printError() { echo "$(tput setaf 1)$1$(tput sgr0)"; } function compileFull() { + local expected_exit_code=0 + local expect_output=0 + if [[ $1 = '-e' ]] + then + expected_exit_code=1 + expect_output=1 + shift; + fi + if [[ $1 = '-w' ]] + then + expect_output=1 + shift; + fi + local files="$*" - local output failed + local output + + local stderr_path=$(mktemp) set +e - output=$( ("$SOLC" $FULLARGS $files) 2>&1 ) - failed=$? + "$SOLC" $FULLARGS $files >/dev/null 2>"$stderr_path" + local exit_code=$? + local errors=$(grep -v -E 'Warning: This is a pre-release compiler version|Warning: Experimental features are turned on|pragma experimental ABIEncoderV2|\^-------------------------------\^' < "$stderr_path") set -e + rm "$stderr_path" - if [ $failed -ne 0 ] + if [[ \ + "$exit_code" -ne "$expected_exit_code" || \ + ( $expect_output -eq 0 && -n "$errors" ) || \ + ( $expect_output -ne 0 && -z "$errors" ) \ + ]] then - printError "Compilation failed on:" - echo "$output" + printError "Unexpected compilation result:" + printError "Expected failure: $expected_exit_code - Expected warning / error output: $expect_output" + printError "Was failure: $exit_code" + echo "$errors" printError "While calling:" echo "\"$SOLC\" $FULLARGS $files" printError "Inside directory:" @@ -67,22 +93,6 @@ function compileFull() fi } -function compileWithoutWarning() -{ - local files="$*" - local output failed - - set +e - output=$("$SOLC" $files 2>&1) - failed=$? - # Remove the pre-release warning from the compiler output - output=$(echo "$output" | grep -v 'pre-release') - echo "$output" - set -e - - test -z "$output" -a "$failed" -eq 0 -} - printTask "Testing unknown options..." ( set +e @@ -98,6 +108,73 @@ printTask "Testing unknown options..." fi ) +# General helper function for testing SOLC behaviour, based on file name, compile opts, exit code, stdout and stderr. +# An failure is expected. +test_solc_file_input_failures() { + local filename="${1}" + local solc_args="${2}" + local stdout_expected="${3}" + local stderr_expected="${4}" + local stdout_path=`mktemp` + local stderr_path=`mktemp` + + set +e + "$SOLC" "${filename}" ${solc_args} 1>$stdout_path 2>$stderr_path + exitCode=$? + set -e + + sed -i -e '/^Warning: This is a pre-release compiler version, please do not use it in production./d' "$stderr_path" + sed -i -e 's/ \?Consider adding "pragma .*$//' "$stderr_path" + + if [[ $exitCode -eq 0 ]]; then + printError "Incorrect exit code. Expected failure (non-zero) but got success (0)." + rm -f $stdout_path $stderr_path + exit 1 + fi + + if [[ "$(cat $stdout_path)" != "${stdout_expected}" ]]; then + printError "Incorrect output on stderr received. Expected:" + echo -e "${stdout_expected}" + + printError "But got:" + cat $stdout_path + rm -f $stdout_path $stderr_path + exit 1 + fi + + if [[ "$(cat $stderr_path)" != "${stderr_expected}" ]]; then + printError "Incorrect output on stderr received. Expected:" + echo -e "${stderr_expected}" + + printError "But got:" + cat $stderr_path + rm -f $stdout_path $stderr_path + exit 1 + fi + + rm -f $stdout_path $stderr_path +} + +printTask "Testing passing files that are not found..." +test_solc_file_input_failures "file_not_found.sol" "" "" "\"file_not_found.sol\" is not found." + +printTask "Testing passing files that are not files..." +test_solc_file_input_failures "." "" "" "\".\" is not a valid file." + +printTask "Testing passing empty remappings..." +test_solc_file_input_failures "${0}" "=/some/remapping/target" "" "Invalid remapping: \"=/some/remapping/target\"." +test_solc_file_input_failures "${0}" "ctx:=/some/remapping/target" "" "Invalid remapping: \"ctx:=/some/remapping/target\"." + +printTask "Testing passing location printing..." +( +cd "$REPO_ROOT"/test/cmdlineErrorReports/ +for file in *.sol +do + ret=`cat $file.ref` + test_solc_file_input_failures "$file" "" "" "$ret" +done +) + printTask "Compiling various other contracts and libraries..." ( cd "$REPO_ROOT"/test/compilationTests/ @@ -107,64 +184,129 @@ do then echo " - $dir" cd "$dir" - compileFull *.sol */*.sol + compileFull -w *.sol */*.sol cd .. fi done ) -printTask "Compiling all files in std and examples..." - -for f in "$REPO_ROOT"/std/*.sol -do - echo "$f" - compileWithoutWarning "$f" -done - printTask "Compiling all examples from the documentation..." -TMPDIR=$(mktemp -d) +SOLTMPDIR=$(mktemp -d) ( set -e cd "$REPO_ROOT" REPO_ROOT=$(pwd) # make it absolute - cd "$TMPDIR" + cd "$SOLTMPDIR" "$REPO_ROOT"/scripts/isolate_tests.py "$REPO_ROOT"/docs/ docs for f in *.sol do + # The contributors guide uses syntax tests, but we cannot + # really handle them here. + if grep -E 'DeclarationError:|// ----' "$f" >/dev/null + then + continue + fi echo "$f" - compileFull "$TMPDIR/$f" + opts='' + # We expect errors if explicitly stated, or if imports + # are used (in the style guide) + if grep -E "This will not compile|import \"" "$f" >/dev/null + then + opts="-e" + fi + if grep "This will report a warning" "$f" >/dev/null + then + opts="$opts -w" + fi + compileFull $opts "$SOLTMPDIR/$f" done ) -rm -rf "$TMPDIR" +rm -rf "$SOLTMPDIR" echo "Done." printTask "Testing library checksum..." -echo '' | "$SOLC" --link --libraries a:0x90f20564390eAe531E810af625A22f51385Cd222 -! echo '' | "$SOLC" --link --libraries a:0x80f20564390eAe531E810af625A22f51385Cd222 2>/dev/null +echo '' | "$SOLC" - --link --libraries a:0x90f20564390eAe531E810af625A22f51385Cd222 >/dev/null +! echo '' | "$SOLC" - --link --libraries a:0x80f20564390eAe531E810af625A22f51385Cd222 &>/dev/null printTask "Testing long library names..." -echo '' | "$SOLC" --link --libraries aveeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeerylonglibraryname:0x90f20564390eAe531E810af625A22f51385Cd222 +echo '' | "$SOLC" - --link --libraries aveeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeerylonglibraryname:0x90f20564390eAe531E810af625A22f51385Cd222 >/dev/null + +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 + # 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 + # Now the placeholder and explanation should be gone. + grep -q -v '[/_]' C.bin +) +rm -rf "$SOLTMPDIR" -printTask "Testing overwriting files" -TMPDIR=$(mktemp -d) +printTask "Testing overwriting files..." +SOLTMPDIR=$(mktemp -d) ( set -e # First time it works - echo 'contract C {} ' | "$SOLC" --bin -o "$TMPDIR/non-existing-stuff-to-create" 2>/dev/null + echo 'contract C {} ' | "$SOLC" - --bin -o "$SOLTMPDIR/non-existing-stuff-to-create" 2>/dev/null # Second time it fails - ! echo 'contract C {} ' | "$SOLC" --bin -o "$TMPDIR/non-existing-stuff-to-create" 2>/dev/null + ! echo 'contract C {} ' | "$SOLC" - --bin -o "$SOLTMPDIR/non-existing-stuff-to-create" 2>/dev/null # Unless we force - echo 'contract C {} ' | "$SOLC" --overwrite --bin -o "$TMPDIR/non-existing-stuff-to-create" 2>/dev/null + echo 'contract C {} ' | "$SOLC" - --overwrite --bin -o "$SOLTMPDIR/non-existing-stuff-to-create" 2>/dev/null +) +rm -rf "$SOLTMPDIR" + +printTask "Testing assemble, yul, strict-assembly..." +echo '{}' | "$SOLC" - --assemble &>/dev/null +echo '{}' | "$SOLC" - --yul &>/dev/null +echo '{}' | "$SOLC" - --strict-assembly &>/dev/null + +printTask "Testing standard input..." +SOLTMPDIR=$(mktemp -d) +( + set +e + 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: $STDERR" + exit 1 + fi + + set +e + output=$(echo 'contract C {} ' | "$SOLC" - --bin 2>/dev/null | grep -q "<stdin>:C") + result=$? + set -e + + # The contract should be compiled + if [[ "$result" != 0 ]] ; then + exit 1 + fi + + # This should not fail + set +e + output=$(echo '' | "$SOLC" --ast - 2>/dev/null) + set -e + if [[ $? != 0 ]] ; then + exit 1 + fi ) -rm -rf "$TMPDIR" printTask "Testing soljson via the fuzzer..." -TMPDIR=$(mktemp -d) +SOLTMPDIR=$(mktemp -d) ( set -e cd "$REPO_ROOT" REPO_ROOT=$(pwd) # make it absolute - cd "$TMPDIR" + cd "$SOLTMPDIR" "$REPO_ROOT"/scripts/isolate_tests.py "$REPO_ROOT"/test/ "$REPO_ROOT"/scripts/isolate_tests.py "$REPO_ROOT"/docs/ docs for f in *.sol @@ -186,5 +328,5 @@ TMPDIR=$(mktemp -d) set -e done ) -rm -rf "$TMPDIR" +rm -rf "$SOLTMPDIR" echo "Commandline tests successful." diff --git a/test/compilationTests/MultiSigWallet/Factory.sol b/test/compilationTests/MultiSigWallet/Factory.sol index f1be6884..f37d230f 100644 --- a/test/compilationTests/MultiSigWallet/Factory.sol +++ b/test/compilationTests/MultiSigWallet/Factory.sol @@ -10,7 +10,7 @@ contract Factory { /// @return Returns number of instantiations by creator. function getInstantiationCount(address creator) public - constant + view returns (uint) { return instantiations[creator].length; @@ -23,6 +23,6 @@ contract Factory { { isInstantiation[instantiation] = true; instantiations[msg.sender].push(instantiation); - ContractInstantiation(msg.sender, instantiation); + emit ContractInstantiation(msg.sender, instantiation); } } diff --git a/test/compilationTests/MultiSigWallet/MultiSigWallet.sol b/test/compilationTests/MultiSigWallet/MultiSigWallet.sol index a6f67c7a..78194b7b 100644 --- a/test/compilationTests/MultiSigWallet/MultiSigWallet.sol +++ b/test/compilationTests/MultiSigWallet/MultiSigWallet.sol @@ -1,4 +1,4 @@ -pragma solidity ^0.4.4; +pragma solidity >=0.0; /// @title Multisignature wallet - Allows multiple parties to agree on transactions before execution. @@ -33,49 +33,49 @@ contract MultiSigWallet { modifier onlyWallet() { if (msg.sender != address(this)) - throw; + revert(); _; } modifier ownerDoesNotExist(address owner) { if (isOwner[owner]) - throw; + revert(); _; } modifier ownerExists(address owner) { if (!isOwner[owner]) - throw; + revert(); _; } modifier transactionExists(uint transactionId) { - if (transactions[transactionId].destination == 0) - throw; + if (transactions[transactionId].destination == address(0)) + revert(); _; } modifier confirmed(uint transactionId, address owner) { if (!confirmations[transactionId][owner]) - throw; + revert(); _; } modifier notConfirmed(uint transactionId, address owner) { if (confirmations[transactionId][owner]) - throw; + revert(); _; } modifier notExecuted(uint transactionId) { if (transactions[transactionId].executed) - throw; + revert(); _; } modifier notNull(address _address) { - if (_address == 0) - throw; + if (_address == address(0)) + revert(); _; } @@ -84,16 +84,17 @@ contract MultiSigWallet { || _required > ownerCount || _required == 0 || ownerCount == 0) - throw; + revert(); _; } /// @dev Fallback function allows to deposit ether. function() + external payable { if (msg.value > 0) - Deposit(msg.sender, msg.value); + emit Deposit(msg.sender, msg.value); } /* @@ -102,13 +103,13 @@ contract MultiSigWallet { /// @dev Contract constructor sets initial owners and required number of confirmations. /// @param _owners List of initial owners. /// @param _required Number of required confirmations. - function MultiSigWallet(address[] _owners, uint _required) + constructor(address[] memory _owners, uint _required) public validRequirement(_owners.length, _required) { for (uint i=0; i<_owners.length; i++) { - if (isOwner[_owners[i]] || _owners[i] == 0) - throw; + if (isOwner[_owners[i]] || _owners[i] == address(0)) + revert(); isOwner[_owners[i]] = true; } owners = _owners; @@ -126,7 +127,7 @@ contract MultiSigWallet { { isOwner[owner] = true; owners.push(owner); - OwnerAddition(owner); + emit OwnerAddition(owner); } /// @dev Allows to remove an owner. Transaction has to be sent by wallet. @@ -145,7 +146,7 @@ contract MultiSigWallet { owners.length -= 1; if (required > owners.length) changeRequirement(owners.length); - OwnerRemoval(owner); + emit OwnerRemoval(owner); } /// @dev Allows to replace an owner with a new owner. Transaction has to be sent by wallet. @@ -164,8 +165,8 @@ contract MultiSigWallet { } isOwner[owner] = false; isOwner[newOwner] = true; - OwnerRemoval(owner); - OwnerAddition(newOwner); + emit OwnerRemoval(owner); + emit OwnerAddition(newOwner); } /// @dev Allows to change the number of required confirmations. Transaction has to be sent by wallet. @@ -176,7 +177,7 @@ contract MultiSigWallet { validRequirement(owners.length, _required) { required = _required; - RequirementChange(_required); + emit RequirementChange(_required); } /// @dev Allows an owner to submit and confirm a transaction. @@ -184,7 +185,7 @@ contract MultiSigWallet { /// @param value Transaction ether value. /// @param data Transaction data payload. /// @return Returns transaction ID. - function submitTransaction(address destination, uint value, bytes data) + function submitTransaction(address destination, uint value, bytes memory data) public returns (uint transactionId) { @@ -201,7 +202,7 @@ contract MultiSigWallet { notConfirmed(transactionId, msg.sender) { confirmations[transactionId][msg.sender] = true; - Confirmation(msg.sender, transactionId); + emit Confirmation(msg.sender, transactionId); executeTransaction(transactionId); } @@ -214,7 +215,7 @@ contract MultiSigWallet { notExecuted(transactionId) { confirmations[transactionId][msg.sender] = false; - Revocation(msg.sender, transactionId); + emit Revocation(msg.sender, transactionId); } /// @dev Allows anyone to execute a confirmed transaction. @@ -224,14 +225,12 @@ contract MultiSigWallet { notExecuted(transactionId) { if (isConfirmed(transactionId)) { - Transaction tx = transactions[transactionId]; - tx.executed = true; - if (tx.destination.call.value(tx.value)(tx.data)) - Execution(transactionId); - else { - ExecutionFailure(transactionId); - tx.executed = false; - } + Transaction storage tx = transactions[transactionId]; + (tx.executed,) = tx.destination.call.value(tx.value)(tx.data); + if (tx.executed) + emit Execution(transactionId); + else + emit ExecutionFailure(transactionId); } } @@ -240,7 +239,7 @@ contract MultiSigWallet { /// @return Confirmation status. function isConfirmed(uint transactionId) public - constant + view returns (bool) { uint count = 0; @@ -260,7 +259,7 @@ contract MultiSigWallet { /// @param value Transaction ether value. /// @param data Transaction data payload. /// @return Returns transaction ID. - function addTransaction(address destination, uint value, bytes data) + function addTransaction(address destination, uint value, bytes memory data) internal notNull(destination) returns (uint transactionId) @@ -273,7 +272,7 @@ contract MultiSigWallet { executed: false }); transactionCount += 1; - Submission(transactionId); + emit Submission(transactionId); } /* @@ -284,7 +283,7 @@ contract MultiSigWallet { /// @return Number of confirmations. function getConfirmationCount(uint transactionId) public - constant + view returns (uint count) { for (uint i=0; i<owners.length; i++) @@ -298,7 +297,7 @@ contract MultiSigWallet { /// @return Total number of transactions after filters are applied. function getTransactionCount(bool pending, bool executed) public - constant + view returns (uint count) { for (uint i=0; i<transactionCount; i++) @@ -311,8 +310,8 @@ contract MultiSigWallet { /// @return List of owner addresses. function getOwners() public - constant - returns (address[]) + view + returns (address[] memory) { return owners; } @@ -322,8 +321,8 @@ contract MultiSigWallet { /// @return Returns array of owner addresses. function getConfirmations(uint transactionId) public - constant - returns (address[] _confirmations) + view + returns (address[] memory _confirmations) { address[] memory confirmationsTemp = new address[](owners.length); uint count = 0; @@ -346,8 +345,8 @@ contract MultiSigWallet { /// @return Returns array of transaction IDs. function getTransactionIds(uint from, uint to, bool pending, bool executed) public - constant - returns (uint[] _transactionIds) + view + returns (uint[] memory _transactionIds) { uint[] memory transactionIdsTemp = new uint[](transactionCount); uint count = 0; diff --git a/test/compilationTests/MultiSigWallet/MultiSigWalletFactory.sol b/test/compilationTests/MultiSigWallet/MultiSigWalletFactory.sol index cb58ab1c..00c165ea 100644 --- a/test/compilationTests/MultiSigWallet/MultiSigWalletFactory.sol +++ b/test/compilationTests/MultiSigWallet/MultiSigWalletFactory.sol @@ -1,4 +1,4 @@ -pragma solidity ^0.4.4; +pragma solidity >=0.0; import "Factory.sol"; import "MultiSigWallet.sol"; @@ -11,11 +11,11 @@ contract MultiSigWalletFactory is Factory { /// @param _owners List of initial owners. /// @param _required Number of required confirmations. /// @return Returns wallet address. - function create(address[] _owners, uint _required) + function create(address[] memory _owners, uint _required) public returns (address wallet) { - wallet = new MultiSigWallet(_owners, _required); + wallet = address(new MultiSigWallet(_owners, _required)); register(wallet); } } diff --git a/test/compilationTests/MultiSigWallet/MultiSigWalletWithDailyLimit.sol b/test/compilationTests/MultiSigWallet/MultiSigWalletWithDailyLimit.sol index 024d3ef4..fc0a3bc5 100644 --- a/test/compilationTests/MultiSigWallet/MultiSigWalletWithDailyLimit.sol +++ b/test/compilationTests/MultiSigWallet/MultiSigWalletWithDailyLimit.sol @@ -1,4 +1,4 @@ -pragma solidity ^0.4.4; +pragma solidity >=0.0; import "MultiSigWallet.sol"; @@ -19,7 +19,7 @@ contract MultiSigWalletWithDailyLimit is MultiSigWallet { /// @param _owners List of initial owners. /// @param _required Number of required confirmations. /// @param _dailyLimit Amount in wei, which can be withdrawn without confirmations on a daily basis. - function MultiSigWalletWithDailyLimit(address[] _owners, uint _required, uint _dailyLimit) + constructor(address[] memory _owners, uint _required, uint _dailyLimit) public MultiSigWallet(_owners, _required) { @@ -33,7 +33,7 @@ contract MultiSigWalletWithDailyLimit is MultiSigWallet { onlyWallet { dailyLimit = _dailyLimit; - DailyLimitChange(_dailyLimit); + emit DailyLimitChange(_dailyLimit); } /// @dev Allows anyone to execute a confirmed transaction or ether withdraws until daily limit is reached. @@ -42,17 +42,16 @@ contract MultiSigWalletWithDailyLimit is MultiSigWallet { public notExecuted(transactionId) { - Transaction tx = transactions[transactionId]; + Transaction storage tx = transactions[transactionId]; bool confirmed = isConfirmed(transactionId); if (confirmed || tx.data.length == 0 && isUnderLimit(tx.value)) { - tx.executed = true; if (!confirmed) spentToday += tx.value; - if (tx.destination.call.value(tx.value)(tx.data)) - Execution(transactionId); + (tx.executed,) = tx.destination.call.value(tx.value)(tx.data); + if (tx.executed) + emit Execution(transactionId); else { - ExecutionFailure(transactionId); - tx.executed = false; + emit ExecutionFailure(transactionId); if (!confirmed) spentToday -= tx.value; } @@ -85,7 +84,7 @@ contract MultiSigWalletWithDailyLimit is MultiSigWallet { /// @return Returns amount. function calcMaxWithdraw() public - constant + view returns (uint) { if (now > lastDay + 24 hours) diff --git a/test/compilationTests/MultiSigWallet/MultiSigWalletWithDailyLimitFactory.sol b/test/compilationTests/MultiSigWallet/MultiSigWalletWithDailyLimitFactory.sol index 8a2efa32..4a038864 100644 --- a/test/compilationTests/MultiSigWallet/MultiSigWalletWithDailyLimitFactory.sol +++ b/test/compilationTests/MultiSigWallet/MultiSigWalletWithDailyLimitFactory.sol @@ -1,4 +1,4 @@ -pragma solidity ^0.4.4; +pragma solidity >=0.0; import "Factory.sol"; import "MultiSigWalletWithDailyLimit.sol"; @@ -12,11 +12,11 @@ contract MultiSigWalletWithDailyLimitFactory is Factory { /// @param _required Number of required confirmations. /// @param _dailyLimit Amount in wei, which can be withdrawn without confirmations on a daily basis. /// @return Returns wallet address. - function create(address[] _owners, uint _required, uint _dailyLimit) + function create(address[] memory _owners, uint _required, uint _dailyLimit) public returns (address wallet) { - wallet = new MultiSigWalletWithDailyLimit(_owners, _required, _dailyLimit); + wallet = address(new MultiSigWalletWithDailyLimit(_owners, _required, _dailyLimit)); register(wallet); } } diff --git a/test/compilationTests/MultiSigWallet/TestToken.sol b/test/compilationTests/MultiSigWallet/TestToken.sol index 0f6cd20e..c9dd64f7 100644 --- a/test/compilationTests/MultiSigWallet/TestToken.sol +++ b/test/compilationTests/MultiSigWallet/TestToken.sol @@ -1,4 +1,4 @@ -pragma solidity ^0.4.4; +pragma solidity >=0.0; /// @title Test token contract - Allows testing of token transfers with multisig wallet. @@ -27,11 +27,11 @@ contract TestToken { returns (bool success) { if (balances[msg.sender] < _value) { - throw; + revert(); } balances[msg.sender] -= _value; balances[_to] += _value; - Transfer(msg.sender, _to, _value); + emit Transfer(msg.sender, _to, _value); return true; } @@ -40,12 +40,12 @@ contract TestToken { returns (bool success) { if (balances[_from] < _value || allowed[_from][msg.sender] < _value) { - throw; + revert(); } balances[_to] += _value; balances[_from] -= _value; allowed[_from][msg.sender] -= _value; - Transfer(_from, _to, _value); + emit Transfer(_from, _to, _value); return true; } @@ -54,12 +54,12 @@ contract TestToken { returns (bool success) { allowed[msg.sender][_spender] = _value; - Approval(msg.sender, _spender, _value); + emit Approval(msg.sender, _spender, _value); return true; } function allowance(address _owner, address _spender) - constant + view public returns (uint256 remaining) { @@ -67,7 +67,7 @@ contract TestToken { } function balanceOf(address _owner) - constant + view public returns (uint256 balance) { diff --git a/test/compilationTests/corion/announcementTypes.sol b/test/compilationTests/corion/announcementTypes.sol index f31d580e..1619e182 100644 --- a/test/compilationTests/corion/announcementTypes.sol +++ b/test/compilationTests/corion/announcementTypes.sol @@ -1,35 +1,35 @@ -pragma solidity ^0.4.11;
-
-contract announcementTypes {
-
- enum announcementType {
- /*
- type of announcements
- */
- newModule,
- dropModule,
- replaceModule,
- replaceModuleHandler,
- question,
- transactionFeeRate,
- transactionFeeMin,
- transactionFeeMax,
- transactionFeeBurn,
- providerPublicFunds,
- providerPrivateFunds,
- providerPrivateClientLimit,
- providerPublicMinRate,
- providerPublicMaxRate,
- providerPrivateMinRate,
- providerPrivateMaxRate,
- providerGasProtect,
- providerInterestMinFunds,
- providerRentRate,
- schellingRoundBlockDelay,
- schellingCheckRounds,
- schellingCheckAboves,
- schellingRate,
- publisherMinAnnouncementDelay,
- publisherOppositeRate
- }
+pragma solidity >=0.0; + +contract announcementTypes { + + enum announcementType { + /* + type of announcements + */ + newModule, + dropModule, + replaceModule, + replaceModuleHandler, + question, + transactionFeeRate, + transactionFeeMin, + transactionFeeMax, + transactionFeeBurn, + providerPublicFunds, + providerPrivateFunds, + providerPrivateClientLimit, + providerPublicMinRate, + providerPublicMaxRate, + providerPrivateMinRate, + providerPrivateMaxRate, + providerGasProtect, + providerInterestMinFunds, + providerRentRate, + schellingRoundBlockDelay, + schellingCheckRounds, + schellingCheckAboves, + schellingRate, + publisherMinAnnouncementDelay, + publisherOppositeRate + } }
\ No newline at end of file diff --git a/test/compilationTests/corion/ico.sol b/test/compilationTests/corion/ico.sol index 6c9f8cb2..c550fbef 100644 --- a/test/compilationTests/corion/ico.sol +++ b/test/compilationTests/corion/ico.sol @@ -1,4 +1,4 @@ -pragma solidity ^0.4.11; +pragma solidity >=0.0; import "./safeMath.sol"; import "./token.sol"; @@ -6,7 +6,7 @@ import "./premium.sol"; import "./moduleHandler.sol"; contract ico is safeMath { - + struct icoLevels_s { uint256 block; uint8 rate; @@ -24,15 +24,15 @@ contract ico is safeMath { uint256 cor; uint256 corp; } - + uint256 constant oneSegment = 40320; - - address public owner; - address public tokenAddr; - address public premiumAddr; + + address payable public owner; + address payable public tokenAddr; + address payable public premiumAddr; uint256 public startBlock; uint256 public icoDelay; - address public foundationAddress; + address payable public foundationAddress; address public icoEtcPriceAddr; uint256 public icoExchangeRate; uint256 public icoExchangeRateSetBlock; @@ -49,11 +49,11 @@ contract ico is safeMath { mapping (address => mapping(uint256 => interest_s)) public interestDB; uint256 public totalMint; uint256 public totalPremiumMint; - - function ico(address foundation, address priceSet, uint256 exchangeRate, uint256 startBlockNum, address[] genesisAddr, uint256[] genesisValue) { + + constructor(address payable foundation, address priceSet, uint256 exchangeRate, uint256 startBlockNum, address[] memory genesisAddr, uint256[] memory genesisValue) public { /* Installation function. - + @foundation The ETC address of the foundation @priceSet The address which will be able to make changes on the rate later on. @exchangeRate The current ETC/USD rate multiplied by 1e4. For example: 2.5 USD/ETC = 25000 @@ -80,11 +80,11 @@ contract ico is safeMath { interestDB[genesisAddr[a]][0].amount = genesisValue[a]; } } - - function ICObonus() public constant returns(uint256 bonus) { + + function ICObonus() public view returns(uint256 bonus) { /* Query of current bonus - + @bonus Bonus % */ for ( uint8 a=0 ; a<icoLevels.length ; a++ ) { @@ -93,32 +93,32 @@ contract ico is safeMath { } } } - + function setInterestDB(address addr, uint256 balance) external returns(bool success) { /* Setting interest database. It can be requested by Token contract only. - A database has to be built in order that after ICO closed everybody can get their compound interest on their capital accumulated - + A database has to be built in order that after ICO closed everybody can get their compound interest on their capital accumulated + @addr Sender @balance Quantity - + @success Was the process successful or not */ require( msg.sender == tokenAddr ); uint256 _num = (block.number - startBlock) / interestBlockDelay; interestDB[addr][_num].amount = balance; - if ( balance == 0 ) { + if ( balance == 0 ) { interestDB[addr][_num].empty = true; } return true; } - - function checkInterest(address addr) public constant returns(uint256 amount) { + + function checkInterest(address addr) public view returns(uint256 amount) { /* Query of compound interest - + @addr Address - + @amount Amount of compound interest */ uint256 _lastBal; @@ -126,9 +126,9 @@ contract ico is safeMath { bool _empty; interest_s memory _idb; uint256 _to = (block.number - startBlock) / interestBlockDelay; - + if ( _to == 0 || aborted ) { return 0; } - + for ( uint256 r=0 ; r < _to ; r++ ) { if ( r*interestBlockDelay+startBlock >= icoDelay ) { break; } _idb = interestDB[addr][r]; @@ -148,11 +148,11 @@ contract ico is safeMath { amount += _tamount; } } - + function getInterest(address beneficiary) external { /* Request of compound interest. This is deleted from the database after the ICO closed and following the query of the compound interest. - + @beneficiary Beneficiary who will receive the interest */ uint256 _lastBal; @@ -162,11 +162,11 @@ contract ico is safeMath { interest_s memory _idb; address _addr = beneficiary; uint256 _to = (block.number - startBlock) / interestBlockDelay; - if ( _addr == 0x00 ) { _addr = msg.sender; } - + if ( _addr == address(0x00) ) { _addr = msg.sender; } + require( block.number > icoDelay ); require( ! aborted ); - + for ( uint256 r=0 ; r < _to ; r++ ) { if ( r*interestBlockDelay+startBlock >= icoDelay ) { break; } _idb = interestDB[msg.sender][r]; @@ -186,17 +186,17 @@ contract ico is safeMath { _amount += _tamount; delete interestDB[msg.sender][r]; } - + require( _amount > 0 ); token(tokenAddr).mint(_addr, _amount); } - + function setICOEthPrice(uint256 value) external { /* - Setting of the ICO ETC USD rates which can only be calle by a pre-defined address. + Setting of the ICO ETC USD rates which can only be calle by a pre-defined address. After this function is completed till the call of the next function (which is at least an exchangeRateDelay array) this rate counts. With this process avoiding the sudden rate changes. - + @value The ETC/USD rate multiplied by 1e4. For example: 2.5 USD/ETC = 25000 */ require( isICO() ); @@ -205,18 +205,18 @@ contract ico is safeMath { icoExchangeRateSetBlock = block.number + exchangeRateDelay; icoExchangeRate = value; } - + function extendICO() external { /* Extend the period of the ICO with one segment. - + It is only possible during the ICO and only callable by the owner. */ require( isICO() ); require( msg.sender == owner ); icoDelay += oneSegment; } - + function closeICO() external { /* Closing the ICO. @@ -231,14 +231,14 @@ contract ico is safeMath { require( ! aborted ); require( token(tokenAddr).mint(foundationAddress, token(tokenAddr).totalSupply() * 96 / 100) ); require( premium(premiumAddr).mint(foundationAddress, totalMint / 5000 - totalPremiumMint) ); - require( foundationAddress.send(this.balance) ); + require( foundationAddress.send(address(this).balance) ); require( token(tokenAddr).closeIco() ); require( premium(premiumAddr).closeIco() ); } - + function abortICO() external { /* - Withdrawal of the ICO. + Withdrawal of the ICO. It is only possible during the ICO period. Only callable by the owner. After this process only the receiveFunds function will be available for the customers. @@ -247,21 +247,21 @@ contract ico is safeMath { require( msg.sender == owner ); aborted = true; } - - function connectTokens(address tokenContractAddr, address premiumContractAddr) external { + + function connectTokens(address payable tokenContractAddr, address payable premiumContractAddr) external { /* Installation function which joins the two token contracts with this contract. Only callable by the owner - + @tokenContractAddr Address of the corion token contract. @premiumContractAddr Address of the corion premium token contract */ require( msg.sender == owner ); - require( tokenAddr == 0x00 && premiumAddr == 0x00 ); + require( tokenAddr == address(0x00) && premiumAddr == address(0x00) ); tokenAddr = tokenContractAddr; premiumAddr = premiumContractAddr; } - + function receiveFunds() external { /* Refund the amount which was purchased during the ICO period. @@ -274,25 +274,25 @@ contract ico is safeMath { delete brought[msg.sender]; require( msg.sender.send(_val) ); } - - function () payable { + + function () external payable { /* - Callback function. Simply calls the buy function as a beneficiary and there is no affilate address. + Callback function. Simply calls the buy function as a beneficiary and there is no affiliate address. If they call the contract without any function then this process will be taken place. */ require( isICO() ); - require( buy(msg.sender, 0x00) ); + require( buy(msg.sender, address(0x00)) ); } - - function buy(address beneficiaryAddress, address affilateAddress) payable returns (bool success) { + + function buy(address payable beneficiaryAddress, address affilateAddress) public payable returns (bool success) { /* Buying a token - + If there is not at least 0.2 ether balance on the beneficiaryAddress then the amount of the ether which was intended for the purchase will be reduced by 0.2 and that will be sent to the address of the beneficiary. From the remaining amount calculate the reward with the help of the getIcoReward function. - Only that affilate address is valid which has some token on it’s account. - If there is a valid affilate address then calculate and credit the reward as well in the following way: - With more than 1e12 token contract credit 5% reward based on the calculation that how many tokens did they buy when he was added as an affilate. + Only that affiliate address is valid which has some token on it’s account. + If there is a valid affiliate address then calculate and credit the reward as well in the following way: + With more than 1e12 token contract credit 5% reward based on the calculation that how many tokens did they buy when he was added as an affiliate. More than 1e11 token: 4% More than 1e10 token: 3% More than 1e9 token: 2% below 1% @@ -300,16 +300,16 @@ contract ico is safeMath { @affilateAddress The address of the person who offered who will get the referral reward. It can not be equal with the beneficiaryAddress. */ require( isICO() ); - if ( beneficiaryAddress == 0x00) { beneficiaryAddress = msg.sender; } + if ( beneficiaryAddress == address(0x00)) { beneficiaryAddress = msg.sender; } if ( beneficiaryAddress == affilateAddress ) { - affilateAddress = 0x00; + affilateAddress = address(0x00); } uint256 _value = msg.value; if ( beneficiaryAddress.balance < 0.2 ether ) { require( beneficiaryAddress.send(0.2 ether) ); _value = safeSub(_value, 0.2 ether); } - var _reward = getIcoReward(_value); + uint256 _reward = getIcoReward(_value); require( _reward > 0 ); require( token(tokenAddr).mint(beneficiaryAddress, _reward) ); brought[beneficiaryAddress].eth = safeAdd(brought[beneficiaryAddress].eth, _value); @@ -317,7 +317,7 @@ contract ico is safeMath { totalMint = safeAdd(totalMint, _reward); require( foundationAddress.send(_value * 10 / 100) ); uint256 extra; - if ( affilateAddress != 0x00 && ( brought[affilateAddress].eth > 0 || interestDB[affilateAddress][0].amount > 0 ) ) { + if ( affilateAddress != address(0x00) && ( brought[affilateAddress].eth > 0 || interestDB[affilateAddress][0].amount > 0 ) ) { affiliate[affilateAddress].weight = safeAdd(affiliate[affilateAddress].weight, _reward); extra = affiliate[affilateAddress].weight; uint256 rate; @@ -327,7 +327,7 @@ contract ico is safeMath { rate = 4; } else if (extra >= 1e10) { rate = 3; - } else if (extra >= 1e9) { + } else if (extra >= 1e9) { rate = 2; } else { rate = 1; @@ -337,15 +337,15 @@ contract ico is safeMath { token(tokenAddr).mint(affilateAddress, extra); } checkPremium(beneficiaryAddress); - EICO(beneficiaryAddress, _reward, affilateAddress, extra); + emit EICO(beneficiaryAddress, _reward, affilateAddress, extra); return true; } function checkPremium(address owner) internal { /* Crediting the premium token - - @owner The corion token balance of this address will be set based on the calculation which shows that how many times can be the amount of the purchased tokens devided by 5000. So after each 5000 token we give 1 premium token. + + @owner The corion token balance of this address will be set based on the calculation which shows that how many times can be the amount of the purchased tokens divided by 5000. So after each 5000 token we give 1 premium token. */ uint256 _reward = (brought[owner].cor / 5e9) - brought[owner].corp; if ( _reward > 0 ) { @@ -354,11 +354,11 @@ contract ico is safeMath { totalPremiumMint = safeAdd(totalPremiumMint, _reward); } } - - function getIcoReward(uint256 value) public constant returns (uint256 reward) { + + function getIcoReward(uint256 value) public view returns (uint256 reward) { /* Expected token volume at token purchase - + @value The amount of ether for the purchase @reward Amount of the token x = (value * 1e6 * USD_ETC_exchange rate / 1e4 / 1e18) * bonus percentage @@ -367,10 +367,10 @@ contract ico is safeMath { reward = (value * 1e6 * icoExchangeRate / icoExchangeRateM / 1 ether) * (ICObonus() + 100) / 100; if ( reward < 5e6) { return 0; } } - - function isICO() public constant returns (bool success) { + + function isICO() public view returns (bool success) { return startBlock <= block.number && block.number <= icoDelay && ( ! aborted ) && ( ! closed ); } - - event EICO(address indexed Address, uint256 indexed value, address Affilate, uint256 AffilateValue); + + event EICO(address indexed Address, uint256 indexed value, address Affiliate, uint256 AffilateValue); } diff --git a/test/compilationTests/corion/module.sol b/test/compilationTests/corion/module.sol index d64044cb..f663d9c2 100644 --- a/test/compilationTests/corion/module.sol +++ b/test/compilationTests/corion/module.sol @@ -1,26 +1,26 @@ -pragma solidity ^0.4.11; +pragma solidity >=0.0; contract abstractModuleHandler { - function transfer(address from, address to, uint256 value, bool fee) external returns (bool success) {} - function balanceOf(address owner) public constant returns (bool success, uint256 value) {} + function transfer(address payable from, address payable to, uint256 value, bool fee) external returns (bool success) {} + function balanceOf(address payable owner) public view returns (bool success, uint256 value) {} } contract module { /* Module */ - + enum status { New, Connected, Disconnected, Disabled } - + status public moduleStatus; uint256 public disabledUntil; - address public moduleHandlerAddress; - + address payable public moduleHandlerAddress; + function disableModule(bool forever) external onlyForModuleHandler returns (bool success) { _disableModule(forever); return true; @@ -29,28 +29,28 @@ contract module { /* Disable the module for one week, if the forever true then for forever. This function calls the Publisher module. - + @forever For forever or not */ if ( forever ) { moduleStatus = status.Disabled; } else { disabledUntil = block.number + 5760; } } - - function replaceModuleHandler(address newModuleHandlerAddress) external onlyForModuleHandler returns (bool success) { + + function replaceModuleHandler(address payable newModuleHandlerAddress) external onlyForModuleHandler returns (bool success) { _replaceModuleHandler(newModuleHandlerAddress); return true; } - function _replaceModuleHandler(address newModuleHandlerAddress) internal { + function _replaceModuleHandler(address payable newModuleHandlerAddress) internal { /* Replace the ModuleHandler address. This function calls the Publisher module. - + @newModuleHandlerAddress New module handler address */ require( moduleStatus == status.Connected ); moduleHandlerAddress = newModuleHandlerAddress; } - + function connectModule() external onlyForModuleHandler returns (bool success) { _connectModule(); return true; @@ -63,7 +63,7 @@ contract module { require( moduleStatus == status.New ); moduleStatus = status.Connected; } - + function disconnectModule() external onlyForModuleHandler returns (bool success) { _disconnectModule(); return true; @@ -76,62 +76,62 @@ contract module { require( moduleStatus != status.New && moduleStatus != status.Disconnected ); moduleStatus = status.Disconnected; } - - function replaceModule(address newModuleAddress) external onlyForModuleHandler returns (bool success) { + + function replaceModule(address payable newModuleAddress) external onlyForModuleHandler returns (bool success) { _replaceModule(newModuleAddress); return true; } - function _replaceModule(address newModuleAddress) internal { + function _replaceModule(address payable newModuleAddress) internal { /* Replace the module for an another new module. This function calls the Publisher module. We send every Token and ether to the new module. - + @newModuleAddress New module handler address */ require( moduleStatus != status.New && moduleStatus != status.Disconnected); - var (_success, _balance) = abstractModuleHandler(moduleHandlerAddress).balanceOf(address(this)); + (bool _success, uint256 _balance) = abstractModuleHandler(moduleHandlerAddress).balanceOf(address(this)); require( _success ); if ( _balance > 0 ) { require( abstractModuleHandler(moduleHandlerAddress).transfer(address(this), newModuleAddress, _balance, false) ); } - if ( this.balance > 0 ) { - require( newModuleAddress.send(this.balance) ); + if ( address(this).balance > 0 ) { + require( newModuleAddress.send(address(this).balance) ); } moduleStatus = status.Disconnected; } - - function transferEvent(address from, address to, uint256 value) external onlyForModuleHandler returns (bool success) { + + function transferEvent(address payable from, address payable to, uint256 value) external onlyForModuleHandler returns (bool success) { return true; } function newSchellingRoundEvent(uint256 roundID, uint256 reward) external onlyForModuleHandler returns (bool success) { return true; } - - function registerModuleHandler(address _moduleHandlerAddress) internal { + + function registerModuleHandler(address payable _moduleHandlerAddress) internal { /* Module constructor function for registering ModuleHandler address. */ moduleHandlerAddress = _moduleHandlerAddress; } - function isModuleHandler(address addr) internal returns (bool ret) { + function isModuleHandler(address payable addr) internal returns (bool ret) { /* Test for ModuleHandler address. If the module is not connected then returns always false. - + @addr Address to check - + @ret This is the module handler address or not */ - if ( moduleHandlerAddress == 0x00 ) { return true; } + if ( moduleHandlerAddress == address(0x00) ) { return true; } if ( moduleStatus != status.Connected ) { return false; } return addr == moduleHandlerAddress; } - function isActive() public constant returns (bool success, bool active) { + function isActive() public view returns (bool success, bool active) { /* Check self for ready for functions or not. - - @success Function call was successfull or not + + @success Function call was successful or not @active Ready for functions or not */ return (true, moduleStatus == status.Connected && block.number >= disabledUntil); @@ -140,4 +140,6 @@ contract module { require( msg.sender == moduleHandlerAddress ); _; } + function() external payable { + } } diff --git a/test/compilationTests/corion/moduleHandler.sol b/test/compilationTests/corion/moduleHandler.sol index 682f81dd..76061a31 100644 --- a/test/compilationTests/corion/moduleHandler.sol +++ b/test/compilationTests/corion/moduleHandler.sol @@ -1,4 +1,4 @@ -pragma solidity ^0.4.11; +pragma solidity >=0.0; import "./module.sol"; import "./announcementTypes.sol"; @@ -16,34 +16,34 @@ contract abstractModule { function disconnectModule() external returns (bool success) {} function replaceModule(address addr) external returns (bool success) {} function disableModule(bool forever) external returns (bool success) {} - function isActive() public constant returns (bool success) {} + function isActive() public view returns (bool success) {} function replaceModuleHandler(address newHandler) external returns (bool success) {} function transferEvent(address from, address to, uint256 value) external returns (bool success) {} function newSchellingRoundEvent(uint256 roundID, uint256 reward) external returns (bool success) {} } contract moduleHandler is multiOwner, announcementTypes { - + struct modules_s { - address addr; + address payable addr; bytes32 name; bool schellingEvent; bool transferEvent; } - + modules_s[] public modules; address public foundationAddress; uint256 debugModeUntil = block.number + 1000000; - - function moduleHandler(address[] newOwners) multiOwner(newOwners) {} - - function load(address foundation, bool forReplace, address Token, address Premium, address Publisher, address Schelling, address Provider) { + + + constructor(address[] memory newOwners) multiOwner(newOwners) public {} + function load(address payable foundation, bool forReplace, address payable Token, address payable Premium, address payable Publisher, address payable Schelling, address payable Provider) public { /* Loading modulest to ModuleHandler. - + This module can be called only once and only by the owner, if every single module and its database are already put on the blockchain. If forReaplace is true, than the ModuleHandler will be replaced. Before the publishing of its replace, the new contract must be already on the blockchain. - + @foundation Address of foundation. @forReplace Is it for replace or not. If not, it will be connected to the module. @Token address of token. @@ -54,26 +54,26 @@ contract moduleHandler is multiOwner, announcementTypes { require( owners[msg.sender] ); require( modules.length == 0 ); foundationAddress = foundation; - addModule( modules_s(Token, sha3('Token'), false, false), ! forReplace); - addModule( modules_s(Premium, sha3('Premium'), false, false), ! forReplace); - addModule( modules_s(Publisher, sha3('Publisher'), false, true), ! forReplace); - addModule( modules_s(Schelling, sha3('Schelling'), false, true), ! forReplace); - addModule( modules_s(Provider, sha3('Provider'), true, true), ! forReplace); + addModule( modules_s(Token, keccak256('Token'), false, false), ! forReplace); + addModule( modules_s(Premium, keccak256('Premium'), false, false), ! forReplace); + addModule( modules_s(Publisher, keccak256('Publisher'), false, true), ! forReplace); + addModule( modules_s(Schelling, keccak256('Schelling'), false, true), ! forReplace); + addModule( modules_s(Provider, keccak256('Provider'), true, true), ! forReplace); } - function addModule(modules_s input, bool call) internal { + function addModule(modules_s memory input, bool call) internal { /* Inside function for registration of the modules in the database. - If the call is false, wont happen any direct call. - + If the call is false, won't happen any direct call. + @input _Structure of module. @call Is connect to the module or not. */ if ( call ) { require( abstractModule(input.addr).connectModule() ); } - var (success, found, id) = getModuleIDByAddress(input.addr); + (bool success, bool found, uint256 id) = getModuleIDByAddress(input.addr); require( success && ! found ); (success, found, id) = getModuleIDByHash(input.name); require( success && ! found ); - (success, found, id) = getModuleIDByAddress(0x00); + (success, found, id) = getModuleIDByAddress(address(0x00)); require( success ); if ( ! found ) { id = modules.length; @@ -81,23 +81,23 @@ contract moduleHandler is multiOwner, announcementTypes { } modules[id] = input; } - function getModuleAddressByName(string name) public constant returns( bool success, bool found, address addr ) { + function getModuleAddressByName(string memory name) public view returns( bool success, bool found, address addr ) { /* Search by name for module. The result is an Ethereum address. - + @name Name of module. @addr Address of module. @found Is there any result. - @success Was the transaction succesfull or not. + @success Was the transaction successful or not. */ - var (_success, _found, _id) = getModuleIDByName(name); + (bool _success, bool _found, uint256 _id) = getModuleIDByName(name); if ( _success && _found ) { return (true, true, modules[_id].addr); } - return (true, false, 0x00); + return (true, false, address(0x00)); } - function getModuleIDByHash(bytes32 hashOfName) public constant returns( bool success, bool found, uint256 id ) { + function getModuleIDByHash(bytes32 hashOfName) public view returns( bool success, bool found, uint256 id ) { /* Search by hash of name in the module array. The result is an index array. - + @name Name of module. @id Index of module. @found Was there any result or not. @@ -109,15 +109,15 @@ contract moduleHandler is multiOwner, announcementTypes { } return (true, false, 0); } - function getModuleIDByName(string name) public constant returns( bool success, bool found, uint256 id ) { + function getModuleIDByName(string memory name) public view returns( bool success, bool found, uint256 id ) { /* Search by name for module. The result is an index array. - + @name Name of module. @id Index of module. @found Was there any result or not. */ - bytes32 _name = sha3(name); + bytes32 _name = keccak256(bytes(name)); for ( uint256 a=0 ; a<modules.length ; a++ ) { if ( modules[a].name == _name ) { return (true, true, a); @@ -125,10 +125,10 @@ contract moduleHandler is multiOwner, announcementTypes { } return (true, false, 0); } - function getModuleIDByAddress(address addr) public constant returns( bool success, bool found, uint256 id ) { + function getModuleIDByAddress(address addr) public view returns( bool success, bool found, uint256 id ) { /* Search by ethereum address for module. The result is an index array. - + @name Name of module. @id Index of module. @found Was there any result or not. @@ -140,20 +140,20 @@ contract moduleHandler is multiOwner, announcementTypes { } return (true, false, 0); } - function replaceModule(string name, address addr, bool callCallback) external returns (bool success) { + function replaceModule(string calldata name, address payable addr, bool callCallback) external returns (bool success) { /* Module replace, can be called only by the Publisher contract. - + @name Name of module. @addr Address of module. @bool Was there any result or not. @callCallback Call the replaceable module to confirm replacement or not. */ - var (_success, _found, _id) = getModuleIDByAddress(msg.sender); + (bool _success, bool _found, uint256 _id) = getModuleIDByAddress(msg.sender); require( _success ); - if ( ! ( _found && modules[_id].name == sha3('Publisher') )) { + if ( ! ( _found && modules[_id].name == keccak256('Publisher') )) { require( block.number < debugModeUntil ); - if ( ! insertAndCheckDo(calcDoHash("replaceModule", sha3(name, addr, callCallback))) ) { + if ( ! insertAndCheckDo(calcDoHash("replaceModule", keccak256(abi.encodePacked(name, addr, callCallback)))) ) { return true; } } @@ -166,52 +166,52 @@ contract moduleHandler is multiOwner, announcementTypes { modules[_id].addr = addr; return true; } - - function callReplaceCallback(string moduleName, address newModule) external returns (bool success) { + + function callReplaceCallback(string calldata moduleName, address newModule) external returns (bool success) { require( block.number < debugModeUntil ); - if ( ! insertAndCheckDo(calcDoHash("callReplaceCallback", sha3(moduleName, newModule))) ) { + if ( ! insertAndCheckDo(calcDoHash("callReplaceCallback", keccak256(abi.encodePacked(moduleName, newModule)))) ) { return true; } - var (_success, _found, _id) = getModuleIDByName(moduleName); + (bool _success, bool _found, uint256 _id) = getModuleIDByName(moduleName); require( _success); require( abstractModule(modules[_id].addr).replaceModule(newModule) ); return true; } - - function newModule(string name, address addr, bool schellingEvent, bool transferEvent) external returns (bool success) { + + function newModule(string calldata name, address payable addr, bool schellingEvent, bool transferEvent) external returns (bool success) { /* Adding new module to the database. Can be called only by the Publisher contract. - + @name Name of module. @addr Address of module. @schellingEvent Gets it new Schelling round notification? @transferEvent Gets it new transaction notification? @bool Was there any result or not. */ - var (_success, _found, _id) = getModuleIDByAddress(msg.sender); + (bool _success, bool _found, uint256 _id) = getModuleIDByAddress(msg.sender); require( _success ); - if ( ! ( _found && modules[_id].name == sha3('Publisher') )) { + if ( ! ( _found && modules[_id].name == keccak256('Publisher') )) { require( block.number < debugModeUntil ); - if ( ! insertAndCheckDo(calcDoHash("newModule", sha3(name, addr, schellingEvent, transferEvent))) ) { + if ( ! insertAndCheckDo(calcDoHash("newModule", keccak256(abi.encodePacked(name, addr, schellingEvent, transferEvent)))) ) { return true; } } - addModule( modules_s(addr, sha3(name), schellingEvent, transferEvent), true); + addModule( modules_s(addr, keccak256(bytes(name)), schellingEvent, transferEvent), true); return true; } - function dropModule(string name, bool callCallback) external returns (bool success) { + function dropModule(string calldata name, bool callCallback) external returns (bool success) { /* Deleting module from the database. Can be called only by the Publisher contract. - + @name Name of module to delete. - @bool Was the function successfull? + @bool Was the function successful? @callCallback Call the replaceable module to confirm replacement or not. */ - var (_success, _found, _id) = getModuleIDByAddress(msg.sender); + (bool _success, bool _found, uint256 _id) = getModuleIDByAddress(msg.sender); require( _success ); - if ( ! ( _found && modules[_id].name == sha3('Publisher') )) { + if ( ! ( _found && modules[_id].name == keccak256('Publisher') )) { require( block.number < debugModeUntil ); - if ( ! insertAndCheckDo(calcDoHash("replaceModule", sha3(name, callCallback))) ) { + if ( ! insertAndCheckDo(calcDoHash("replaceModule", keccak256(abi.encodePacked(name, callCallback)))) ) { return true; } } @@ -223,32 +223,32 @@ contract moduleHandler is multiOwner, announcementTypes { delete modules[_id]; return true; } - - function callDisableCallback(string moduleName) external returns (bool success) { + + function callDisableCallback(string calldata moduleName) external returns (bool success) { require( block.number < debugModeUntil ); - if ( ! insertAndCheckDo(calcDoHash("callDisableCallback", sha3(moduleName))) ) { + if ( ! insertAndCheckDo(calcDoHash("callDisableCallback", keccak256(bytes(moduleName)))) ) { return true; } - var (_success, _found, _id) = getModuleIDByName(moduleName); + (bool _success, bool _found, uint256 _id) = getModuleIDByName(moduleName); require( _success); require( abstractModule(modules[_id].addr).disableModule(true) ); return true; } - + function broadcastTransfer(address from, address to, uint256 value) external returns (bool success) { /* Announcing transactions for the modules. - + Can be called only by the token module. Only the configured modules get notifications.( transferEvent ) - + @from from who. @to to who. @value amount. - @bool Was the function successfull? + @bool Was the function successful? */ - var (_success, _found, _id) = getModuleIDByAddress(msg.sender); - require( _success && _found && modules[_id].name == sha3('Token') ); + (bool _success, bool _found, uint256 _id) = getModuleIDByAddress(msg.sender); + require( _success && _found && modules[_id].name == keccak256('Token') ); for ( uint256 a=0 ; a<modules.length ; a++ ) { if ( modules[a].transferEvent && abstractModule(modules[a].addr).isActive() ) { require( abstractModule(modules[a].addr).transferEvent(from, to, value) ); @@ -261,13 +261,13 @@ contract moduleHandler is multiOwner, announcementTypes { Announcing new Schelling round for the modules. Can be called only by the Schelling module. Only the configured modules get notifications( schellingEvent ). - + @roundID Number of Schelling round. @reward Coin emission in this Schelling round. - @bool Was the function successfull? + @bool Was the function successful? */ - var (_success, _found, _id) = getModuleIDByAddress(msg.sender); - require( _success && _found && modules[_id].name == sha3('Schelling') ); + (bool _success, bool _found, uint256 _id) = getModuleIDByAddress(msg.sender); + require( _success && _found && modules[_id].name == keccak256('Schelling') ); for ( uint256 a=0 ; a<modules.length ; a++ ) { if ( modules[a].schellingEvent && abstractModule(modules[a].addr).isActive() ) { require( abstractModule(modules[a].addr).newSchellingRoundEvent(roundID, reward) ); @@ -278,18 +278,18 @@ contract moduleHandler is multiOwner, announcementTypes { function replaceModuleHandler(address newHandler) external returns (bool success) { /* Replacing ModuleHandler. - + Can be called only by the publisher. Every module will be informed about the ModuleHandler replacement. - + @newHandler Address of the new ModuleHandler. - @bool Was the function successfull? + @bool Was the function successful? */ - var (_success, _found, _id) = getModuleIDByAddress(msg.sender); + (bool _success, bool _found, uint256 _id) = getModuleIDByAddress(msg.sender); require( _success ); - if ( ! ( _found && modules[_id].name == sha3('Publisher') )) { + if ( ! ( _found && modules[_id].name == keccak256('Publisher') )) { require( block.number < debugModeUntil ); - if ( ! insertAndCheckDo(calcDoHash("replaceModuleHandler", sha3(newHandler))) ) { + if ( ! insertAndCheckDo(calcDoHash("replaceModuleHandler", keccak256(abi.encodePacked(newHandler)))) ) { return true; } } @@ -298,62 +298,62 @@ contract moduleHandler is multiOwner, announcementTypes { } return true; } - function balanceOf(address owner) public constant returns (bool success, uint256 value) { + function balanceOf(address owner) public view returns (bool success, uint256 value) { /* Query of token balance. - + @owner address @value balance. - @success was the function successfull? + @success was the function successful? */ - var (_success, _found, _id) = getModuleIDByName('Token'); + (bool _success, bool _found, uint256 _id) = getModuleIDByName('Token'); require( _success && _found ); return (true, token(modules[_id].addr).balanceOf(owner)); } - function totalSupply() public constant returns (bool success, uint256 value) { + function totalSupply() public view returns (bool success, uint256 value) { /* Query of the whole token amount. - + @value amount. - @success was the function successfull? + @success was the function successful? */ - var (_success, _found, _id) = getModuleIDByName('Token'); + (bool _success, bool _found, uint256 _id) = getModuleIDByName('Token'); require( _success && _found ); return (true, token(modules[_id].addr).totalSupply()); } - function isICO() public constant returns (bool success, bool ico) { + function isICO() public view returns (bool success, bool ico) { /* Query of ICO state - + @ico Is ICO in progress?. - @success was the function successfull? + @success was the function successful? */ - var (_success, _found, _id) = getModuleIDByName('Token'); + (bool _success, bool _found, uint256 _id) = getModuleIDByName('Token'); require( _success && _found ); return (true, token(modules[_id].addr).isICO()); } - function getCurrentSchellingRoundID() public constant returns (bool success, uint256 round) { + function getCurrentSchellingRoundID() public view returns (bool success, uint256 round) { /* Query of number of the actual Schelling round. - + @round Schelling round. - @success was the function successfull? + @success was the function successful? */ - var (_success, _found, _id) = getModuleIDByName('Schelling'); + (bool _success, bool _found, uint256 _id) = getModuleIDByName('Schelling'); require( _success && _found ); return (true, schelling(modules[_id].addr).getCurrentSchellingRoundID()); } function mint(address to, uint256 value) external returns (bool success) { /* Token emission request. Can be called only by the provider. - + @to Place of new token @value Token amount - - @success Was the function successfull? + + @success Was the function successful? */ - var (_success, _found, _id) = getModuleIDByAddress(msg.sender); - require( _success && _found && modules[_id].name == sha3('Provider') ); + (bool _success, bool _found, uint256 _id) = getModuleIDByAddress(msg.sender); + require( _success && _found && modules[_id].name == keccak256('Provider') ); (_success, _found, _id) = getModuleIDByName('Token'); require( _success && _found ); require( token(modules[_id].addr).mint(to, value) ); @@ -362,14 +362,14 @@ contract moduleHandler is multiOwner, announcementTypes { function transfer(address from, address to, uint256 value, bool fee) external returns (bool success) { /* Token transaction request. Can be called only by a module. - + @from from who. @to To who. @value Token amount. @fee Transaction fee will be charged or not? - @success Was the function successfull? + @success Was the function successful? */ - var (_success, _found, _id) = getModuleIDByAddress(msg.sender); + (bool _success, bool _found, uint256 _id) = getModuleIDByAddress(msg.sender); require( _success && _found ); (_success, _found, _id) = getModuleIDByName('Token'); require( _success && _found ); @@ -379,13 +379,13 @@ contract moduleHandler is multiOwner, announcementTypes { function processTransactionFee(address from, uint256 value) external returns (bool success) { /* Token transaction fee. Can be called only by the provider. - + @from From who. @value Token amount. - @success Was the function successfull? + @success Was the function successful? */ - var (_success, _found, _id) = getModuleIDByAddress(msg.sender); - require( _success && _found && modules[_id].name == sha3('Provider') ); + (bool _success, bool _found, uint256 _id) = getModuleIDByAddress(msg.sender); + require( _success && _found && modules[_id].name == keccak256('Provider') ); (_success, _found, _id) = getModuleIDByName('Token'); require( _success && _found ); require( token(modules[_id].addr).processTransactionFee(from, value) ); @@ -394,32 +394,32 @@ contract moduleHandler is multiOwner, announcementTypes { function burn(address from, uint256 value) external returns (bool success) { /* Token burn. Can be called only by Schelling. - + @from From who. @value Token amount. - @success Was the function successfull? + @success Was the function successful? */ - var (_success, _found, _id) = getModuleIDByAddress(msg.sender); - require( _success && _found && modules[_id].name == sha3('Schelling') ); + (bool _success, bool _found, uint256 _id) = getModuleIDByAddress(msg.sender); + require( _success && _found && modules[_id].name == keccak256('Schelling') ); (_success, _found, _id) = getModuleIDByName('Token'); require( _success && _found ); require( token(modules[_id].addr).burn(from, value) ); return true; } - function configureModule(string moduleName, announcementType aType, uint256 value) external returns (bool success) { + function configureModule(string calldata moduleName, announcementType aType, uint256 value) external returns (bool success) { /* Changing configuration of a module. Can be called only by Publisher or while debug mode by owners. - + @moduleName Module name which will be configured @aType Type of variable (announcementType). @value New value - @success Was the function successfull? + @success Was the function successful? */ - var (_success, _found, _id) = getModuleIDByAddress(msg.sender); + (bool _success, bool _found, uint256 _id) = getModuleIDByAddress(msg.sender); require( _success ); - if ( ! ( _found && modules[_id].name == sha3('Publisher') )) { + if ( ! ( _found && modules[_id].name == keccak256('Publisher') )) { require( block.number < debugModeUntil ); - if ( ! insertAndCheckDo(calcDoHash("configureModule", sha3(moduleName, aType, value))) ) { + if ( ! insertAndCheckDo(calcDoHash("configureModule", keccak256(abi.encodePacked(moduleName, aType, value)))) ) { return true; } } @@ -431,15 +431,15 @@ contract moduleHandler is multiOwner, announcementTypes { function freezing(bool forever) external { /* Freezing CORION Platform. Can be called only by the owner. - Freez can not be recalled! - + Freeze can not be recalled! + @forever Is it forever or not? */ require( owners[msg.sender] ); if ( forever ) { - if ( ! insertAndCheckDo(calcDoHash("freezing", sha3(forever))) ) { + if ( ! insertAndCheckDo(calcDoHash("freezing", keccak256(abi.encodePacked(forever)))) ) { return; - } + } } for ( uint256 a=0 ; a<modules.length ; a++ ) { require( abstractModule(modules[a].addr).disableModule(forever) ); diff --git a/test/compilationTests/corion/multiOwner.sol b/test/compilationTests/corion/multiOwner.sol index 9aae0ebd..b61289dd 100644 --- a/test/compilationTests/corion/multiOwner.sol +++ b/test/compilationTests/corion/multiOwner.sol @@ -1,18 +1,18 @@ -pragma solidity ^0.4.11; +pragma solidity >=0.0; import "./safeMath.sol"; contract multiOwner is safeMath { - + mapping(address => bool) public owners; uint256 public ownerCount; mapping(bytes32 => address[]) public doDB; - + /* Constructor */ - function multiOwner(address[] newOwners) { + constructor(address[] memory newOwners) public { for ( uint256 a=0 ; a<newOwners.length ; a++ ) { _addOwner(newOwners[a]); } @@ -21,12 +21,12 @@ contract multiOwner is safeMath { Externals */ function insertOwner(address addr) external { - if ( insertAndCheckDo(calcDoHash("insertOwner", sha3(addr))) ) { + if ( insertAndCheckDo(calcDoHash("insertOwner", keccak256(abi.encodePacked(addr)))) ) { _addOwner(addr); } } function dropOwner(address addr) external { - if ( insertAndCheckDo(calcDoHash("dropOwner", sha3(addr))) ) { + if ( insertAndCheckDo(calcDoHash("dropOwner", keccak256(abi.encodePacked(addr)))) ) { _delOwner(addr); } } @@ -38,13 +38,13 @@ contract multiOwner is safeMath { /* Constants */ - function ownersForChange() public constant returns (uint256 owners) { + function ownersForChange() public view returns (uint256 owners) { return ownerCount * 75 / 100; } - function calcDoHash(string job, bytes32 data) public constant returns (bytes32 hash) { - return sha3(job, data); + function calcDoHash(string memory job, bytes32 data) public pure returns (bytes32 hash) { + return keccak256(abi.encodePacked(job, data)); } - function validDoHash(bytes32 doHash) public constant returns (bool valid) { + function validDoHash(bytes32 doHash) public view returns (bool valid) { return doDB[doHash].length > 0; } /* diff --git a/test/compilationTests/corion/owned.sol b/test/compilationTests/corion/owned.sol index bd187775..f273e3b5 100644 --- a/test/compilationTests/corion/owned.sol +++ b/test/compilationTests/corion/owned.sol @@ -1,28 +1,28 @@ -pragma solidity ^0.4.11;
-
-contract ownedDB {
- address private owner;
-
- function replaceOwner(address newOwner) external returns(bool) {
- /*
- Owner replace.
-
- @newOwner Address of new owner.
- */
- require( isOwner() );
- owner = newOwner;
- return true;
- }
-
- function isOwner() internal returns(bool) {
- /*
- Check of owner address.
-
- @bool Owner has called the contract or not
- */
- if ( owner == 0x00 ) {
- return true;
- }
- return owner == msg.sender;
- }
-}
+pragma solidity >=0.0; + +contract ownedDB { + address private owner; + + function replaceOwner(address newOwner) external returns(bool) { + /* + Owner replace. + + @newOwner Address of new owner. + */ + require( isOwner() ); + owner = newOwner; + return true; + } + + function isOwner() internal returns(bool) { + /* + Check of owner address. + + @bool Owner has called the contract or not + */ + if ( owner == address(0x00) ) { + return true; + } + return owner == msg.sender; + } +} diff --git a/test/compilationTests/corion/premium.sol b/test/compilationTests/corion/premium.sol index be2c5de0..091e291a 100644 --- a/test/compilationTests/corion/premium.sol +++ b/test/compilationTests/corion/premium.sol @@ -1,26 +1,26 @@ -pragma solidity ^0.4.11; +pragma solidity >=0.0; import "./safeMath.sol"; import "./tokenDB.sol"; import "./module.sol"; contract thirdPartyPContractAbstract { - function receiveCorionPremiumToken(address, uint256, bytes) external returns (bool, uint256) {} - function approvedCorionPremiumToken(address, uint256, bytes) external returns (bool) {} + function receiveCorionPremiumToken(address, uint256, bytes calldata) external returns (bool, uint256) {} + function approvedCorionPremiumToken(address, uint256, bytes calldata) external returns (bool) {} } contract ptokenDB is tokenDB {} contract premium is module, safeMath { - function replaceModule(address addr) external returns (bool success) { + function replaceModule(address payable addr) external returns (bool success) { require( super.isModuleHandler(msg.sender) ); require( db.replaceOwner(addr) ); super._replaceModule(addr); return true; } modifier isReady { - var (_success, _active) = super.isActive(); - require( _success && _active ); + (bool _success, bool _active) = super.isActive(); + require( _success && _active ); _; } /** @@ -29,22 +29,22 @@ contract premium is module, safeMath { * @author iFA @ Corion Platform * */ - + string public name = "Corion Premium"; string public symbol = "CORP"; uint8 public decimals = 0; - + address public icoAddr; tokenDB public db; bool public isICO; - + mapping(address => bool) public genesis; - - function premium(bool forReplace, address moduleHandler, address dbAddress, address icoContractAddr, address[] genesisAddr, uint256[] genesisValue) { + + constructor(bool forReplace, address payable moduleHandler, address dbAddress, address icoContractAddr, address[] memory genesisAddr, uint256[] memory genesisValue) public { /* Setup function. If an ICOaddress is defined then the balance of the genesis addresses will be set as well. - + @forReplace This address will be replaced with the old one or not. @moduleHandler Modulhandler’s address @dbAddress Address of database @@ -53,32 +53,32 @@ contract premium is module, safeMath { @genesisValue Array of the balance of the genesis addresses */ super.registerModuleHandler(moduleHandler); - require( dbAddress != 0x00 ); + require( dbAddress != address(0x00) ); db = ptokenDB(dbAddress); if ( ! forReplace ) { - require( db.replaceOwner(this) ); + require( db.replaceOwner(address(this)) ); isICO = true; icoAddr = icoContractAddr; assert( genesisAddr.length == genesisValue.length ); for ( uint256 a=0 ; a<genesisAddr.length ; a++ ) { genesis[genesisAddr[a]] = true; require( db.increase(genesisAddr[a], genesisValue[a]) ); - Mint(genesisAddr[a], genesisValue[a]); + emit Mint(genesisAddr[a], genesisValue[a]); } } } - + function closeIco() external returns (bool success) { /* Finishing the ICO. Can be invited only by an ICO contract. - + @success If the function was successful. */ require( isICO ); isICO = false; return true; } - + /** * @notice `msg.sender` approves `spender` to spend `amount` tokens on its behalf. * @param spender The address of the account able to transfer the tokens @@ -88,18 +88,18 @@ contract premium is module, safeMath { */ function approve(address spender, uint256 amount, uint256 nonce) isReady external returns (bool success) { /* - Authorize another address to use an exact amount of the principal’s balance. - + Authorize another address to use an exact amount of the principal’s balance. + @spender Address of authorised party @amount Token quantity @nonce Transaction count - + @success Was the Function successful? */ _approve(spender, amount, nonce); return true; } - + /** * @notice `msg.sender` approves `spender` to spend `amount` tokens on its behalf and notify the spender from your approve with your `extraData` data. * @param spender The address of the account able to transfer the tokens @@ -108,28 +108,28 @@ contract premium is module, safeMath { * @param extraData Data to give forward to the receiver * @return True if the approval was successful */ - function approveAndCall(address spender, uint256 amount, uint256 nonce, bytes extraData) isReady external returns (bool success) { + function approveAndCall(address spender, uint256 amount, uint256 nonce, bytes calldata extraData) isReady external returns (bool success) { /* Authorize another address to use an exact amount of the principal’s balance. After the transaction the approvedCorionPremiumToken function of the address will be called with the given data. - + @spender Authorized address @amount Token quantity @extraData Extra data to be received by the receiver @nonce Transaction count - - @sucess Was the Function successful? + + @success Was the Function successful? */ _approve(spender, amount, nonce); require( thirdPartyPContractAbstract(spender).approvedCorionPremiumToken(msg.sender, amount, extraData) ); return true; } - + function _approve(address spender, uint256 amount, uint256 nonce) isReady internal { /* - Inner function to authorize another address to use an exact amount of the principal’s balance. + Inner function to authorize another address to use an exact amount of the principal’s balance. If the transaction count not match the authorise fails. - + @spender Address of authorised party @amount Token quantity @nonce Transaction count @@ -137,24 +137,24 @@ contract premium is module, safeMath { require( msg.sender != spender ); require( db.balanceOf(msg.sender) >= amount ); require( db.setAllowance(msg.sender, spender, amount, nonce) ); - Approval(msg.sender, spender, amount); + emit Approval(msg.sender, spender, amount); } - - function allowance(address owner, address spender) constant returns (uint256 remaining, uint256 nonce) { + + function allowance(address owner, address spender) public view returns (uint256 remaining, uint256 nonce) { /* Get the quantity of tokens given to be used - + @owner Authorising address @spender Authorised address - + @remaining Tokens to be spent @nonce Transaction count */ - var (_success, _remaining, _nonce) = db.getAllowance(owner, spender); + (bool _success, uint256 _remaining, uint256 _nonce) = db.getAllowance(owner, spender); require( _success ); return (_remaining, _nonce); } - + /** * @notice Send `amount` Corion tokens to `to` from `msg.sender` * @param to The address of the recipient @@ -166,10 +166,10 @@ contract premium is module, safeMath { Launch a transaction where the token is sent from the sender’s address to the receiver’s address. Transaction fee is going to be added as well. If the receiver is not a natural address but also a person then she/he will be invited as well. - + @to For who @amount Amount - + @success Was the function successful? */ bytes memory _data; @@ -178,10 +178,10 @@ contract premium is module, safeMath { } else { _transfer(msg.sender, to, amount); } - Transfer(msg.sender, to, amount, _data); + emit Transfer(msg.sender, to, amount, _data); return true; } - + /** * @notice Send `amount` tokens to `to` from `from` on the condition it is approved by `from` * @param from The address holding the tokens being transferred @@ -194,20 +194,20 @@ contract premium is module, safeMath { Launch a transaction where we transfer from a given address to another one. It can only be called by an address which was allowed before. Transaction fee will be charged too. If the receiver is not a natural address but also a person then she/he will be invited as well - + @from From who? @to For who? @amount Amount - + @success If the function was successful. */ if ( from != msg.sender ) { - var (_success, _reamining, _nonce) = db.getAllowance(from, msg.sender); + (bool _success, uint256 _reamining, uint256 _nonce) = db.getAllowance(from, msg.sender); require( _success ); _reamining = safeSub(_reamining, amount); _nonce = safeAdd(_nonce, 1); require( db.setAllowance(from, msg.sender, _reamining, _nonce) ); - AllowanceUsed(msg.sender, from, amount); + emit AllowanceUsed(msg.sender, from, amount); } bytes memory _data; if ( isContract(to) ) { @@ -215,10 +215,10 @@ contract premium is module, safeMath { } else { _transfer( from, to, amount); } - Transfer(from, to, amount, _data); + emit Transfer(from, to, amount, _data); return true; } - + /** * @notice Send `amount` Corion tokens to `to` from `msg.sender` and notify the receiver from your transaction with your `extraData` data * @param to The contract address of the recipient @@ -226,15 +226,15 @@ contract premium is module, safeMath { * @param extraData Data to give forward to the receiver * @return Whether the transfer was successful or not */ - function transfer(address to, uint256 amount, bytes extraData) isReady external returns (bool success) { + function transfer(address to, uint256 amount, bytes calldata extraData) isReady external returns (bool success) { /* Launch a transaction where we transfer from a given address to another one. After thetransaction the approvedCorionPremiumToken function of the receiver’s address is going to be called with the given data. - + @to For who? @amount Amount @extraData Extra data that will be given to the receiver - + @success If the function was successful. */ if ( isContract(to) ) { @@ -242,74 +242,74 @@ contract premium is module, safeMath { } else { _transfer( msg.sender, to, amount); } - Transfer(msg.sender, to, amount, extraData); + emit Transfer(msg.sender, to, amount, extraData); return true; } - - function transferToContract(address from, address to, uint256 amount, bytes extraData) internal { + + function transferToContract(address from, address to, uint256 amount, bytes memory extraData) internal { /* Inner function in order to transact a contract. - + @to For who? @amount Amount @extraData Extra data that will be given to the receiver */ _transfer(from, to, amount); - var (_success, _back) = thirdPartyPContractAbstract(to).receiveCorionPremiumToken(from, amount, extraData); + (bool _success, uint256 _back) = thirdPartyPContractAbstract(to).receiveCorionPremiumToken(from, amount, extraData); require( _success ); require( amount > _back ); if ( _back > 0 ) { _transfer(to, from, _back); } } - + function _transfer(address from, address to, uint256 amount) isReady internal { /* Inner function to launch a transaction. During the ICO transactions are only possible from the genesis address. 0xa636a97578d26a3b76b060bbc18226d954cf3757 address is blacklisted. - + @from From how? @to For who? @amount Amount */ - require( from != 0x00 && to != 0x00 && to != 0xa636a97578d26a3b76b060bbc18226d954cf3757 ); + require( from != address(0x00) && to != address(0x00) && to != 0xa636A97578d26A3b76B060Bbc18226d954cf3757 ); require( ( ! isICO) || genesis[from] ); require( db.decrease(from, amount) ); require( db.increase(to, amount) ); } - + function mint(address owner, uint256 value) external returns (bool success) { /* Generating tokens. It can be called only by ICO contract. - + @owner Address @value Amount. - + @success Was the Function successful? */ require( msg.sender == icoAddr || isICO ); _mint(owner, value); return true; } - + function _mint(address owner, uint256 value) isReady internal { /* Inner function to create a token. - + @owner Address of crediting the token. @value Amount */ require( db.increase(owner, value) ); - Mint(owner, value); + emit Mint(owner, value); } - + function isContract(address addr) internal returns (bool success) { /* Inner function in order to check if the given address is a natural address or a contract. - + @addr The address which is needed to be checked. - + @success Is the address crontact or not */ uint256 _codeLength; @@ -318,26 +318,26 @@ contract premium is module, safeMath { } return _codeLength > 0; } - - function balanceOf(address owner) constant returns (uint256 value) { + + function balanceOf(address owner) public view returns (uint256 value) { /* Token balance query - + @owner Address @value Balance of address */ return db.balanceOf(owner); } - - function totalSupply() constant returns (uint256 value) { + + function totalSupply() public view returns (uint256 value) { /* Total token quantity query - + @value Total token quantity */ return db.totalSupply(); } - + event AllowanceUsed(address indexed spender, address indexed owner, uint256 indexed value); event Mint(address indexed addr, uint256 indexed value); event Burn(address indexed addr, uint256 indexed value); diff --git a/test/compilationTests/corion/provider.sol b/test/compilationTests/corion/provider.sol index 5fa90fcd..48ec3adb 100644 --- a/test/compilationTests/corion/provider.sol +++ b/test/compilationTests/corion/provider.sol @@ -1,4 +1,4 @@ -pragma solidity ^0.4.11; +pragma solidity >=0.0; import "./module.sol"; import "./moduleHandler.sol"; @@ -12,15 +12,15 @@ contract provider is module, safeMath, announcementTypes { function connectModule() external returns (bool success) { require( super.isModuleHandler(msg.sender) ); super._connectModule(); - var (_success, currentSchellingRound) = moduleHandler(moduleHandlerAddress).getCurrentSchellingRoundID(); + (bool _success, uint256 currentSchellingRound) = moduleHandler(moduleHandlerAddress).getCurrentSchellingRoundID(); require( _success ); return true; } - function transferEvent(address from, address to, uint256 value) external returns (bool success) { + function transferEvent(address payable from, address payable to, uint256 value) external returns (bool success) { /* - Transaction completed. This function is ony available for the modulehandler. + Transaction completed. This function is only available for the modulehandler. It should be checked if the sender or the acceptor does not connect to the provider or it is not a provider itself if so than the change should be recorded. - + @from From whom? @to For who? @value amount @@ -35,10 +35,10 @@ contract provider is module, safeMath, announcementTypes { /* New schelling round. This function is only available for the moduleHandler. We are recording the new schelling round and we are storing the whole current quantity of the tokens. - We generate a reward quantity of tokens directed to the providers address. The collected interest will be tranfered from this contract. - + We generate a reward quantity of tokens directed to the providers address. The collected interest will be transferred from this contract. + @roundID Number of the schelling round. - @reward token emission + @reward token emission @bool Was the function successful? */ require( super.isModuleHandler(msg.sender) ); @@ -49,8 +49,8 @@ contract provider is module, safeMath, announcementTypes { return true; } modifier isReady { - var (_success, _active) = super.isActive(); - require( _success && _active ); + (bool _success, bool _active) = super.isActive(); + require( _success && _active ); _; } /* @@ -98,13 +98,13 @@ contract provider is module, safeMath, announcementTypes { uint256 currentHeight; } mapping(address => _providers) private providers; - + struct _globalFunds { uint256 reward; uint256 supply; } mapping(uint256 => _globalFunds) private globalFunds; - + struct _client{ address providerAddress; uint256 providerHeight; @@ -115,13 +115,13 @@ contract provider is module, safeMath, announcementTypes { uint256 paidUpTo; } mapping(address => _client) private clients; - + uint256 private currentSchellingRound = 1; - function provider(address _moduleHandler) { + constructor(address payable _moduleHandler) public { /* Install function. - + @_moduleHandler Address of the moduleHandler. */ super.registerModuleHandler(_moduleHandler); @@ -129,7 +129,7 @@ contract provider is module, safeMath, announcementTypes { function configure(announcementType a, uint256 b) external returns(bool) { /* Configuration of the provider. Can be invited just by the moduleHandler. - + @a Type of the setting @b value */ @@ -147,10 +147,10 @@ contract provider is module, safeMath, announcementTypes { else { return false; } return true; } - function getUserDetails(address addr, uint256 schellingRound) public constant returns (address ProviderAddress, uint256 ProviderHeight, uint256 ConnectedOn, uint256 value) { + function getUserDetails(address payable addr, uint256 schellingRound) public view returns (address ProviderAddress, uint256 ProviderHeight, uint256 ConnectedOn, uint256 value) { /* Collecting the datas of the client. - + @addr Address of the client. @schellingRound Number of the schelling round. If it is not defined then the current one. @ProviderAddress Address of the provider the one where connected to @@ -161,21 +161,21 @@ contract provider is module, safeMath, announcementTypes { if ( schellingRound == 0 ) { schellingRound = currentSchellingRound; } - if ( clients[addr].providerAddress != 0 ) { + if ( clients[addr].providerAddress != address(0x00) ) { ProviderAddress = clients[addr].providerAddress; ProviderHeight = clients[addr].providerHeight; ConnectedOn = clients[addr].providerConnected; value = clients[addr].supply[schellingRound]; } } - function rightForInterest(uint256 value, bool priv) internal returns (bool) { + function rightForInterest(uint256 value, bool priv) internal view returns (bool) { /* the share from the token emission. In case is a private provider it has to be checked if it has enough connected capital to be able to accept share from the token emission. The provider’s account counts as a capital for the emission as well. - + @value amount of the connected capital - @priv Is the provider private or not? + @priv Is the provider private or not? @bool Gets the share from the token emission. */ if ( priv ) { @@ -187,13 +187,13 @@ contract provider is module, safeMath, announcementTypes { /* It checks if the provider has enough connected captital to be able to get from the token emission. In case the provider is not able to get the share from the token emission then the connected capital will not count to the value of the globalFunds, to the current schelling round. - - @oldValue old + + @oldValue old @newValue new @priv Is the provider private? */ - var a = rightForInterest(oldValue, priv); - var b = rightForInterest(newValue, priv); + bool a = rightForInterest(oldValue, priv); + bool b = rightForInterest(newValue, priv); if ( a && b ) { globalFunds[currentSchellingRound].supply = globalFunds[currentSchellingRound].supply - oldValue + newValue; } else if ( a && ! b ) { @@ -205,15 +205,15 @@ contract provider is module, safeMath, announcementTypes { function checkCorrectRate(bool priv, uint8 rate) internal returns(bool) { /* Inner function which checks if the amount of interest what is given by the provider is fits to the criteria. - + @priv Is the provider private or not? @rate Percentage/rate of the interest @bool Correct or not? */ - return ( ! priv && ( rate >= publicMinRate && rate <= publicMaxRate ) ) || + return ( ! priv && ( rate >= publicMinRate && rate <= publicMaxRate ) ) || ( priv && ( rate >= privateMinRate && rate <= privateMaxRate ) ); } - function createProvider(bool priv, string name, string website, string country, string info, uint8 rate, bool isForRent, address admin) isReady external { + function createProvider(bool priv, string calldata name, string calldata website, string calldata country, string calldata info, uint8 rate, bool isForRent, address payable admin) isReady external { /* Creating a provider. During the ICO its not allowed to create provider. @@ -222,18 +222,18 @@ contract provider is module, safeMath, announcementTypes { For opening, has to have enough capital. All the functions of the provider except of the closing are going to be handled by the admin. The provider can be start as a rent as well, in this case the isForRent has to be true/correct. In case it runs as a rent the 20% of the profit will belong to the leser and the rest goes to the admin. - + @priv Privat szolgaltato e. Is private provider? @name Provider’s name. @website Provider’s website @country Provider’s country @info Provider’s short introduction. - @rate Rate of the emission what is going to be transfered to the client by the provider. + @rate Rate of the emission what is going to be transferred to the client by the provider. @isForRent is for Rent or not? @admin The admin’s address */ require( ! providers[msg.sender].data[providers[msg.sender].currentHeight].valid ); - require( clients[msg.sender].providerAddress == 0x00 ); + require( clients[msg.sender].providerAddress == address(0x00) ); require( ! checkICO() ); if ( priv ) { require( getTokenBalance(msg.sender) >= minFundsForPrivate ); @@ -241,11 +241,11 @@ contract provider is module, safeMath, announcementTypes { require( getTokenBalance(msg.sender) >= minFundsForPublic ); } require( checkCorrectRate(priv, rate) ); - + providers[msg.sender].currentHeight++; - var currHeight = providers[msg.sender].currentHeight; + uint256 currHeight = providers[msg.sender].currentHeight; providers[msg.sender].data[currHeight].valid = true; - if ( admin == 0x00 ) { + if ( admin == address(0x00) ) { providers[msg.sender].data[currHeight].admin = msg.sender; } else { providers[msg.sender].data[currHeight].admin = admin; @@ -268,27 +268,27 @@ contract provider is module, safeMath, announcementTypes { } else { delete providers[msg.sender].data[currHeight].supply[currentSchellingRound]; } - EProviderOpen(msg.sender, currHeight); + emit EProviderOpen(msg.sender, currHeight); } - function setProviderDetails(address addr, string website, string country, string info, uint8 rate, address admin) isReady external { + function setProviderDetails(address payable addr, string calldata website, string calldata country, string calldata info, uint8 rate, address payable admin) isReady external { /* Modifying the datas of the provider. This can only be invited by the provider’s admin. The emission rate is only valid for the next schelling round for this one it is not. The admin can only be changed by the address of the provider. - + @addr Address of the provider. @website Website. - @admin The new address of the admin. If we do not want to set it then we should enter 0X00. + @admin The new address of the admin. If we do not want to set it then we should enter 0X00. @country Country @info Short intro. @rate Rate of the emission what will be given to the client. */ - var currHeight = providers[addr].currentHeight; + uint256 currHeight = providers[addr].currentHeight; require( providers[addr].data[currHeight].valid ); require( checkCorrectRate(providers[addr].data[currHeight].priv, rate) ); require( providers[addr].data[currHeight].admin == msg.sender || msg.sender == addr ); - if ( admin != 0x00 ) { + if ( admin != address(0x00) ) { require( msg.sender == addr ); providers[addr].data[currHeight].admin = admin; } @@ -297,13 +297,13 @@ contract provider is module, safeMath, announcementTypes { providers[addr].data[currHeight].country = country; providers[addr].data[currHeight].info = info; providers[addr].data[currHeight].currentRate = rate; - EProviderDetailsChanged(addr, currHeight, website, country, info, rate, admin); + emit EProviderDetailsChanged(addr, currHeight, website, country, info, rate, admin); } - function getProviderInfo(address addr, uint256 height) public constant returns (string name, string website, string country, string info, uint256 create) { + function getProviderInfo(address addr, uint256 height) public view returns (string memory name, string memory website, string memory country, string memory info, uint256 create) { /* for the infos of the provider. In case the height is unknown then the system will use the last known height. - + @addr Addr of the provider @height Height @name Name of the provider. @@ -321,14 +321,14 @@ contract provider is module, safeMath, announcementTypes { info = providers[addr].data[height].info; create = providers[addr].data[height].create; } - function getProviderDetails(address addr, uint256 height) public constant returns (uint8 rate, bool isForRent, uint256 clientsCount, bool priv, bool getInterest, bool valid) { + function getProviderDetails(address payable addr, uint256 height) public view returns (uint8 rate, bool isForRent, uint256 clientsCount, bool priv, bool getInterest, bool valid) { /* Asking for the datas of the provider. In case the height is unknown then the system will use the last known height. @addr Address of the provider @height Height - @rate The rate of the emission which will be transfered to the client. + @rate The rate of the emission which will be transferred to the client. @isForRent Rent or not. @clientsCount Number of the clients. @priv Private or not? @@ -345,10 +345,10 @@ contract provider is module, safeMath, announcementTypes { getInterest = rightForInterest(getProviderCurrentSupply(addr), providers[addr].data[height].priv ); valid = providers[addr].data[height].valid; } - function getProviderCurrentSupply(address addr) internal returns (uint256) { + function getProviderCurrentSupply(address addr) internal view returns (uint256) { /* Inner function for polling the current height and the current quantity of the connected capital of the schelling round. - + @addr Provider’s address. @uint256 Amount of the connected capital */ @@ -356,51 +356,51 @@ contract provider is module, safeMath, announcementTypes { } function closeProvider() isReady external { /* - Closing and inactivate the provider. + Closing and deactivating the provider. It is only possible to close that active provider which is owned by the sender itself after calling the whole share of the emission. Whom were connected to the provider those clients will have to disconnect after they’ve called their share of emission which was not called before. */ - var currHeight = providers[msg.sender].currentHeight; + uint256 currHeight = providers[msg.sender].currentHeight; require( providers[msg.sender].data[currHeight].valid ); require( providers[msg.sender].data[currHeight].paidUpTo == currentSchellingRound ); - + providers[msg.sender].data[currHeight].valid = false; providers[msg.sender].data[currHeight].close = currentSchellingRound; setRightForInterest(getProviderCurrentSupply(msg.sender), 0, providers[msg.sender].data[currHeight].priv); - EProviderClose(msg.sender, currHeight); + emit EProviderClose(msg.sender, currHeight); } - function allowUsers(address provider, address[] addr) isReady external { + function allowUsers(address provider, address[] calldata addr) isReady external { /* Permition of the user to be able to connect to the provider. This can only be invited by the provider’s admin. - With this kind of call only 100 address can be permited. - + With this kind of call only 100 address can be permitted. + @addr Array of the addresses for whom the connection is allowed. */ - var currHeight = providers[provider].currentHeight; + uint256 currHeight = providers[provider].currentHeight; require( providers[provider].data[currHeight].valid ); require( providers[provider].data[currHeight].priv ); require( providers[provider].data[currHeight].admin == msg.sender ); require( addr.length <= 100 ); - + for ( uint256 a=0 ; a<addr.length ; a++ ) { providers[provider].data[currHeight].allowedUsers[addr[a]] = true; } } - function disallowUsers(address provider, address[] addr) isReady external { + function disallowUsers(address provider, address[] calldata addr) isReady external { /* Disable of the user not to be able to connect to the provider. It is can called only for the admin of the provider. - With this kind of call only 100 address can be permited. - + With this kind of call only 100 address can be permitted. + @addr Array of the addresses for whom the connection is allowed. */ - var currHeight = providers[provider].currentHeight; + uint256 currHeight = providers[provider].currentHeight; require( providers[provider].data[currHeight].valid ); require( providers[provider].data[currHeight].priv ); require( providers[provider].data[currHeight].admin == msg.sender ); require( addr.length <= 100 ); - + for ( uint256 a=0 ; a<addr.length ; a++ ) { delete providers[provider].data[currHeight].allowedUsers[addr[a]]; } @@ -411,23 +411,23 @@ contract provider is module, safeMath, announcementTypes { Providers can not connect to other providers. If is a client at any provider, then it is not possible to connect to other provider one. It is only possible to connect to valid and active providers. - If is an active provider then the client can only connect, if address is permited at the provider (Whitelist). + If is an active provider then the client can only connect, if address is permitted at the provider (Whitelist). At private providers, the number of the client is restricted. If it reaches the limit no further clients are allowed to connect. This process has a transaction fee based on the senders whole token quantity. - + @provider Address of the provider. */ - var currHeight = providers[provider].currentHeight; + uint256 currHeight = providers[provider].currentHeight; require( ! providers[msg.sender].data[currHeight].valid ); - require( clients[msg.sender].providerAddress == 0x00 ); + require( clients[msg.sender].providerAddress == address(0x00) ); require( providers[provider].data[currHeight].valid ); if ( providers[provider].data[currHeight].priv ) { require( providers[provider].data[currHeight].allowedUsers[msg.sender] && providers[provider].data[currHeight].clientsCount < privateProviderLimit ); } - var bal = getTokenBalance(msg.sender); + uint256 bal = getTokenBalance(msg.sender); require( moduleHandler(moduleHandlerAddress).processTransactionFee(msg.sender, bal) ); - + checkFloatingSupply(provider, currHeight, false, bal); providers[provider].data[currHeight].clientsCount++; clients[msg.sender].providerAddress = provider; @@ -437,7 +437,7 @@ contract provider is module, safeMath, announcementTypes { clients[msg.sender].paidUpTo = currentSchellingRound; clients[msg.sender].lastRate = providers[provider].data[currHeight].currentRate; clients[msg.sender].providerConnected = now; - ENewClient(msg.sender, provider, currHeight, bal); + emit ENewClient(msg.sender, provider, currHeight, bal); } function partProvider() isReady external { /* @@ -445,9 +445,9 @@ contract provider is module, safeMath, announcementTypes { Before disconnecting we should poll our share from the token emission even if there was nothing factually. It is only possible to disconnect those providers who were connected by us before. */ - var provider = clients[msg.sender].providerAddress; - require( provider != 0x0 ); - var currHeight = clients[msg.sender].providerHeight; + address provider = clients[msg.sender].providerAddress; + require( provider != address(0x00) ); + uint256 currHeight = clients[msg.sender].providerHeight; bool providerHasClosed = false; if ( providers[provider].data[currHeight].close > 0 ) { providerHasClosed = true; @@ -455,8 +455,8 @@ contract provider is module, safeMath, announcementTypes { } else { require( clients[msg.sender].paidUpTo == currentSchellingRound ); } - - var bal = getTokenBalance(msg.sender); + + uint256 bal = getTokenBalance(msg.sender); if ( ! providerHasClosed ) { providers[provider].data[currHeight].clientsCount--; checkFloatingSupply(provider, currHeight, true, bal); @@ -467,19 +467,19 @@ contract provider is module, safeMath, announcementTypes { delete clients[msg.sender].paidUpTo; delete clients[msg.sender].lastRate; delete clients[msg.sender].providerConnected; - EClientLost(msg.sender, provider, currHeight, bal); + emit EClientLost(msg.sender, provider, currHeight, bal); } - function checkReward(address addr) public constant returns (uint256 reward) { + function checkReward(address addr) public returns (uint256 reward) { /* Polling the share from the token emission for clients and for providers. - + @addr The address want to check. @reward Accumulated amount. */ if ( providers[addr].data[providers[addr].currentHeight].valid ) { uint256 a; (reward, a) = getProviderReward(addr, 0); - } else if ( clients[addr].providerAddress != 0x0 ) { + } else if ( clients[addr].providerAddress != address(0x00) ) { reward = getClientReward(0); } } @@ -487,34 +487,34 @@ contract provider is module, safeMath, announcementTypes { /* Polling the share from the token emission token emission for clients and for providers. - It is optionaly possible to give an address of a beneficiary for whom we can transfer the accumulated amount. In case we don’t enter any address then the amount will be transfered to the caller’s address. + It is optionally possible to give an address of a beneficiary for whom we can transfer the accumulated amount. In case we don’t enter any address then the amount will be transferred to the caller’s address. As the interest should be checked at each schelling round in order to get the share from that so to avoid the overflow of the gas the number of the check-rounds should be limited. - Opcionalisan megadhato az ellenorzes koreinek szama. It is possible to enter optionaly the number of the check-rounds. If it is 0 then it is automatic. + Opcionalisan megadhato az ellenorzes koreinek szama. It is possible to enter optionally the number of the check-rounds. If it is 0 then it is automatic. Provider variable should only be entered if the real owner of the provider is not the caller’s address. In case the client/provider was far behind then it is possible that this function should be called several times to check the total generated schelling rounds and to collect the share. - If is neighter a client nor a provider then the function is not available. + If is neither a client nor a provider then the function is not available. The tokens will be sent to the beneficiary from the address of the provider without any transaction fees. - + @beneficiary Address of the beneficiary @limit Quota of the check-rounds. @provider Address of the provider @reward Accumulated amount from the previous rounds. */ - var _limit = limit; - var _beneficiary = beneficiary; - var _provider = provider; + uint256 _limit = limit; + address _beneficiary = beneficiary; + address _provider = provider; if ( _limit == 0 ) { _limit = gasProtectMaxRounds; } - if ( _beneficiary == 0x00 ) { _beneficiary = msg.sender; } - if ( _provider == 0x00 ) { _provider = msg.sender; } + if ( _beneficiary == address(0x00) ) { _beneficiary = msg.sender; } + if ( _provider == address(0x00) ) { _provider = msg.sender; } uint256 clientReward; uint256 providerReward; if ( providers[_provider].data[providers[_provider].currentHeight].valid ) { require( providers[_provider].data[providers[_provider].currentHeight].admin == msg.sender || msg.sender == _provider ); (providerReward, clientReward) = getProviderReward(_provider, _limit); - } else if ( clients[msg.sender].providerAddress != 0x00 ) { + } else if ( clients[msg.sender].providerAddress != address(0x00) ) { clientReward = getClientReward(_limit); } else { - throw; + revert(); } if ( clientReward > 0 ) { require( moduleHandler(moduleHandlerAddress).transfer(address(this), _beneficiary, clientReward, false) ); @@ -522,12 +522,12 @@ contract provider is module, safeMath, announcementTypes { if ( providerReward > 0 ) { require( moduleHandler(moduleHandlerAddress).transfer(address(this), provider, providerReward, false) ); } - EReward(msg.sender, provider, clientReward, providerReward); + emit EReward(msg.sender, provider, clientReward, providerReward); } function getClientReward(uint256 limit) internal returns (uint256 reward) { /* Inner function for the client in order to collect the share from the token emission - + @limit Quota of checking the schelling-rounds. @reward Collected token amount from the checked rounds. */ @@ -536,8 +536,9 @@ contract provider is module, safeMath, announcementTypes { address provAddr; uint256 provHeight; bool interest = false; - var rate = clients[msg.sender].lastRate; - for ( uint256 a = (clients[msg.sender].paidUpTo + 1) ; a <= currentSchellingRound ; a++ ) { + uint256 a; + uint8 rate = clients[msg.sender].lastRate; + for ( a = (clients[msg.sender].paidUpTo + 1) ; a <= currentSchellingRound ; a++ ) { if (globalFunds[a].reward > 0 && globalFunds[a].supply > 0) { provAddr = clients[msg.sender].providerAddress; provHeight = clients[msg.sender].providerHeight; @@ -573,7 +574,7 @@ contract provider is module, safeMath, announcementTypes { } function getProviderReward(address addr, uint256 limit) internal returns (uint256 providerReward, uint256 adminReward) { /* - Inner function for the provider in order to collect the share from the token emission + Inner function for the provider in order to collect the share from the token emission @addr Address of the provider. @limit Quota of the check-rounds. @providerReward The reward of the provider’s address from the checked rounds. @@ -585,14 +586,15 @@ contract provider is module, safeMath, announcementTypes { uint256 steps; uint256 currHeight = providers[addr].currentHeight; uint256 LTSID = providers[addr].data[currHeight].lastSupplyID; - var rate = providers[addr].data[currHeight].lastPaidRate; - for ( uint256 a = (providers[addr].data[currHeight].paidUpTo + 1) ; a <= currentSchellingRound ; a++ ) { + uint256 a; + uint8 rate = providers[addr].data[currHeight].lastPaidRate; + for ( a = (providers[addr].data[currHeight].paidUpTo + 1) ; a <= currentSchellingRound ; a++ ) { if (globalFunds[a].reward > 0 && globalFunds[a].supply > 0) { if ( providers[addr].data[currHeight].rateHistory[a].valid ) { rate = providers[addr].data[currHeight].rateHistory[a].value; } if ( rate > 0 ) { - if ( ( a > LTSID && rightForInterest(providers[addr].data[currHeight].supply[LTSID], providers[addr].data[currHeight].priv) || + if ( ( a > LTSID && rightForInterest(providers[addr].data[currHeight].supply[LTSID], providers[addr].data[currHeight].priv) || rightForInterest(providers[addr].data[currHeight].supply[a], providers[addr].data[currHeight].priv) ) ) { if ( limit > 0 && steps > limit ) { a--; @@ -634,7 +636,7 @@ contract provider is module, safeMath, announcementTypes { /* Inner function for updating the database when some token change has happened. In this case we are checking if despite the changes the provider is still entitled to the token emission. In case the legitimacy changes then the global supply should be set as well. - + @providerAddress Provider address. @providerHeight Provider height. @neg the change was negative or not @@ -644,15 +646,15 @@ contract provider is module, safeMath, announcementTypes { if ( currentSchellingRound != LSID ) { if ( neg ) { setRightForInterest( - providers[providerAddress].data[providerHeight].supply[LSID], - providers[providerAddress].data[providerHeight].supply[LSID] - value, + providers[providerAddress].data[providerHeight].supply[LSID], + providers[providerAddress].data[providerHeight].supply[LSID] - value, providers[providerAddress].data[providerHeight].priv ); providers[providerAddress].data[providerHeight].supply[currentSchellingRound] = providers[providerAddress].data[providerHeight].supply[LSID] - value; } else { setRightForInterest( - providers[providerAddress].data[providerHeight].supply[LSID], - providers[providerAddress].data[providerHeight].supply[LSID] + value, + providers[providerAddress].data[providerHeight].supply[LSID], + providers[providerAddress].data[providerHeight].supply[LSID] + value, providers[providerAddress].data[providerHeight].priv ); providers[providerAddress].data[providerHeight].supply[currentSchellingRound] = providers[providerAddress].data[providerHeight].supply[LSID] + value; @@ -661,15 +663,15 @@ contract provider is module, safeMath, announcementTypes { } else { if ( neg ) { setRightForInterest( - getProviderCurrentSupply(providerAddress), - getProviderCurrentSupply(providerAddress) - value, + getProviderCurrentSupply(providerAddress), + getProviderCurrentSupply(providerAddress) - value, providers[providerAddress].data[providerHeight].priv ); providers[providerAddress].data[providerHeight].supply[currentSchellingRound] -= value; } else { setRightForInterest( - getProviderCurrentSupply(providerAddress), - getProviderCurrentSupply(providerAddress) + value, + getProviderCurrentSupply(providerAddress), + getProviderCurrentSupply(providerAddress) + value, providers[providerAddress].data[providerHeight].priv ); providers[providerAddress].data[providerHeight].supply[currentSchellingRound] += value; @@ -681,7 +683,7 @@ contract provider is module, safeMath, announcementTypes { Inner function for updating the database in case token change has happened. In this case we check if the provider despite the changes is still entitled to the token emission. We just call this only if the private provider and it’s own capital bears emission. - + @providerAddress Provider address. @providerHeight Provider height. @neg Was the change negative? @@ -691,15 +693,15 @@ contract provider is module, safeMath, announcementTypes { if ( currentSchellingRound != LSID ) { if ( neg ) { setRightForInterest( - providers[providerAddress].data[providerHeight].ownSupply[LSID], - providers[providerAddress].data[providerHeight].ownSupply[LSID] - value, + providers[providerAddress].data[providerHeight].ownSupply[LSID], + providers[providerAddress].data[providerHeight].ownSupply[LSID] - value, true ); providers[providerAddress].data[providerHeight].ownSupply[currentSchellingRound] = providers[providerAddress].data[providerHeight].ownSupply[LSID] - value; } else { setRightForInterest( - providers[providerAddress].data[providerHeight].ownSupply[LSID], - providers[providerAddress].data[providerHeight].ownSupply[LSID] + value, + providers[providerAddress].data[providerHeight].ownSupply[LSID], + providers[providerAddress].data[providerHeight].ownSupply[LSID] + value, true ); providers[providerAddress].data[providerHeight].ownSupply[currentSchellingRound] = providers[providerAddress].data[providerHeight].ownSupply[LSID] + value; @@ -708,15 +710,15 @@ contract provider is module, safeMath, announcementTypes { } else { if ( neg ) { setRightForInterest( - getProviderCurrentSupply(providerAddress), - getProviderCurrentSupply(providerAddress) - value, + getProviderCurrentSupply(providerAddress), + getProviderCurrentSupply(providerAddress) - value, true ); providers[providerAddress].data[providerHeight].ownSupply[currentSchellingRound] -= value; } else { setRightForInterest( - getProviderCurrentSupply(providerAddress), - getProviderCurrentSupply(providerAddress) + value, + getProviderCurrentSupply(providerAddress), + getProviderCurrentSupply(providerAddress) + value, true ); providers[providerAddress].data[providerHeight].ownSupply[currentSchellingRound] += value; @@ -726,7 +728,7 @@ contract provider is module, safeMath, announcementTypes { function TEMath(uint256 a, uint256 b, bool neg) internal returns (uint256) { /* Inner function for the changes of the numbers - + @a First number @b 2nd number @neg Operation with numbers. If it is TRUE then subtraction, if it is FALSE then addition. @@ -738,12 +740,12 @@ contract provider is module, safeMath, announcementTypes { /* Inner function for perceiving the changes of the balance and updating the database. If the address is a provider and the balance is decreasing than can not let it go under the minimum level. - + @addr The address where the change happened. @value Rate of the change. @neg ype of the change. If it is TRUE then the balance has been decreased if it is FALSE then it has been increased. */ - if ( clients[addr].providerAddress != 0 ) { + if ( clients[addr].providerAddress != address(0x00) ) { checkFloatingSupply(clients[addr].providerAddress, providers[clients[addr].providerAddress].currentHeight, ! neg, value); if (clients[addr].lastSupplyID != currentSchellingRound) { clients[addr].supply[currentSchellingRound] = TEMath(clients[addr].supply[clients[addr].lastSupplyID], value, neg); @@ -752,7 +754,7 @@ contract provider is module, safeMath, announcementTypes { clients[addr].supply[currentSchellingRound] = TEMath(clients[addr].supply[currentSchellingRound], value, neg); } } else if ( providers[addr].data[providers[addr].currentHeight].valid ) { - var currentHeight = providers[addr].currentHeight; + uint256 currentHeight = providers[addr].currentHeight; if ( neg ) { uint256 balance = getTokenBalance(addr); if ( providers[addr].data[currentHeight].priv ) { @@ -769,22 +771,22 @@ contract provider is module, safeMath, announcementTypes { function getTokenBalance(address addr) internal returns (uint256 balance) { /* Inner function in order to poll the token balance of the address. - + @addr Address - + @balance Balance of the address. */ - var (_success, _balance) = moduleHandler(moduleHandlerAddress).balanceOf(addr); + (bool _success, uint256 _balance) = moduleHandler(moduleHandlerAddress).balanceOf(addr); require( _success ); return _balance; } function checkICO() internal returns (bool isICO) { /* Inner function to check the ICO status. - - @isICO Is the ICO in proccess or not? + + @isICO Is the ICO in process or not? */ - var (_success, _isICO) = moduleHandler(moduleHandlerAddress).isICO(); + (bool _success, bool _isICO) = moduleHandler(moduleHandlerAddress).isICO(); require( _success ); return _isICO; } diff --git a/test/compilationTests/corion/publisher.sol b/test/compilationTests/corion/publisher.sol index e94b9a92..991fc8f4 100644 --- a/test/compilationTests/corion/publisher.sol +++ b/test/compilationTests/corion/publisher.sol @@ -1,278 +1,278 @@ -pragma solidity ^0.4.11;
-
-import "./announcementTypes.sol";
-import "./module.sol";
-import "./moduleHandler.sol";
-import "./safeMath.sol";
-
-contract publisher is announcementTypes, module, safeMath {
- /*
- module callbacks
- */
- function transferEvent(address from, address to, uint256 value) external returns (bool success) {
- /*
- Transaction completed. This function is available only for moduleHandler
- If a transaction is carried out from or to an address which participated in the objection of an announcement, its objection purport is automatically set
- */
- require( super.isModuleHandler(msg.sender) );
- uint256 announcementID;
- uint256 a;
- // need reverse lookup
- for ( a=0 ; a<opponents[from].length ; a++ ) {
- announcementID = opponents[msg.sender][a];
- if ( announcements[announcementID].end < block.number && announcements[announcementID].open ) {
- announcements[announcementID].oppositionWeight = safeSub(announcements[a].oppositionWeight, value);
- }
- }
- for ( a=0 ; a<opponents[to].length ; a++ ) {
- announcementID = opponents[msg.sender][a];
- if ( announcements[announcementID].end < block.number && announcements[announcementID].open ) {
- announcements[announcementID].oppositionWeight = safeAdd(announcements[a].oppositionWeight, value);
- }
- }
- return true;
- }
-
- /*
- Pool
- */
-
- uint256 public minAnnouncementDelay = 40320;
- uint256 public minAnnouncementDelayOnICO = 17280;
- uint8 public oppositeRate = 33;
-
- struct announcements_s {
- announcementType Type;
- uint256 start;
- uint256 end;
- bool open;
- string announcement;
- string link;
- bool oppositable;
- uint256 oppositionWeight;
- bool result;
-
- string _str;
- uint256 _uint;
- address _addr;
- }
- mapping(uint256 => announcements_s) public announcements;
- uint256 announcementsLength = 1;
-
- mapping (address => uint256[]) public opponents;
-
- function publisher(address moduleHandler) {
- /*
- Installation function. The installer will be registered in the admin list automatically
-
- @moduleHandler Address of moduleHandler
- */
- super.registerModuleHandler(moduleHandler);
- }
-
- function Announcements(uint256 id) public constant returns (uint256 Type, uint256 Start, uint256 End, bool Closed, string Announcement, string Link, bool Opposited, string _str, uint256 _uint, address _addr) {
- /*
- Announcement data query
-
- @id Its identification
-
- @Type Subject of announcement
- @Start Height of announcement block
- @End Planned completion of announcement
- @Closed Closed or not
- @Announcement Announcement text
- @Link Link perhaps to a Forum
- @Opposited Objected or not
- @_str Text value
- @_uint Number value
- @_addr Address value
- */
- Type = uint256(announcements[id].Type);
- Start = announcements[id].start;
- End = announcements[id].end;
- Closed = ! announcements[id].open;
- Announcement = announcements[id].announcement;
- Link = announcements[id].link;
- if ( checkOpposited(announcements[id].oppositionWeight, announcements[id].oppositable) ) {
- Opposited = true;
- }
- _str = announcements[id]._str;
- _uint = announcements[id]._uint;
- _addr = announcements[id]._addr;
- }
-
- function checkOpposited(uint256 weight, bool oppositable) public constant returns (bool success) {
- /*
- Veto check
-
- @weight Purport of objections so far
- @oppositable Opposable at all
-
- @success Opposed or not
- */
- if ( ! oppositable ) { return false; }
- var (_success, _amount) = moduleHandler(moduleHandlerAddress).totalSupply();
- require( _success );
- return _amount * oppositeRate / 100 > weight;
- }
-
- function newAnnouncement(announcementType Type, string Announcement, string Link, bool Oppositable, string _str, uint256 _uint, address _addr) onlyOwner external {
- /*
- New announcement. Can be called only by those in the admin list
-
- @Type Topic of announcement
- @Start height of announcement block
- @End planned completion of announcement
- @Closed Completed or not
- @Announcement Announcement text
- @Link link to a Forum
- @Opposition opposed or not
- @_str text box
- @_uint number box
- @_addr address box
- */
- announcementsLength++;
- announcements[announcementsLength].Type = Type;
- announcements[announcementsLength].start = block.number;
- if ( checkICO() ) {
- announcements[announcementsLength].end = block.number + minAnnouncementDelayOnICO;
- } else {
- announcements[announcementsLength].end = block.number + minAnnouncementDelay;
- }
- announcements[announcementsLength].open = true;
- announcements[announcementsLength].announcement = Announcement;
- announcements[announcementsLength].link = Link;
- announcements[announcementsLength].oppositable = Oppositable;
- announcements[announcementsLength].oppositionWeight = 0;
- announcements[announcementsLength].result = false;
- announcements[announcementsLength]._str = _str;
- announcements[announcementsLength]._uint = _uint;
- announcements[announcementsLength]._addr = _addr;
- ENewAnnouncement(announcementsLength, Type);
- }
-
- function closeAnnouncement(uint256 id) onlyOwner external {
- /*
- Close announcement. It can be closed only by those in the admin list. Windup is allowed only after the announcement is completed.
-
- @id Announcement identification
- */
- require( announcements[id].open && announcements[id].end < block.number );
- if ( ! checkOpposited(announcements[id].oppositionWeight, announcements[id].oppositable) ) {
- announcements[id].result = true;
- if ( announcements[id].Type == announcementType.newModule ) {
- require( moduleHandler(moduleHandlerAddress).newModule(announcements[id]._str, announcements[id]._addr, true, true) );
- } else if ( announcements[id].Type == announcementType.dropModule ) {
- require( moduleHandler(moduleHandlerAddress).dropModule(announcements[id]._str, true) );
- } else if ( announcements[id].Type == announcementType.replaceModule ) {
- require( moduleHandler(moduleHandlerAddress).replaceModule(announcements[id]._str, announcements[id]._addr, true) );
- } else if ( announcements[id].Type == announcementType.replaceModuleHandler) {
- require( moduleHandler(moduleHandlerAddress).replaceModuleHandler(announcements[id]._addr) );
- } else if ( announcements[id].Type == announcementType.transactionFeeRate ||
- announcements[id].Type == announcementType.transactionFeeMin ||
- announcements[id].Type == announcementType.transactionFeeMax ||
- announcements[id].Type == announcementType.transactionFeeBurn ) {
- require( moduleHandler(moduleHandlerAddress).configureModule("token", announcements[id].Type, announcements[id]._uint) );
- } else if ( announcements[id].Type == announcementType.providerPublicFunds ||
- announcements[id].Type == announcementType.providerPrivateFunds ||
- announcements[id].Type == announcementType.providerPrivateClientLimit ||
- announcements[id].Type == announcementType.providerPublicMinRate ||
- announcements[id].Type == announcementType.providerPublicMaxRate ||
- announcements[id].Type == announcementType.providerPrivateMinRate ||
- announcements[id].Type == announcementType.providerPrivateMaxRate ||
- announcements[id].Type == announcementType.providerGasProtect ||
- announcements[id].Type == announcementType.providerInterestMinFunds ||
- announcements[id].Type == announcementType.providerRentRate ) {
- require( moduleHandler(moduleHandlerAddress).configureModule("provider", announcements[id].Type, announcements[id]._uint) );
- } else if ( announcements[id].Type == announcementType.schellingRoundBlockDelay ||
- announcements[id].Type == announcementType.schellingCheckRounds ||
- announcements[id].Type == announcementType.schellingCheckAboves ||
- announcements[id].Type == announcementType.schellingRate ) {
- require( moduleHandler(moduleHandlerAddress).configureModule("schelling", announcements[id].Type, announcements[id]._uint) );
- } else if ( announcements[id].Type == announcementType.publisherMinAnnouncementDelay) {
- minAnnouncementDelay = announcements[id]._uint;
- } else if ( announcements[id].Type == announcementType.publisherOppositeRate) {
- oppositeRate = uint8(announcements[id]._uint);
- }
- }
- announcements[id].end = block.number;
- announcements[id].open = false;
- }
-
- function oppositeAnnouncement(uint256 id) external {
- /*
- Opposition of announcement
- If announcement is opposable, anyone owning a token can oppose it
- Opposition is automatically with the total amount of tokens
- If the quantity of his tokens changes, the purport of his opposition changes automatically
- The prime time is the windup of the announcement, because this is the moment when the number of tokens in opposition are counted.
- One address is entitled to be in oppositon only once. An opposition cannot be withdrawn.
- Running announcements can be opposed only.
-
- @id Announcement identification
- */
- uint256 newArrayID = 0;
- bool foundEmptyArrayID = false;
- require( announcements[id].open );
- require( announcements[id].oppositable );
- for ( uint256 a=0 ; a<opponents[msg.sender].length ; a++ ) {
- require( opponents[msg.sender][a] != id );
- if ( ! announcements[opponents[msg.sender][a]].open) {
- delete opponents[msg.sender][a];
- if ( ! foundEmptyArrayID ) {
- foundEmptyArrayID = true;
- newArrayID = a;
- }
- }
- if ( ! foundEmptyArrayID ) {
- foundEmptyArrayID = true;
- newArrayID = a;
- }
- }
- var (_success, _balance) = moduleHandler(moduleHandlerAddress).balanceOf(msg.sender);
- require( _success );
- require( _balance > 0);
- if ( foundEmptyArrayID ) {
- opponents[msg.sender][newArrayID] = id;
- } else {
- opponents[msg.sender].push(id);
- }
- announcements[id].oppositionWeight += _balance;
- EOppositeAnnouncement(id, msg.sender, _balance);
- }
-
- function invalidateAnnouncement(uint256 id) onlyOwner external {
- /*
- Withdraw announcement. Only those in the admin list can withdraw it.
-
- @id Announcement identification
- */
- require( announcements[id].open );
- announcements[id].end = block.number;
- announcements[id].open = false;
- EInvalidateAnnouncement(id);
- }
-
- modifier onlyOwner() {
- /*
- Only the owner is allowed to call it.
- */
- require( moduleHandler(moduleHandlerAddress).owners(msg.sender) );
- _;
- }
-
- function checkICO() internal returns (bool isICO) {
- /*
- Inner function to check the ICO status.
- @bool Is the ICO in proccess or not?
- */
- var (_success, _isICO) = moduleHandler(moduleHandlerAddress).isICO();
- require( _success );
- return _isICO;
- }
-
- event ENewAnnouncement(uint256 id, announcementType typ);
- event EOppositeAnnouncement(uint256 id, address addr, uint256 value);
- event EInvalidateAnnouncement(uint256 id);
- event ECloseAnnouncement(uint256 id);
-}
+pragma solidity >=0.0; + +import "./announcementTypes.sol"; +import "./module.sol"; +import "./moduleHandler.sol"; +import "./safeMath.sol"; + +contract publisher is announcementTypes, module, safeMath { + /* + module callbacks + */ + function transferEvent(address payable from, address payable to, uint256 value) external returns (bool success) { + /* + Transaction completed. This function is available only for moduleHandler + If a transaction is carried out from or to an address which participated in the objection of an announcement, its objection purport is automatically set + */ + require( super.isModuleHandler(msg.sender) ); + uint256 announcementID; + uint256 a; + // need reverse lookup + for ( a=0 ; a<opponents[from].length ; a++ ) { + announcementID = opponents[msg.sender][a]; + if ( announcements[announcementID].end < block.number && announcements[announcementID].open ) { + announcements[announcementID].oppositionWeight = safeSub(announcements[a].oppositionWeight, value); + } + } + for ( a=0 ; a<opponents[to].length ; a++ ) { + announcementID = opponents[msg.sender][a]; + if ( announcements[announcementID].end < block.number && announcements[announcementID].open ) { + announcements[announcementID].oppositionWeight = safeAdd(announcements[a].oppositionWeight, value); + } + } + return true; + } + + /* + Pool + */ + + uint256 public minAnnouncementDelay = 40320; + uint256 public minAnnouncementDelayOnICO = 17280; + uint8 public oppositeRate = 33; + + struct announcements_s { + announcementType Type; + uint256 start; + uint256 end; + bool open; + string announcement; + string link; + bool oppositable; + uint256 oppositionWeight; + bool result; + + string _str; + uint256 _uint; + address payable _addr; + } + mapping(uint256 => announcements_s) public announcements; + uint256 announcementsLength = 1; + + mapping (address => uint256[]) public opponents; + + constructor(address payable moduleHandler) public { + /* + Installation function. The installer will be registered in the admin list automatically + + @moduleHandler Address of moduleHandler + */ + super.registerModuleHandler(moduleHandler); + } + + function Announcements(uint256 id) public view returns (uint256 Type, uint256 Start, uint256 End, bool Closed, string memory Announcement, string memory Link, bool Opposited, string memory _str, uint256 _uint, address _addr) { + /* + Announcement data query + + @id Its identification + + @Type Subject of announcement + @Start Height of announcement block + @End Planned completion of announcement + @Closed Closed or not + @Announcement Announcement text + @Link Link perhaps to a Forum + @Opposited Objected or not + @_str Text value + @_uint Number value + @_addr Address value + */ + Type = uint256(announcements[id].Type); + Start = announcements[id].start; + End = announcements[id].end; + Closed = ! announcements[id].open; + Announcement = announcements[id].announcement; + Link = announcements[id].link; + if ( checkOpposited(announcements[id].oppositionWeight, announcements[id].oppositable) ) { + Opposited = true; + } + _str = announcements[id]._str; + _uint = announcements[id]._uint; + _addr = announcements[id]._addr; + } + + function checkOpposited(uint256 weight, bool oppositable) public view returns (bool success) { + /* + Veto check + + @weight Purport of objections so far + @oppositable Opposable at all + + @success Opposed or not + */ + if ( ! oppositable ) { return false; } + (bool _success, uint256 _amount) = moduleHandler(moduleHandlerAddress).totalSupply(); + require( _success ); + return _amount * oppositeRate / 100 > weight; + } + + function newAnnouncement(announcementType Type, string calldata Announcement, string calldata Link, bool Oppositable, string calldata _str, uint256 _uint, address payable _addr) onlyOwner external { + /* + New announcement. Can be called only by those in the admin list + + @Type Topic of announcement + @Start height of announcement block + @End planned completion of announcement + @Closed Completed or not + @Announcement Announcement text + @Link link to a Forum + @Opposition opposed or not + @_str text box + @_uint number box + @_addr address box + */ + announcementsLength++; + announcements[announcementsLength].Type = Type; + announcements[announcementsLength].start = block.number; + if ( checkICO() ) { + announcements[announcementsLength].end = block.number + minAnnouncementDelayOnICO; + } else { + announcements[announcementsLength].end = block.number + minAnnouncementDelay; + } + announcements[announcementsLength].open = true; + announcements[announcementsLength].announcement = Announcement; + announcements[announcementsLength].link = Link; + announcements[announcementsLength].oppositable = Oppositable; + announcements[announcementsLength].oppositionWeight = 0; + announcements[announcementsLength].result = false; + announcements[announcementsLength]._str = _str; + announcements[announcementsLength]._uint = _uint; + announcements[announcementsLength]._addr = _addr; + emit ENewAnnouncement(announcementsLength, Type); + } + + function closeAnnouncement(uint256 id) onlyOwner external { + /* + Close announcement. It can be closed only by those in the admin list. Windup is allowed only after the announcement is completed. + + @id Announcement identification + */ + require( announcements[id].open && announcements[id].end < block.number ); + if ( ! checkOpposited(announcements[id].oppositionWeight, announcements[id].oppositable) ) { + announcements[id].result = true; + if ( announcements[id].Type == announcementType.newModule ) { + require( moduleHandler(moduleHandlerAddress).newModule(announcements[id]._str, announcements[id]._addr, true, true) ); + } else if ( announcements[id].Type == announcementType.dropModule ) { + require( moduleHandler(moduleHandlerAddress).dropModule(announcements[id]._str, true) ); + } else if ( announcements[id].Type == announcementType.replaceModule ) { + require( moduleHandler(moduleHandlerAddress).replaceModule(announcements[id]._str, announcements[id]._addr, true) ); + } else if ( announcements[id].Type == announcementType.replaceModuleHandler) { + require( moduleHandler(moduleHandlerAddress).replaceModuleHandler(announcements[id]._addr) ); + } else if ( announcements[id].Type == announcementType.transactionFeeRate || + announcements[id].Type == announcementType.transactionFeeMin || + announcements[id].Type == announcementType.transactionFeeMax || + announcements[id].Type == announcementType.transactionFeeBurn ) { + require( moduleHandler(moduleHandlerAddress).configureModule("token", announcements[id].Type, announcements[id]._uint) ); + } else if ( announcements[id].Type == announcementType.providerPublicFunds || + announcements[id].Type == announcementType.providerPrivateFunds || + announcements[id].Type == announcementType.providerPrivateClientLimit || + announcements[id].Type == announcementType.providerPublicMinRate || + announcements[id].Type == announcementType.providerPublicMaxRate || + announcements[id].Type == announcementType.providerPrivateMinRate || + announcements[id].Type == announcementType.providerPrivateMaxRate || + announcements[id].Type == announcementType.providerGasProtect || + announcements[id].Type == announcementType.providerInterestMinFunds || + announcements[id].Type == announcementType.providerRentRate ) { + require( moduleHandler(moduleHandlerAddress).configureModule("provider", announcements[id].Type, announcements[id]._uint) ); + } else if ( announcements[id].Type == announcementType.schellingRoundBlockDelay || + announcements[id].Type == announcementType.schellingCheckRounds || + announcements[id].Type == announcementType.schellingCheckAboves || + announcements[id].Type == announcementType.schellingRate ) { + require( moduleHandler(moduleHandlerAddress).configureModule("schelling", announcements[id].Type, announcements[id]._uint) ); + } else if ( announcements[id].Type == announcementType.publisherMinAnnouncementDelay) { + minAnnouncementDelay = announcements[id]._uint; + } else if ( announcements[id].Type == announcementType.publisherOppositeRate) { + oppositeRate = uint8(announcements[id]._uint); + } + } + announcements[id].end = block.number; + announcements[id].open = false; + } + + function oppositeAnnouncement(uint256 id) external { + /* + Opposition of announcement + If announcement is opposable, anyone owning a token can oppose it + Opposition is automatically with the total amount of tokens + If the quantity of his tokens changes, the purport of his opposition changes automatically + The prime time is the windup of the announcement, because this is the moment when the number of tokens in opposition are counted. + One address is entitled to be in oppositon only once. An opposition cannot be withdrawn. + Running announcements can be opposed only. + + @id Announcement identification + */ + uint256 newArrayID = 0; + bool foundEmptyArrayID = false; + require( announcements[id].open ); + require( announcements[id].oppositable ); + for ( uint256 a=0 ; a<opponents[msg.sender].length ; a++ ) { + require( opponents[msg.sender][a] != id ); + if ( ! announcements[opponents[msg.sender][a]].open) { + delete opponents[msg.sender][a]; + if ( ! foundEmptyArrayID ) { + foundEmptyArrayID = true; + newArrayID = a; + } + } + if ( ! foundEmptyArrayID ) { + foundEmptyArrayID = true; + newArrayID = a; + } + } + (bool _success, uint256 _balance) = moduleHandler(moduleHandlerAddress).balanceOf(msg.sender); + require( _success ); + require( _balance > 0); + if ( foundEmptyArrayID ) { + opponents[msg.sender][newArrayID] = id; + } else { + opponents[msg.sender].push(id); + } + announcements[id].oppositionWeight += _balance; + emit EOppositeAnnouncement(id, msg.sender, _balance); + } + + function invalidateAnnouncement(uint256 id) onlyOwner external { + /* + Withdraw announcement. Only those in the admin list can withdraw it. + + @id Announcement identification + */ + require( announcements[id].open ); + announcements[id].end = block.number; + announcements[id].open = false; + emit EInvalidateAnnouncement(id); + } + + modifier onlyOwner() { + /* + Only the owner is allowed to call it. + */ + require( moduleHandler(moduleHandlerAddress).owners(msg.sender) ); + _; + } + + function checkICO() internal returns (bool isICO) { + /* + Inner function to check the ICO status. + @bool Is the ICO in process or not? + */ + (bool _success, bool _isICO) = moduleHandler(moduleHandlerAddress).isICO(); + require( _success ); + return _isICO; + } + + event ENewAnnouncement(uint256 id, announcementType typ); + event EOppositeAnnouncement(uint256 id, address addr, uint256 value); + event EInvalidateAnnouncement(uint256 id); + event ECloseAnnouncement(uint256 id); +} diff --git a/test/compilationTests/corion/safeMath.sol b/test/compilationTests/corion/safeMath.sol index 9e27d379..3e7e5578 100644 --- a/test/compilationTests/corion/safeMath.sol +++ b/test/compilationTests/corion/safeMath.sol @@ -1,11 +1,11 @@ -pragma solidity ^0.4.11; +pragma solidity >=0.0; contract safeMath { function safeAdd(uint256 a, uint256 b) internal returns(uint256) { /* Biztonsagos hozzadas. Tulcsordulas elleni vedelem. A vegeredmeny nem lehet kevesebb mint az @a, ha igen megall a kod. - + @a Amihez hozzaadni kell @b Amennyit hozzaadni kell. @uint256 Vegeredmeny. @@ -15,12 +15,12 @@ contract safeMath { } return a + b; } - + function safeSub(uint256 a, uint256 b) internal returns(uint256) { /* Biztonsagos kivonas. Tulcsordulas elleni vedelem. A vegeredmeny nem lehet tobb mint az @a, ha igen megall a kod. - + @a Amibol kivonni kell. @b Amennyit kivonni kell. @uint256 Vegeredmeny. diff --git a/test/compilationTests/corion/schelling.sol b/test/compilationTests/corion/schelling.sol index 8c8e093c..e70716bd 100644 --- a/test/compilationTests/corion/schelling.sol +++ b/test/compilationTests/corion/schelling.sol @@ -1,4 +1,4 @@ -pragma solidity ^0.4.11; +pragma solidity >=0.0; import "./announcementTypes.sol"; import "./module.sol"; @@ -37,7 +37,7 @@ contract schellingDB is safeMath, schellingVars { */ address private owner; function replaceOwner(address newOwner) external returns(bool) { - require( owner == 0x00 || msg.sender == owner ); + require( owner == address(0x00) || msg.sender == owner ); owner = newOwner; return true; } @@ -45,7 +45,7 @@ contract schellingDB is safeMath, schellingVars { /* Constructor */ - function schellingDB() { + constructor() public { rounds.length = 2; rounds[0].blockHeight = block.number; currentSchellingRound = 1; @@ -54,7 +54,7 @@ contract schellingDB is safeMath, schellingVars { Funds */ mapping(address => uint256) private funds; - function getFunds(address _owner) constant returns(bool, uint256) { + function getFunds(address _owner) public view returns(bool, uint256) { return (true, funds[_owner]); } function setFunds(address _owner, uint256 _amount) isOwner external returns(bool) { @@ -65,7 +65,7 @@ contract schellingDB is safeMath, schellingVars { Rounds */ _rounds[] private rounds; - function getRound(uint256 _id) constant returns(bool, uint256, uint256, uint256, uint256, bool) { + function getRound(uint256 _id) public view returns(bool, uint256, uint256, uint256, uint256, bool) { if ( rounds.length <= _id ) { return (false, 0, 0, 0, 0, false); } else { return (true, rounds[_id].totalAboveWeight, rounds[_id].totalBelowWeight, rounds[_id].reward, rounds[_id].blockHeight, rounds[_id].voted); } } @@ -76,14 +76,14 @@ contract schellingDB is safeMath, schellingVars { rounds[_id] = _rounds(_totalAboveWeight, _totalBelowWeight, _reward, _blockHeight, _voted); return true; } - function getCurrentRound() constant returns(bool, uint256) { + function getCurrentRound() public view returns(bool, uint256) { return (true, rounds.length-1); } /* Voter */ mapping(address => _voter) private voter; - function getVoter(address _owner) constant returns(bool success, uint256 roundID, + function getVoter(address _owner) public view returns(bool success, uint256 roundID, bytes32 hash, voterStatus status, bool voteResult, uint256 rewards) { roundID = voter[_owner].roundID; hash = voter[_owner].hash; @@ -106,7 +106,7 @@ contract schellingDB is safeMath, schellingVars { Schelling Token emission */ mapping(uint256 => uint256) private schellingExpansion; - function getSchellingExpansion(uint256 _id) constant returns(bool, uint256) { + function getSchellingExpansion(uint256 _id) public view returns(bool, uint256) { return (true, schellingExpansion[_id]); } function setSchellingExpansion(uint256 _id, uint256 _expansion) isOwner external returns(bool) { @@ -121,7 +121,7 @@ contract schellingDB is safeMath, schellingVars { currentSchellingRound = _id; return true; } - function getCurrentSchellingRound() constant returns(bool, uint256) { + function getCurrentSchellingRound() public view returns(bool, uint256) { return (true, currentSchellingRound); } } @@ -133,63 +133,63 @@ contract schelling is module, announcementTypes, schellingVars { /* module callbacks */ - function replaceModule(address addr) external returns (bool) { + function replaceModule(address payable addr) external returns (bool) { require( super.isModuleHandler(msg.sender) ); require( db.replaceOwner(addr) ); super._replaceModule(addr); return true; } - function transferEvent(address from, address to, uint256 value) external returns (bool) { + function transferEvent(address payable from, address payable to, uint256 value) external returns (bool) { /* - Transaction completed. This function can be called only by the ModuleHandler. + Transaction completed. This function can be called only by the ModuleHandler. If this contract is the receiver, the amount will be added to the prize pool of the current round. - + @from From who @to To who @value Amount - @bool Was the transaction succesfull? + @bool Was the transaction successful? */ require( super.isModuleHandler(msg.sender) ); if ( to == address(this) ) { - var currentRound = getCurrentRound(); - var round = getRound(currentRound); + uint256 currentRound = getCurrentRound(); + schellingVars._rounds memory round = getRound(currentRound); round.reward += value; setRound(currentRound, round); } return true; } modifier isReady { - var (_success, _active) = super.isActive(); - require( _success && _active ); + (bool _success, bool _active) = super.isActive(); + require( _success && _active ); _; } /* Schelling database functions. */ function getFunds(address addr) internal returns (uint256) { - var (a, b) = db.getFunds(addr); + (bool a, uint256 b) = db.getFunds(addr); require( a ); return b; } function setFunds(address addr, uint256 amount) internal { require( db.setFunds(addr, amount) ); } - function setVoter(address owner, _voter voter) internal { - require( db.setVoter(owner, + function setVoter(address owner, _voter memory voter) internal { + require( db.setVoter(owner, voter.roundID, voter.hash, voter.status, voter.voteResult, voter.rewards ) ); - } - function getVoter(address addr) internal returns (_voter) { - var (a, b, c, d, e, f) = db.getVoter(addr); + } + function getVoter(address addr) internal view returns (_voter memory) { + (bool a, uint256 b, bytes32 c, schellingVars.voterStatus d, bool e, uint256 f) = db.getVoter(addr); require( a ); return _voter(b, c, d, e, f); } - function setRound(uint256 id, _rounds round) internal { - require( db.setRound(id, + function setRound(uint256 id, _rounds memory round) internal { + require( db.setRound(id, round.totalAboveWeight, round.totalBelowWeight, round.reward, @@ -197,8 +197,8 @@ contract schelling is module, announcementTypes, schellingVars { round.voted ) ); } - function pushRound(_rounds round) internal returns (uint256) { - var (a, b) = db.pushRound( + function pushRound(_rounds memory round) internal returns (uint256) { + (bool a, uint256 b) = db.pushRound( round.totalAboveWeight, round.totalBelowWeight, round.reward, @@ -208,29 +208,29 @@ contract schelling is module, announcementTypes, schellingVars { require( a ); return b; } - function getRound(uint256 id) internal returns (_rounds) { - var (a, b, c, d, e, f) = db.getRound(id); + function getRound(uint256 id) internal returns (_rounds memory) { + (bool a, uint256 b, uint256 c, uint256 d, uint256 e, bool f) = db.getRound(id); require( a ); return _rounds(b, c, d, e, f); } function getCurrentRound() internal returns (uint256) { - var (a, b) = db.getCurrentRound(); + (bool a, uint256 b) = db.getCurrentRound(); require( a ); return b; } function setCurrentSchellingRound(uint256 id) internal { require( db.setCurrentSchellingRound(id) ); } - function getCurrentSchellingRound() internal returns(uint256) { - var (a, b) = db.getCurrentSchellingRound(); + function getCurrentSchellingRound() internal view returns(uint256) { + (bool a, uint256 b) = db.getCurrentSchellingRound(); require( a ); return b; } function setSchellingExpansion(uint256 id, uint256 amount) internal { require( db.setSchellingExpansion(id, amount) ); } - function getSchellingExpansion(uint256 id) internal returns(uint256) { - var (a, b) = db.getSchellingExpansion(id); + function getSchellingExpansion(uint256 id) internal view returns(uint256) { + (bool a, uint256 b) = db.getSchellingExpansion(id); require( a ); return b; } @@ -246,11 +246,11 @@ contract schelling is module, announcementTypes, schellingVars { bytes1 public aboveChar = 0x31; bytes1 public belowChar = 0x30; schellingDB private db; - - function schelling(address _moduleHandler, address _db, bool _forReplace) { + + constructor(address payable _moduleHandler, address _db, bool _forReplace) public { /* Installation function. - + @_moduleHandler Address of ModuleHandler. @_db Address of the database. @_forReplace This address will be replaced with the old one or not. @@ -259,13 +259,13 @@ contract schelling is module, announcementTypes, schellingVars { db = schellingDB(_db); super.registerModuleHandler(_moduleHandler); if ( ! _forReplace ) { - require( db.replaceOwner(this) ); + require( db.replaceOwner(address(this)) ); } } function configure(announcementType a, uint256 b) external returns(bool) { /* Can be called only by the ModuleHandler. - + @a Sort of configuration @b Value */ @@ -280,22 +280,22 @@ contract schelling is module, announcementTypes, schellingVars { function prepareVote(bytes32 votehash, uint256 roundID) isReady noContract external { /* Initializing manual vote. - Only the hash of vote will be sent. (Envelope sending). - The address must be in default state, that is there are no vote in progress. + Only the hash of vote will be sent. (Envelope sending). + The address must be in default state, that is there are no vote in progress. Votes can be sent only on the actually Schelling round. - + @votehash Hash of the vote @roundID Number of Schelling round */ nextRound(); - - var currentRound = getCurrentRound(); - var round = getRound(currentRound); + + uint256 currentRound = getCurrentRound(); + schellingVars._rounds memory round = getRound(currentRound); _voter memory voter; uint256 funds; - + require( roundID == currentRound ); - + voter = getVoter(msg.sender); funds = getFunds(msg.sender); @@ -304,13 +304,13 @@ contract schelling is module, announcementTypes, schellingVars { voter.roundID = currentRound; voter.hash = votehash; voter.status = voterStatus.afterPrepareVote; - + setVoter(msg.sender, voter); round.voted = true; - + setRound(currentRound, round); } - function sendVote(string vote) isReady noContract external { + function sendVote(string calldata vote) isReady noContract external { /* Check vote (Envelope opening) Only the sent “envelopes” can be opened. @@ -318,24 +318,24 @@ contract schelling is module, announcementTypes, schellingVars { If the vote invalid, the deposit will be lost. If the “envelope” was opened later than 1,5 Schelling round, the vote is automatically invalid, and deposit can be lost. Lost deposits will be 100% burned. - + @vote Hash of the content of the vote. */ nextRound(); - - var currentRound = getCurrentRound(); + + uint256 currentRound = getCurrentRound(); _rounds memory round; _voter memory voter; uint256 funds; - + bool lostEverything; voter = getVoter(msg.sender); round = getRound(voter.roundID); funds = getFunds(msg.sender); - + require( voter.status == voterStatus.afterPrepareVote ); require( voter.roundID < currentRound ); - if ( sha3(vote) == voter.hash ) { + if ( keccak256(bytes(vote)) == voter.hash ) { delete voter.hash; if (round.blockHeight+roundBlockDelay/2 >= block.number) { if ( bytes(vote)[0] == aboveChar ) { @@ -355,7 +355,7 @@ contract schelling is module, announcementTypes, schellingVars { delete funds; delete voter.status; } - + setVoter(msg.sender, voter); setRound(voter.roundID, round); setFunds(msg.sender, funds); @@ -368,17 +368,17 @@ contract schelling is module, announcementTypes, schellingVars { The right votes take share of deposits. */ nextRound(); - - var currentRound = getCurrentRound(); + + uint256 currentRound = getCurrentRound(); _rounds memory round; _voter memory voter; uint256 funds; - + voter = getVoter(msg.sender); round = getRound(voter.roundID); funds = getFunds(msg.sender); - - require( voter.status == voterStatus.afterSendVoteOk || + + require( voter.status == voterStatus.afterSendVoteOk || voter.status == voterStatus.afterSendVoteBad ); if ( round.blockHeight+roundBlockDelay/2 <= block.number ) { if ( isWinner(round, voter.voteResult) && voter.status == voterStatus.afterSendVoteOk ) { @@ -389,8 +389,8 @@ contract schelling is module, announcementTypes, schellingVars { } delete voter.status; delete voter.roundID; - } else { throw; } - + } else { revert(); } + setVoter(msg.sender, voter); setFunds(msg.sender, funds); } @@ -400,61 +400,61 @@ contract schelling is module, announcementTypes, schellingVars { The prizes will be collected here, and with this function can be transferred to the account of the user. Optionally there can be an address of a beneficiary added, which address the prize will be sent to. Without beneficiary, the owner is the default address. Prize will be sent from the Schelling address without any transaction fee. - + @beneficiary Address of the beneficiary */ - var voter = getVoter(msg.sender); - var funds = getFunds(msg.sender); - + schellingVars._voter memory voter = getVoter(msg.sender); + uint256 funds = getFunds(msg.sender); + address _beneficiary = msg.sender; - if (beneficiary != 0x0) { _beneficiary = beneficiary; } + if (beneficiary != address(0x00)) { _beneficiary = beneficiary; } uint256 reward; require( voter.rewards > 0 ); require( voter.status == voterStatus.base ); reward = voter.rewards; delete voter.rewards; require( moduleHandler(moduleHandlerAddress).transfer(address(this), _beneficiary, reward, false) ); - + setVoter(msg.sender, voter); } - function checkReward() public constant returns (uint256 reward) { + function checkReward() public view returns (uint256 reward) { /* Withdraw of the amount of the prize (it’s only information). - + @reward Prize */ - var voter = getVoter(msg.sender); + schellingVars._voter memory voter = getVoter(msg.sender); return voter.rewards; } function nextRound() internal returns (bool) { /* Inside function, checks the time of the Schelling round and if its needed, creates a new Schelling round. */ - var currentRound = getCurrentRound(); - var round = getRound(currentRound); + uint256 currentRound = getCurrentRound(); + _rounds memory round = getRound(currentRound); _rounds memory newRound; _rounds memory prevRound; - var currentSchellingRound = getCurrentSchellingRound(); - + uint256 currentSchellingRound = getCurrentSchellingRound(); + if ( round.blockHeight+roundBlockDelay > block.number) { return false; } - + newRound.blockHeight = block.number; if ( ! round.voted ) { newRound.reward = round.reward; } - uint256 aboves; + uint256 above; for ( uint256 a=currentRound ; a>=currentRound-interestCheckRounds ; a-- ) { if (a == 0) { break; } prevRound = getRound(a); - if ( prevRound.totalAboveWeight > prevRound.totalBelowWeight ) { aboves++; } + if ( prevRound.totalAboveWeight > prevRound.totalBelowWeight ) { above++; } } uint256 expansion; - if ( aboves >= interestCheckAboves ) { + if ( above >= interestCheckAboves ) { expansion = getTotalSupply() * interestRate / interestRateM / 100; } - + currentSchellingRound++; - + pushRound(newRound); setSchellingExpansion(currentSchellingRound, expansion); require( moduleHandler(moduleHandlerAddress).broadcastSchellingRound(currentSchellingRound, expansion) ); @@ -466,19 +466,19 @@ contract schelling is module, announcementTypes, schellingVars { Every participant entry with his own deposit. In case of wrong vote only this amount of deposit will be burn. The deposit will be sent to the address of Schelling, charged with transaction fee. - + @amount Amount of deposit. */ - var voter = getVoter(msg.sender); - var funds = getFunds(msg.sender); - - var (a, b) = moduleHandler(moduleHandlerAddress).isICO(); + _voter memory voter = getVoter(msg.sender); + uint256 funds = getFunds(msg.sender); + + (bool a, bool b) = moduleHandler(moduleHandlerAddress).isICO(); require( b && b ); require( voter.status == voterStatus.base ); require( amount > 0 ); require( moduleHandler(moduleHandlerAddress).transfer(msg.sender, address(this), amount, true) ); funds += amount; - + setFunds(msg.sender, funds); } function getFunds() isReady noContract external { @@ -487,27 +487,27 @@ contract schelling is module, announcementTypes, schellingVars { If the deposit isn’t lost, it can be withdrawn. By withdrawn, the deposit will be sent from Schelling address to the users address, charged with transaction fee.. */ - var voter = getVoter(msg.sender); - var funds = getFunds(msg.sender); - + _voter memory voter = getVoter(msg.sender); + uint256 funds = getFunds(msg.sender); + require( funds > 0 ); require( voter.status == voterStatus.base ); setFunds(msg.sender, 0); - + require( moduleHandler(moduleHandlerAddress).transfer(address(this), msg.sender, funds, true) ); } - function getCurrentSchellingRoundID() public constant returns (uint256) { + function getCurrentSchellingRoundID() public view returns (uint256) { /* Number of actual Schelling round. - + @uint256 Schelling round. */ return getCurrentSchellingRound(); } - function getSchellingRound(uint256 id) public constant returns (uint256 expansion) { + function getSchellingRound(uint256 id) public view returns (uint256 expansion) { /* Amount of token emission of the Schelling round. - + @id Number of Schelling round @expansion Amount of token emission */ @@ -516,7 +516,7 @@ contract schelling is module, announcementTypes, schellingVars { function getRoundWeight(uint256 aboveW, uint256 belowW) internal returns (uint256) { /* Inside function for calculating the weights of the votes. - + @aboveW Weight of votes: ABOVE @belowW Weight of votes: BELOW @uint256 Calculatet weight @@ -529,10 +529,10 @@ contract schelling is module, announcementTypes, schellingVars { return belowW; } } - function isWinner(_rounds round, bool aboveVote) internal returns (bool) { + function isWinner(_rounds memory round, bool aboveVote) internal returns (bool) { /* Inside function for calculating the result of the game. - + @round Structure of Schelling round. @aboveVote Is the vote = ABOVE or not @bool Result @@ -543,31 +543,31 @@ contract schelling is module, announcementTypes, schellingVars { } return false; } - + function getTotalSupply() internal returns (uint256 amount) { /* Inside function for querying the whole amount of the tokens. - + @uint256 Whole token amount */ - var (_success, _amount) = moduleHandler(moduleHandlerAddress).totalSupply(); + (bool _success, uint256 _amount) = moduleHandler(moduleHandlerAddress).totalSupply(); require( _success ); return _amount; } - + function getTokenBalance(address addr) internal returns (uint256 balance) { /* Inner function in order to poll the token balance of the address. - + @addr Address - + @balance Balance of the address. */ - var (_success, _balance) = moduleHandler(moduleHandlerAddress).balanceOf(addr); + (bool _success, uint256 _balance) = moduleHandler(moduleHandlerAddress).balanceOf(addr); require( _success ); return _balance; } - + modifier noContract { /* Contract can’t call this function, only a natural address. diff --git a/test/compilationTests/corion/token.sol b/test/compilationTests/corion/token.sol index 3aa0bf23..6ab22530 100644 --- a/test/compilationTests/corion/token.sol +++ b/test/compilationTests/corion/token.sol @@ -1,4 +1,4 @@ -pragma solidity ^0.4.11; +pragma solidity >=0.0; import "./announcementTypes.sol"; import "./safeMath.sol"; @@ -7,23 +7,23 @@ import "./moduleHandler.sol"; import "./tokenDB.sol"; contract thirdPartyContractAbstract { - function receiveCorionToken(address, uint256, bytes) external returns (bool, uint256) {} - function approvedCorionToken(address, uint256, bytes) external returns (bool) {} + function receiveCorionToken(address, uint256, bytes calldata) external returns (bool, uint256) {} + function approvedCorionToken(address, uint256, bytes calldata) external returns (bool) {} } contract token is safeMath, module, announcementTypes { /* module callbacks */ - function replaceModule(address addr) external returns (bool success) { + function replaceModule(address payable addr) external returns (bool success) { require( super.isModuleHandler(msg.sender) ); require( db.replaceOwner(addr) ); super._replaceModule(addr); return true; } modifier isReady { - var (_success, _active) = super.isActive(); - require( _success && _active ); + (bool _success, bool _active) = super.isActive(); + require( _success && _active ); _; } /** @@ -35,25 +35,25 @@ contract token is safeMath, module, announcementTypes { string public name = "Corion"; string public symbol = "COR"; uint8 public decimals = 6; - + tokenDB public db; - address public icoAddr; + address payable public icoAddr; uint256 public transactionFeeRate = 20; uint256 public transactionFeeRateM = 1e3; uint256 public transactionFeeMin = 20000; uint256 public transactionFeeMax = 5000000; uint256 public transactionFeeBurn = 80; - address public exchangeAddress; + address payable public exchangeAddress; bool public isICO = true; - + mapping(address => bool) public genesis; - - function token(bool forReplace, address moduleHandler, address dbAddr, address icoContractAddr, address exchangeContractAddress, address[] genesisAddr, uint256[] genesisValue) payable { + + constructor(bool forReplace, address payable moduleHandler, address dbAddr, address payable icoContractAddr, address payable exchangeContractAddress, address payable[] memory genesisAddr, uint256[] memory genesisValue) public payable { /* Installation function - + When _icoAddr is defined, 0.2 ether has to be attached as many times as many genesis addresses are given - + @forReplace This address will be replaced with the old one or not. @moduleHandler Modulhandler's address @dbAddr Address of database @@ -63,37 +63,37 @@ contract token is safeMath, module, announcementTypes { @genesisValue Array of balance of genesis addresses */ super.registerModuleHandler(moduleHandler); - require( dbAddr != 0x00 ); - require( icoContractAddr != 0x00 ); - require( exchangeContractAddress != 0x00 ); + require( dbAddr != address(0x00) ); + require( icoContractAddr != address(0x00) ); + require( exchangeContractAddress != address(0x00) ); db = tokenDB(dbAddr); icoAddr = icoContractAddr; exchangeAddress = exchangeContractAddress; isICO = ! forReplace; if ( ! forReplace ) { - require( db.replaceOwner(this) ); + require( db.replaceOwner(address(this)) ); assert( genesisAddr.length == genesisValue.length ); - require( this.balance >= genesisAddr.length * 0.2 ether ); + require( address(this).balance >= genesisAddr.length * 0.2 ether ); for ( uint256 a=0 ; a<genesisAddr.length ; a++ ) { genesis[genesisAddr[a]] = true; require( db.increase(genesisAddr[a], genesisValue[a]) ); if ( ! genesisAddr[a].send(0.2 ether) ) {} - Mint(genesisAddr[a], genesisValue[a]); + emit Mint(genesisAddr[a], genesisValue[a]); } } } - + function closeIco() external returns (bool success) { /* ICO finished. It can be called only by ICO contract - + @success Was the Function successful? */ require( msg.sender == icoAddr ); isICO = false; return true; } - + /** * @notice `msg.sender` approves `spender` to spend `amount` tokens on its behalf. * @param spender The address of the account able to transfer the tokens @@ -104,17 +104,17 @@ contract token is safeMath, module, announcementTypes { function approve(address spender, uint256 amount, uint256 nonce) isReady external returns (bool success) { /* Authorise another address to use a certain quantity of the authorising owner’s balance - + @spender Address of authorised party @amount Token quantity @nonce Transaction count - + @success Was the Function successful? */ _approve(spender, amount, nonce); return true; } - + /** * @notice `msg.sender` approves `spender` to spend `amount` tokens on its behalf and notify the spender from your approve with your `extraData` data. * @param spender The address of the account able to transfer the tokens @@ -123,28 +123,28 @@ contract token is safeMath, module, announcementTypes { * @param extraData Data to give forward to the receiver * @return True if the approval was successful */ - function approveAndCall(address spender, uint256 amount, uint256 nonce, bytes extraData) isReady external returns (bool success) { + function approveAndCall(address spender, uint256 amount, uint256 nonce, bytes calldata extraData) isReady external returns (bool success) { /* Authorise another address to use a certain quantity of the authorising owner’s balance Following the transaction the receiver address `approvedCorionToken` function is called by the given data - + @spender Authorized address @amount Token quantity @extraData Extra data to be received by the receiver @nonce Transaction count - + @success Was the Function successful? */ _approve(spender, amount, nonce); require( thirdPartyContractAbstract(spender).approvedCorionToken(msg.sender, amount, extraData) ); return true; } - + function _approve(address spender, uint256 amount, uint256 nonce) internal { /* Internal Function to authorise another address to use a certain quantity of the authorising owner’s balance. If the transaction count not match the authorise fails. - + @spender Address of authorised party @amount Token quantity @nonce Transaction count @@ -152,24 +152,24 @@ contract token is safeMath, module, announcementTypes { require( msg.sender != spender ); require( db.balanceOf(msg.sender) >= amount ); require( db.setAllowance(msg.sender, spender, amount, nonce) ); - Approval(msg.sender, spender, amount); + emit Approval(msg.sender, spender, amount); } - - function allowance(address owner, address spender) constant returns (uint256 remaining, uint256 nonce) { + + function allowance(address owner, address spender) public view returns (uint256 remaining, uint256 nonce) { /* Get the quantity of tokens given to be used - + @owner Authorising address @spender Authorised address - + @remaining Tokens to be spent @nonce Transaction count */ - var (_success, _remaining, _nonce) = db.getAllowance(owner, spender); + (bool _success, uint256 _remaining, uint256 _nonce) = db.getAllowance(owner, spender); require( _success ); return (_remaining, _nonce); } - + /** * @notice Send `amount` Corion tokens to `to` from `msg.sender` * @param to The address of the recipient @@ -181,10 +181,10 @@ contract token is safeMath, module, announcementTypes { Start transaction, token is sent from caller’s address to receiver’s address Transaction fee is to be deducted. If receiver is not a natural address but a person, he will be called - + @to To who @amount Quantity - + @success Was the Function successful? */ bytes memory _data; @@ -193,10 +193,10 @@ contract token is safeMath, module, announcementTypes { } else { _transfer( msg.sender, to, amount, true); } - Transfer(msg.sender, to, amount, _data); + emit Transfer(msg.sender, to, amount, _data); return true; } - + /** * @notice Send `amount` tokens to `to` from `from` on the condition it is approved by `from` * @param from The address holding the tokens being transferred @@ -209,20 +209,20 @@ contract token is safeMath, module, announcementTypes { Start transaction to send a quantity from a given address to another address. (approve / allowance). This can be called only by the address approved in advance Transaction fee is to be deducted If receiver is not a natural address but a person, he will be called - + @from From who. @to To who @amount Quantity - + @success Was the Function successful? */ if ( from != msg.sender ) { - var (_success, _reamining, _nonce) = db.getAllowance(from, msg.sender); + (bool _success, uint256 _reamining, uint256 _nonce) = db.getAllowance(from, msg.sender); require( _success ); _reamining = safeSub(_reamining, amount); _nonce = safeAdd(_nonce, 1); require( db.setAllowance(from, msg.sender, _reamining, _nonce) ); - AllowanceUsed(msg.sender, from, amount); + emit AllowanceUsed(msg.sender, from, amount); } bytes memory _data; if ( isContract(to) ) { @@ -230,10 +230,10 @@ contract token is safeMath, module, announcementTypes { } else { _transfer( from, to, amount, true); } - Transfer(from, to, amount, _data); + emit Transfer(from, to, amount, _data); return true; } - + /** * @notice Send `amount` tokens to `to` from `from` on the condition it is approved by `from` * @param from The address holding the tokens being transferred @@ -245,21 +245,21 @@ contract token is safeMath, module, announcementTypes { /* Start transaction to send a quantity from a given address to another address Only ModuleHandler can call it - + @from From who @to To who. @amount Quantity @fee Deduct transaction fee - yes or no? - + @success Was the Function successful? */ bytes memory _data; require( super.isModuleHandler(msg.sender) ); _transfer( from, to, amount, fee); - Transfer(from, to, amount, _data); + emit Transfer(from, to, amount, _data); return true; } - + /** * @notice Send `amount` Corion tokens to `to` from `msg.sender` and notify the receiver from your transaction with your `extraData` data * @param to The contract address of the recipient @@ -267,16 +267,16 @@ contract token is safeMath, module, announcementTypes { * @param extraData Data to give forward to the receiver * @return Whether the transfer was successful or not */ - function transfer(address to, uint256 amount, bytes extraData) isReady external returns (bool success) { + function transfer(address to, uint256 amount, bytes calldata extraData) isReady external returns (bool success) { /* Start transaction to send a quantity from a given address to another address After transaction the function `receiveCorionToken`of the receiver is called by the given data When sending an amount, it is possible the total amount cannot be processed, the remaining amount is sent back with no fee charged - + @to To who. @amount Quantity @extraData Extra data the receiver will get - + @success Was the Function successful? */ if ( isContract(to) ) { @@ -284,21 +284,21 @@ contract token is safeMath, module, announcementTypes { } else { _transfer( msg.sender, to, amount, true); } - Transfer(msg.sender, to, amount, extraData); + emit Transfer(msg.sender, to, amount, extraData); return true; } - - function _transferToContract(address from, address to, uint256 amount, bytes extraData) internal { + + function _transferToContract(address from, address to, uint256 amount, bytes memory extraData) internal { /* Internal function to start transactions to a contract - + @from From who @to To who. @amount Quantity @extraData Extra data the receiver will get */ _transfer(from, to, amount, exchangeAddress == to); - var (_success, _back) = thirdPartyContractAbstract(to).receiveCorionToken(from, amount, extraData); + (bool _success, uint256 _back) = thirdPartyContractAbstract(to).receiveCorionToken(from, amount, extraData); require( _success ); require( amount > _back ); if ( _back > 0 ) { @@ -306,26 +306,26 @@ contract token is safeMath, module, announcementTypes { } _processTransactionFee(from, amount - _back); } - + function _transfer(address from, address to, uint256 amount, bool fee) internal { /* Internal function to start transactions. When Tokens are sent, transaction fee is charged During ICO transactions are allowed only from genesis addresses. - After sending the tokens, the ModuleHandler is notified and it will broadcast the fact among members - + After sending the tokens, the ModuleHandler is notified and it will broadcast the fact among members + The 0xa636a97578d26a3b76b060bbc18226d954cf3757 address are blacklisted. - + @from From who @to To who @amount Quantity @fee Deduct transaction fee - yes or no? */ if( fee ) { - var (success, _fee) = getTransactionFee(amount); + (bool success, uint256 _fee) = getTransactionFee(amount); require( success ); require( db.balanceOf(from) >= amount + _fee ); } - require( from != 0x00 && to != 0x00 && to != 0xa636a97578d26a3b76b060bbc18226d954cf3757 ); + require( from != address(0x00) && to != address(0x00) && to != 0xa636A97578d26A3b76B060Bbc18226d954cf3757 ); require( ( ! isICO) || genesis[from] ); require( db.decrease(from, amount) ); require( db.increase(to, amount) ); @@ -336,7 +336,7 @@ contract token is safeMath, module, announcementTypes { } require( moduleHandler(moduleHandlerAddress).broadcastTransfer(from, to, amount) ); } - + /** * @notice Transaction fee will be deduced from `owner` for transacting `value` * @param owner The address where will the transaction fee deduced @@ -345,28 +345,28 @@ contract token is safeMath, module, announcementTypes { */ function processTransactionFee(address owner, uint256 value) isReady external returns (bool success) { /* - Charge transaction fee. It can be called only by moduleHandler - + Charge transaction fee. It can be called only by moduleHandler + @owner From who. @value Quantity to calculate the fee - + @success Was the Function successful? */ require( super.isModuleHandler(msg.sender) ); _processTransactionFee(owner, value); return true; } - + function _processTransactionFee(address owner, uint256 value) internal { /* Internal function to charge the transaction fee. A certain quantity is burnt, the rest is sent to the Schelling game prize pool. No transaction fee during ICO. - + @owner From who @value Quantity to calculate the fee */ if ( isICO ) { return; } - var (_success, _fee) = getTransactionFee(value); + (bool _success, uint256 _fee) = getTransactionFee(value); require( _success ); uint256 _forBurn = _fee * transactionFeeBurn / 100; uint256 _forSchelling = _fee - _forBurn; @@ -374,24 +374,24 @@ contract token is safeMath, module, announcementTypes { address _schellingAddr; (_success, _found, _schellingAddr) = moduleHandler(moduleHandlerAddress).getModuleAddressByName('Schelling'); require( _success ); - if ( _schellingAddr != 0x00 && _found) { + if ( _schellingAddr != address(0x00) && _found) { require( db.decrease(owner, _forSchelling) ); require( db.increase(_schellingAddr, _forSchelling) ); _burn(owner, _forBurn); bytes memory _data; - Transfer(owner, _schellingAddr, _forSchelling, _data); + emit Transfer(owner, _schellingAddr, _forSchelling, _data); require( moduleHandler(moduleHandlerAddress).broadcastTransfer(owner, _schellingAddr, _forSchelling) ); } else { _burn(owner, _fee); } } - - function getTransactionFee(uint256 value) public constant returns (bool success, uint256 fee) { + + function getTransactionFee(uint256 value) public view returns (bool success, uint256 fee) { /* Transaction fee query. - + @value Quantity to calculate the fee - + @success Was the Function successful? @fee Amount of Transaction fee */ @@ -401,68 +401,68 @@ contract token is safeMath, module, announcementTypes { else if ( fee < transactionFeeMin ) { fee = transactionFeeMin; } return (true, fee); } - + function mint(address owner, uint256 value) isReady external returns (bool success) { /* Generating tokens. It can be called only by ICO contract or the moduleHandler. - + @owner Address @value Amount. - + @success Was the Function successful? */ require( super.isModuleHandler(msg.sender) || msg.sender == icoAddr ); _mint(owner, value); return true; } - + function _mint(address owner, uint256 value) internal { /* Internal function to generate tokens - + @owner Token is credited to this address @value Quantity */ require( db.increase(owner, value) ); - require( moduleHandler(moduleHandlerAddress).broadcastTransfer(0x00, owner, value) ); + require( moduleHandler(moduleHandlerAddress).broadcastTransfer(address(0x00), owner, value) ); if ( isICO ) { require( ico(icoAddr).setInterestDB(owner, db.balanceOf(owner)) ); } - Mint(owner, value); + emit Mint(owner, value); } - + function burn(address owner, uint256 value) isReady external returns (bool success) { /* Burning the token. Can call only modulehandler - + @owner Burn the token from this address @value Quantity - + @success Was the Function successful? */ require( super.isModuleHandler(msg.sender) ); _burn(owner, value); return true; } - + function _burn(address owner, uint256 value) internal { /* Internal function to burn the token - + @owner Burn the token from this address @value Quantity */ require( db.decrease(owner, value) ); - require( moduleHandler(moduleHandlerAddress).broadcastTransfer(owner, 0x00, value) ); - Burn(owner, value); + require( moduleHandler(moduleHandlerAddress).broadcastTransfer(owner, address(0x00), value) ); + emit Burn(owner, value); } - + function isContract(address addr) internal returns (bool success) { /* Internal function to check if the given address is natural, or a contract - + @addr Address to be checked - + @success Is the address crontact or not */ uint256 _codeLength; @@ -471,34 +471,34 @@ contract token is safeMath, module, announcementTypes { } return _codeLength > 0; } - - function balanceOf(address owner) constant returns (uint256 value) { + + function balanceOf(address owner) public view returns (uint256 value) { /* Token balance query - + @owner Address - + @value Balance of address */ return db.balanceOf(owner); } - - function totalSupply() constant returns (uint256 value) { + + function totalSupply() public view returns (uint256 value) { /* Total token quantity query - + @value Total token quantity */ return db.totalSupply(); } - + function configure(announcementType aType, uint256 value) isReady external returns(bool success) { /* Token settings configuration.It can be call only by moduleHandler - + @aType Type of setting @value Value - + @success Was the Function successful? */ require( super.isModuleHandler(msg.sender) ); @@ -509,7 +509,7 @@ contract token is safeMath, module, announcementTypes { else { return false; } return true; } - + event AllowanceUsed(address indexed spender, address indexed owner, uint256 indexed value); event Mint(address indexed addr, uint256 indexed value); event Burn(address indexed addr, uint256 indexed value); diff --git a/test/compilationTests/corion/tokenDB.sol b/test/compilationTests/corion/tokenDB.sol index 6de1b6c3..a77a1efa 100644 --- a/test/compilationTests/corion/tokenDB.sol +++ b/test/compilationTests/corion/tokenDB.sol @@ -1,77 +1,77 @@ -pragma solidity ^0.4.11;
-
-import "./safeMath.sol";
-import "./owned.sol";
-
-contract tokenDB is safeMath, ownedDB {
-
- struct allowance_s {
- uint256 amount;
- uint256 nonce;
- }
-
- mapping(address => mapping(address => allowance_s)) public allowance;
- mapping (address => uint256) public balanceOf;
- uint256 public totalSupply;
-
- function increase(address owner, uint256 value) external returns(bool success) {
- /*
- Increase of balance of the address in database. Only owner can call it.
-
- @owner Address
- @value Quantity
-
- @success Was the Function successful?
- */
- require( isOwner() );
- balanceOf[owner] = safeAdd(balanceOf[owner], value);
- totalSupply = safeAdd(totalSupply, value);
- return true;
- }
-
- function decrease(address owner, uint256 value) external returns(bool success) {
- /*
- Decrease of balance of the address in database. Only owner can call it.
-
- @owner Address
- @value Quantity
-
- @success Was the Function successful?
- */
- require( isOwner() );
- balanceOf[owner] = safeSub(balanceOf[owner], value);
- totalSupply = safeSub(totalSupply, value);
- return true;
- }
-
- function setAllowance(address owner, address spender, uint256 amount, uint256 nonce) external returns(bool success) {
- /*
- Set allowance in the database. Only owner can call it.
-
- @owner Owner address
- @spender Spender address
- @amount Amount to set
- @nonce Transaction count
-
- @success Was the Function successful?
- */
- require( isOwner() );
- allowance[owner][spender].amount = amount;
- allowance[owner][spender].nonce = nonce;
- return true;
- }
-
- function getAllowance(address owner, address spender) constant returns(bool success, uint256 remaining, uint256 nonce) {
- /*
- Get allowance from the database.
-
- @owner Owner address
- @spender Spender address
-
- @success Was the Function successful?
- @remaining Remaining amount of the allowance
- @nonce Transaction count
- */
- return ( true, allowance[owner][spender].amount, allowance[owner][spender].nonce );
- }
-}
+pragma solidity >=0.0; + +import "./safeMath.sol"; +import "./owned.sol"; + +contract tokenDB is safeMath, ownedDB { + + struct allowance_s { + uint256 amount; + uint256 nonce; + } + + mapping(address => mapping(address => allowance_s)) public allowance; + mapping (address => uint256) public balanceOf; + uint256 public totalSupply; + + function increase(address owner, uint256 value) external returns(bool success) { + /* + Increase of balance of the address in database. Only owner can call it. + + @owner Address + @value Quantity + + @success Was the Function successful? + */ + require( isOwner() ); + balanceOf[owner] = safeAdd(balanceOf[owner], value); + totalSupply = safeAdd(totalSupply, value); + return true; + } + + function decrease(address owner, uint256 value) external returns(bool success) { + /* + Decrease of balance of the address in database. Only owner can call it. + + @owner Address + @value Quantity + + @success Was the Function successful? + */ + require( isOwner() ); + balanceOf[owner] = safeSub(balanceOf[owner], value); + totalSupply = safeSub(totalSupply, value); + return true; + } + + function setAllowance(address owner, address spender, uint256 amount, uint256 nonce) external returns(bool success) { + /* + Set allowance in the database. Only owner can call it. + + @owner Owner address + @spender Spender address + @amount Amount to set + @nonce Transaction count + + @success Was the Function successful? + */ + require( isOwner() ); + allowance[owner][spender].amount = amount; + allowance[owner][spender].nonce = nonce; + return true; + } + + function getAllowance(address owner, address spender) public view returns(bool success, uint256 remaining, uint256 nonce) { + /* + Get allowance from the database. + + @owner Owner address + @spender Spender address + + @success Was the Function successful? + @remaining Remaining amount of the allowance + @nonce Transaction count + */ + return ( true, allowance[owner][spender].amount, allowance[owner][spender].nonce ); + } +} diff --git a/test/compilationTests/gnosis/Events/CategoricalEvent.sol b/test/compilationTests/gnosis/Events/CategoricalEvent.sol index fbd1d744..0d6d1b45 100644 --- a/test/compilationTests/gnosis/Events/CategoricalEvent.sol +++ b/test/compilationTests/gnosis/Events/CategoricalEvent.sol @@ -1,4 +1,4 @@ -pragma solidity ^0.4.11; +pragma solidity >=0.0; import "../Events/Event.sol"; @@ -13,7 +13,7 @@ contract CategoricalEvent is Event { /// @param _collateralToken Tokens used as collateral in exchange for outcome tokens /// @param _oracle Oracle contract used to resolve the event /// @param outcomeCount Number of event outcomes - function CategoricalEvent( + constructor( Token _collateralToken, Oracle _oracle, uint8 outcomeCount @@ -38,16 +38,16 @@ contract CategoricalEvent is Event { outcomeTokens[uint(outcome)].revoke(msg.sender, winnings); // Payout winnings require(collateralToken.transfer(msg.sender, winnings)); - WinningsRedemption(msg.sender, winnings); + emit WinningsRedemption(msg.sender, winnings); } /// @dev Calculates and returns event hash /// @return Event hash function getEventHash() public - constant + view returns (bytes32) { - return keccak256(collateralToken, oracle, outcomeTokens.length); + return keccak256(abi.encodePacked(collateralToken, oracle, outcomeTokens.length)); } } diff --git a/test/compilationTests/gnosis/Events/Event.sol b/test/compilationTests/gnosis/Events/Event.sol index 9aa257c4..b11dd15d 100644 --- a/test/compilationTests/gnosis/Events/Event.sol +++ b/test/compilationTests/gnosis/Events/Event.sol @@ -1,4 +1,4 @@ -pragma solidity ^0.4.11; +pragma solidity >=0.0; import "../Tokens/Token.sol"; import "../Tokens/OutcomeToken.sol"; import "../Oracles/Oracle.sol"; @@ -33,18 +33,18 @@ contract Event { /// @param _collateralToken Tokens used as collateral in exchange for outcome tokens /// @param _oracle Oracle contract used to resolve the event /// @param outcomeCount Number of event outcomes - function Event(Token _collateralToken, Oracle _oracle, uint8 outcomeCount) + constructor(Token _collateralToken, Oracle _oracle, uint8 outcomeCount) public { // Validate input - require(address(_collateralToken) != 0 && address(_oracle) != 0 && outcomeCount >= 2); + require(address(_collateralToken) != address(0) && address(_oracle) != address(0) && outcomeCount >= 2); collateralToken = _collateralToken; oracle = _oracle; // Create an outcome token for each outcome for (uint8 i = 0; i < outcomeCount; i++) { OutcomeToken outcomeToken = new OutcomeToken(); outcomeTokens.push(outcomeToken); - OutcomeTokenCreation(outcomeToken, i); + emit OutcomeTokenCreation(outcomeToken, i); } } @@ -54,11 +54,11 @@ contract Event { public { // Transfer collateral tokens to events contract - require(collateralToken.transferFrom(msg.sender, this, collateralTokenCount)); + require(collateralToken.transferFrom(msg.sender, address(this), collateralTokenCount)); // Issue new outcome tokens to sender for (uint8 i = 0; i < outcomeTokens.length; i++) outcomeTokens[i].issue(msg.sender, collateralTokenCount); - OutcomeTokenSetIssuance(msg.sender, collateralTokenCount); + emit OutcomeTokenSetIssuance(msg.sender, collateralTokenCount); } /// @dev Sells equal number of tokens of all outcomes, exchanging collateral tokens and sets of outcome tokens 1:1 @@ -71,7 +71,7 @@ contract Event { outcomeTokens[i].revoke(msg.sender, outcomeTokenCount); // Transfer collateral tokens to sender require(collateralToken.transfer(msg.sender, outcomeTokenCount)); - OutcomeTokenSetRevocation(msg.sender, outcomeTokenCount); + emit OutcomeTokenSetRevocation(msg.sender, outcomeTokenCount); } /// @dev Sets winning event outcome @@ -83,14 +83,14 @@ contract Event { // Set winning outcome outcome = oracle.getOutcome(); isOutcomeSet = true; - OutcomeAssignment(outcome); + emit OutcomeAssignment(outcome); } /// @dev Returns outcome count /// @return Outcome count function getOutcomeCount() public - constant + view returns (uint8) { return uint8(outcomeTokens.length); @@ -100,8 +100,8 @@ contract Event { /// @return Outcome tokens function getOutcomeTokens() public - constant - returns (OutcomeToken[]) + view + returns (OutcomeToken[] memory) { return outcomeTokens; } @@ -110,8 +110,8 @@ contract Event { /// @return Outcome token distribution function getOutcomeTokenDistribution(address owner) public - constant - returns (uint[] outcomeTokenDistribution) + view + returns (uint[] memory outcomeTokenDistribution) { outcomeTokenDistribution = new uint[](outcomeTokens.length); for (uint8 i = 0; i < outcomeTokenDistribution.length; i++) @@ -120,7 +120,7 @@ contract Event { /// @dev Calculates and returns event hash /// @return Event hash - function getEventHash() public constant returns (bytes32); + function getEventHash() public view returns (bytes32); /// @dev Exchanges sender's winning outcome tokens for collateral tokens /// @return Sender's winnings diff --git a/test/compilationTests/gnosis/Events/EventFactory.sol b/test/compilationTests/gnosis/Events/EventFactory.sol index dfb1a579..4f4efe35 100644 --- a/test/compilationTests/gnosis/Events/EventFactory.sol +++ b/test/compilationTests/gnosis/Events/EventFactory.sol @@ -1,4 +1,4 @@ -pragma solidity ^0.4.11; +pragma solidity >=0.0; import "../Events/CategoricalEvent.sol"; import "../Events/ScalarEvent.sol"; @@ -35,9 +35,9 @@ contract EventFactory { public returns (CategoricalEvent eventContract) { - bytes32 eventHash = keccak256(collateralToken, oracle, outcomeCount); + bytes32 eventHash = keccak256(abi.encodePacked(collateralToken, oracle, outcomeCount)); // Event should not exist yet - require(address(categoricalEvents[eventHash]) == 0); + require(address(categoricalEvents[eventHash]) == address(0)); // Create event eventContract = new CategoricalEvent( collateralToken, @@ -45,7 +45,7 @@ contract EventFactory { outcomeCount ); categoricalEvents[eventHash] = eventContract; - CategoricalEventCreation(msg.sender, eventContract, collateralToken, oracle, outcomeCount); + emit CategoricalEventCreation(msg.sender, eventContract, collateralToken, oracle, outcomeCount); } /// @dev Creates a new scalar event and adds it to the event mapping @@ -63,9 +63,9 @@ contract EventFactory { public returns (ScalarEvent eventContract) { - bytes32 eventHash = keccak256(collateralToken, oracle, lowerBound, upperBound); + bytes32 eventHash = keccak256(abi.encodePacked(collateralToken, oracle, lowerBound, upperBound)); // Event should not exist yet - require(address(scalarEvents[eventHash]) == 0); + require(address(scalarEvents[eventHash]) == address(0)); // Create event eventContract = new ScalarEvent( collateralToken, @@ -74,6 +74,6 @@ contract EventFactory { upperBound ); scalarEvents[eventHash] = eventContract; - ScalarEventCreation(msg.sender, eventContract, collateralToken, oracle, lowerBound, upperBound); + emit ScalarEventCreation(msg.sender, eventContract, collateralToken, oracle, lowerBound, upperBound); } } diff --git a/test/compilationTests/gnosis/Events/ScalarEvent.sol b/test/compilationTests/gnosis/Events/ScalarEvent.sol index 2e5718ef..325f4302 100644 --- a/test/compilationTests/gnosis/Events/ScalarEvent.sol +++ b/test/compilationTests/gnosis/Events/ScalarEvent.sol @@ -1,4 +1,4 @@ -pragma solidity ^0.4.11; +pragma solidity >=0.0; import "../Events/Event.sol"; @@ -28,7 +28,7 @@ contract ScalarEvent is Event { /// @param _oracle Oracle contract used to resolve the event /// @param _lowerBound Lower bound for event outcome /// @param _upperBound Lower bound for event outcome - function ScalarEvent( + constructor( Token _collateralToken, Oracle _oracle, int _lowerBound, @@ -72,16 +72,16 @@ contract ScalarEvent is Event { outcomeTokens[LONG].revoke(msg.sender, longOutcomeTokenCount); // Payout winnings to sender require(collateralToken.transfer(msg.sender, winnings)); - WinningsRedemption(msg.sender, winnings); + emit WinningsRedemption(msg.sender, winnings); } /// @dev Calculates and returns event hash /// @return Event hash function getEventHash() public - constant + view returns (bytes32) { - return keccak256(collateralToken, oracle, lowerBound, upperBound); + return keccak256(abi.encodePacked(collateralToken, oracle, lowerBound, upperBound)); } } diff --git a/test/compilationTests/gnosis/MarketMakers/LMSRMarketMaker.sol b/test/compilationTests/gnosis/MarketMakers/LMSRMarketMaker.sol index 1529129d..c305d168 100644 --- a/test/compilationTests/gnosis/MarketMakers/LMSRMarketMaker.sol +++ b/test/compilationTests/gnosis/MarketMakers/LMSRMarketMaker.sol @@ -1,4 +1,4 @@ -pragma solidity ^0.4.11; +pragma solidity >=0.0; import "../Utils/Math.sol"; import "../MarketMakers/MarketMaker.sol"; @@ -24,7 +24,7 @@ contract LMSRMarketMaker is MarketMaker { /// @return Cost function calcCost(Market market, uint8 outcomeTokenIndex, uint outcomeTokenCount) public - constant + view returns (uint cost) { require(market.eventContract().getOutcomeCount() > 1); @@ -59,7 +59,7 @@ contract LMSRMarketMaker is MarketMaker { /// @return Profit function calcProfit(Market market, uint8 outcomeTokenIndex, uint outcomeTokenCount) public - constant + view returns (uint profit) { require(market.eventContract().getOutcomeCount() > 1); @@ -85,7 +85,7 @@ contract LMSRMarketMaker is MarketMaker { /// @return Marginal price of an outcome as a fixed point number function calcMarginalPrice(Market market, uint8 outcomeTokenIndex) public - constant + view returns (uint price) { require(market.eventContract().getOutcomeCount() > 1); @@ -95,7 +95,7 @@ contract LMSRMarketMaker is MarketMaker { // The price function is exp(quantities[i]/b) / sum(exp(q/b) for q in quantities) // To avoid overflow, calculate with // exp(quantities[i]/b - offset) / sum(exp(q/b - offset) for q in quantities) - var (sum, , outcomeExpTerm) = sumExpOffset(logN, netOutcomeTokensSold, funding, outcomeTokenIndex); + (uint256 sum, , uint256 outcomeExpTerm) = sumExpOffset(logN, netOutcomeTokensSold, funding, outcomeTokenIndex); return outcomeExpTerm / (sum / ONE); } @@ -108,15 +108,15 @@ contract LMSRMarketMaker is MarketMaker { /// @param netOutcomeTokensSold Net outcome tokens sold by market /// @param funding Initial funding for market /// @return Cost level - function calcCostLevel(int logN, int[] netOutcomeTokensSold, uint funding) + function calcCostLevel(int logN, int[] memory netOutcomeTokensSold, uint funding) private - constant + view returns(int costLevel) { // The cost function is C = b * log(sum(exp(q/b) for q in quantities)). // To avoid overflow, we need to calc with an exponent offset: // C = b * (offset + log(sum(exp(q/b - offset) for q in quantities))) - var (sum, offset, ) = sumExpOffset(logN, netOutcomeTokensSold, funding, 0); + (uint256 sum, int256 offset, ) = sumExpOffset(logN, netOutcomeTokensSold, funding, 0); costLevel = Math.ln(sum); costLevel = costLevel.add(offset); costLevel = (costLevel.mul(int(ONE)) / logN).mul(int(funding)); @@ -129,9 +129,9 @@ contract LMSRMarketMaker is MarketMaker { /// @param funding Initial funding for market /// @param outcomeIndex Index of exponential term to extract (for use by marginal price function) /// @return A result structure composed of the sum, the offset used, and the summand associated with the supplied index - function sumExpOffset(int logN, int[] netOutcomeTokensSold, uint funding, uint8 outcomeIndex) + function sumExpOffset(int logN, int[] memory netOutcomeTokensSold, uint funding, uint8 outcomeIndex) private - constant + view returns (uint sum, int offset, uint outcomeExpTerm) { // Naive calculation of this causes an overflow @@ -170,8 +170,8 @@ contract LMSRMarketMaker is MarketMaker { /// @return Net outcome tokens sold by market function getNetOutcomeTokensSold(Market market) private - constant - returns (int[] quantities) + view + returns (int[] memory quantities) { quantities = new int[](market.eventContract().getOutcomeCount()); for (uint8 i = 0; i < quantities.length; i++) diff --git a/test/compilationTests/gnosis/MarketMakers/MarketMaker.sol b/test/compilationTests/gnosis/MarketMakers/MarketMaker.sol index 3162ce64..f61cdba8 100644 --- a/test/compilationTests/gnosis/MarketMakers/MarketMaker.sol +++ b/test/compilationTests/gnosis/MarketMakers/MarketMaker.sol @@ -1,4 +1,4 @@ -pragma solidity ^0.4.11; +pragma solidity >=0.0; import "../Markets/Market.sol"; @@ -8,7 +8,7 @@ contract MarketMaker { /* * Public functions */ - function calcCost(Market market, uint8 outcomeTokenIndex, uint outcomeTokenCount) public constant returns (uint); - function calcProfit(Market market, uint8 outcomeTokenIndex, uint outcomeTokenCount) public constant returns (uint); - function calcMarginalPrice(Market market, uint8 outcomeTokenIndex) public constant returns (uint); + function calcCost(Market market, uint8 outcomeTokenIndex, uint outcomeTokenCount) public view returns (uint); + function calcProfit(Market market, uint8 outcomeTokenIndex, uint outcomeTokenCount) public view returns (uint); + function calcMarginalPrice(Market market, uint8 outcomeTokenIndex) public view returns (uint); } diff --git a/test/compilationTests/gnosis/Markets/Campaign.sol b/test/compilationTests/gnosis/Markets/Campaign.sol index 9aee1033..b16d102c 100644 --- a/test/compilationTests/gnosis/Markets/Campaign.sol +++ b/test/compilationTests/gnosis/Markets/Campaign.sol @@ -1,4 +1,4 @@ -pragma solidity ^0.4.11; +pragma solidity >=0.0; import "../Events/Event.sol"; import "../Markets/StandardMarketFactory.sol"; import "../Utils/Math.sol"; @@ -70,7 +70,7 @@ contract Campaign { /// @param _fee Market fee /// @param _funding Initial funding for market /// @param _deadline Campaign deadline - function Campaign( + constructor( Event _eventContract, MarketFactory _marketFactory, MarketMaker _marketMaker, @@ -81,9 +81,9 @@ contract Campaign { public { // Validate input - require( address(_eventContract) != 0 - && address(_marketFactory) != 0 - && address(_marketMaker) != 0 + require( address(_eventContract) != address(0) + && address(_marketFactory) != address(0) + && address(_marketMaker) != address(0) && _fee < FEE_RANGE && _funding > 0 && now < _deadline); @@ -102,16 +102,16 @@ contract Campaign { timedTransitions atStage(Stages.AuctionStarted) { - uint raisedAmount = eventContract.collateralToken().balanceOf(this); + uint raisedAmount = eventContract.collateralToken().balanceOf(address(this)); uint maxAmount = funding.sub(raisedAmount); if (maxAmount < amount) amount = maxAmount; // Collect collateral tokens - require(eventContract.collateralToken().transferFrom(msg.sender, this, amount)); + require(eventContract.collateralToken().transferFrom(msg.sender, address(this), amount)); contributions[msg.sender] = contributions[msg.sender].add(amount); if (amount == maxAmount) stage = Stages.AuctionSuccessful; - CampaignFunding(msg.sender, amount); + emit CampaignFunding(msg.sender, amount); } /// @dev Withdraws refund amount @@ -126,7 +126,7 @@ contract Campaign { contributions[msg.sender] = 0; // Refund collateral tokens require(eventContract.collateralToken().transfer(msg.sender, refundAmount)); - CampaignRefund(msg.sender, refundAmount); + emit CampaignRefund(msg.sender, refundAmount); } /// @dev Allows to create market after successful funding @@ -138,10 +138,10 @@ contract Campaign { returns (Market) { market = marketFactory.createMarket(eventContract, marketMaker, fee); - require(eventContract.collateralToken().approve(market, funding)); + require(eventContract.collateralToken().approve(address(market), funding)); market.fund(funding); stage = Stages.MarketCreated; - MarketCreation(market); + emit MarketCreation(market); return market; } @@ -156,9 +156,9 @@ contract Campaign { market.close(); market.withdrawFees(); eventContract.redeemWinnings(); - finalBalance = eventContract.collateralToken().balanceOf(this); + finalBalance = eventContract.collateralToken().balanceOf(address(this)); stage = Stages.MarketClosed; - MarketClosing(); + emit MarketClosing(); } /// @dev Allows to withdraw fees from campaign contract to contributor @@ -172,6 +172,6 @@ contract Campaign { contributions[msg.sender] = 0; // Send fee share to contributor require(eventContract.collateralToken().transfer(msg.sender, fees)); - FeeWithdrawal(msg.sender, fees); + emit FeeWithdrawal(msg.sender, fees); } } diff --git a/test/compilationTests/gnosis/Markets/CampaignFactory.sol b/test/compilationTests/gnosis/Markets/CampaignFactory.sol index 930ec2e2..c7afeded 100644 --- a/test/compilationTests/gnosis/Markets/CampaignFactory.sol +++ b/test/compilationTests/gnosis/Markets/CampaignFactory.sol @@ -1,4 +1,4 @@ -pragma solidity ^0.4.11; +pragma solidity >=0.0; import "../Markets/Campaign.sol"; @@ -34,6 +34,6 @@ contract CampaignFactory { returns (Campaign campaign) { campaign = new Campaign(eventContract, marketFactory, marketMaker, fee, funding, deadline); - CampaignCreation(msg.sender, campaign, eventContract, marketFactory, marketMaker, fee, funding, deadline); + emit CampaignCreation(msg.sender, campaign, eventContract, marketFactory, marketMaker, fee, funding, deadline); } } diff --git a/test/compilationTests/gnosis/Markets/Market.sol b/test/compilationTests/gnosis/Markets/Market.sol index 635b14db..963c2c94 100644 --- a/test/compilationTests/gnosis/Markets/Market.sol +++ b/test/compilationTests/gnosis/Markets/Market.sol @@ -1,4 +1,4 @@ -pragma solidity ^0.4.11; +pragma solidity >=0.0; import "../Events/Event.sol"; import "../MarketMakers/MarketMaker.sol"; @@ -43,5 +43,5 @@ contract Market { function buy(uint8 outcomeTokenIndex, uint outcomeTokenCount, uint maxCost) public returns (uint); function sell(uint8 outcomeTokenIndex, uint outcomeTokenCount, uint minProfit) public returns (uint); function shortSell(uint8 outcomeTokenIndex, uint outcomeTokenCount, uint minProfit) public returns (uint); - function calcMarketFee(uint outcomeTokenCost) public constant returns (uint); + function calcMarketFee(uint outcomeTokenCost) public view returns (uint); } diff --git a/test/compilationTests/gnosis/Markets/MarketFactory.sol b/test/compilationTests/gnosis/Markets/MarketFactory.sol index d368e8db..40d80db6 100644 --- a/test/compilationTests/gnosis/Markets/MarketFactory.sol +++ b/test/compilationTests/gnosis/Markets/MarketFactory.sol @@ -1,4 +1,4 @@ -pragma solidity ^0.4.11; +pragma solidity >=0.0; import "../Events/Event.sol"; import "../MarketMakers/MarketMaker.sol"; import "../Markets/Market.sol"; diff --git a/test/compilationTests/gnosis/Markets/StandardMarket.sol b/test/compilationTests/gnosis/Markets/StandardMarket.sol index b973119a..99648c90 100644 --- a/test/compilationTests/gnosis/Markets/StandardMarket.sol +++ b/test/compilationTests/gnosis/Markets/StandardMarket.sol @@ -1,4 +1,4 @@ -pragma solidity ^0.4.11; +pragma solidity >=0.0; import "../Markets/Market.sol"; import "../Tokens/Token.sol"; import "../Events/Event.sol"; @@ -38,11 +38,11 @@ contract StandardMarket is Market { /// @param _eventContract Event contract /// @param _marketMaker Market maker contract /// @param _fee Market fee - function StandardMarket(address _creator, Event _eventContract, MarketMaker _marketMaker, uint24 _fee) + constructor(address _creator, Event _eventContract, MarketMaker _marketMaker, uint24 _fee) public { // Validate inputs - require(address(_eventContract) != 0 && address(_marketMaker) != 0 && _fee < FEE_RANGE); + require(address(_eventContract) != address(0) && address(_marketMaker) != address(0) && _fee < FEE_RANGE); creator = _creator; createdAtBlock = block.number; eventContract = _eventContract; @@ -60,12 +60,12 @@ contract StandardMarket is Market { atStage(Stages.MarketCreated) { // Request collateral tokens and allow event contract to transfer them to buy all outcomes - require( eventContract.collateralToken().transferFrom(msg.sender, this, _funding) - && eventContract.collateralToken().approve(eventContract, _funding)); + require( eventContract.collateralToken().transferFrom(msg.sender, address(this), _funding) + && eventContract.collateralToken().approve(address(eventContract), _funding)); eventContract.buyAllOutcomes(_funding); funding = _funding; stage = Stages.MarketFunded; - MarketFunding(funding); + emit MarketFunding(funding); } /// @dev Allows market creator to close the markets by transferring all remaining outcome tokens to the creator @@ -76,9 +76,9 @@ contract StandardMarket is Market { { uint8 outcomeCount = eventContract.getOutcomeCount(); for (uint8 i = 0; i < outcomeCount; i++) - require(eventContract.outcomeTokens(i).transfer(creator, eventContract.outcomeTokens(i).balanceOf(this))); + require(eventContract.outcomeTokens(i).transfer(creator, eventContract.outcomeTokens(i).balanceOf(address(this)))); stage = Stages.MarketClosed; - MarketClosing(); + emit MarketClosing(); } /// @dev Allows market creator to withdraw fees generated by trades @@ -88,10 +88,10 @@ contract StandardMarket is Market { isCreator returns (uint fees) { - fees = eventContract.collateralToken().balanceOf(this); + fees = eventContract.collateralToken().balanceOf(address(this)); // Transfer fees require(eventContract.collateralToken().transfer(creator, fees)); - FeeWithdrawal(fees); + emit FeeWithdrawal(fees); } /// @dev Allows to buy outcome tokens from market maker @@ -112,8 +112,8 @@ contract StandardMarket is Market { // Check cost doesn't exceed max cost require(cost > 0 && cost <= maxCost); // Transfer tokens to markets contract and buy all outcomes - require( eventContract.collateralToken().transferFrom(msg.sender, this, cost) - && eventContract.collateralToken().approve(eventContract, outcomeTokenCost)); + require( eventContract.collateralToken().transferFrom(msg.sender, address(this), cost) + && eventContract.collateralToken().approve(address(eventContract), outcomeTokenCost)); // Buy all outcomes eventContract.buyAllOutcomes(outcomeTokenCost); // Transfer outcome tokens to buyer @@ -121,7 +121,7 @@ contract StandardMarket is Market { // Add outcome token count to market maker net balance require(int(outcomeTokenCount) >= 0); netOutcomeTokensSold[outcomeTokenIndex] = netOutcomeTokensSold[outcomeTokenIndex].add(int(outcomeTokenCount)); - OutcomeTokenPurchase(msg.sender, outcomeTokenIndex, outcomeTokenCount, cost); + emit OutcomeTokenPurchase(msg.sender, outcomeTokenIndex, outcomeTokenCount, cost); } /// @dev Allows to sell outcome tokens to market maker @@ -142,7 +142,7 @@ contract StandardMarket is Market { // Check profit is not too low require(profit > 0 && profit >= minProfit); // Transfer outcome tokens to markets contract to sell all outcomes - require(eventContract.outcomeTokens(outcomeTokenIndex).transferFrom(msg.sender, this, outcomeTokenCount)); + require(eventContract.outcomeTokens(outcomeTokenIndex).transferFrom(msg.sender, address(this), outcomeTokenCount)); // Sell all outcomes eventContract.sellAllOutcomes(outcomeTokenProfit); // Transfer profit to seller @@ -150,7 +150,7 @@ contract StandardMarket is Market { // Subtract outcome token count from market maker net balance require(int(outcomeTokenCount) >= 0); netOutcomeTokensSold[outcomeTokenIndex] = netOutcomeTokensSold[outcomeTokenIndex].sub(int(outcomeTokenCount)); - OutcomeTokenSale(msg.sender, outcomeTokenIndex, outcomeTokenCount, profit); + emit OutcomeTokenSale(msg.sender, outcomeTokenIndex, outcomeTokenCount, profit); } /// @dev Buys all outcomes, then sells all shares of selected outcome which were bought, keeping @@ -164,11 +164,11 @@ contract StandardMarket is Market { returns (uint cost) { // Buy all outcomes - require( eventContract.collateralToken().transferFrom(msg.sender, this, outcomeTokenCount) - && eventContract.collateralToken().approve(eventContract, outcomeTokenCount)); + require( eventContract.collateralToken().transferFrom(msg.sender, address(this), outcomeTokenCount) + && eventContract.collateralToken().approve(address(eventContract), outcomeTokenCount)); eventContract.buyAllOutcomes(outcomeTokenCount); // Short sell selected outcome - eventContract.outcomeTokens(outcomeTokenIndex).approve(this, outcomeTokenCount); + eventContract.outcomeTokens(outcomeTokenIndex).approve(address(this), outcomeTokenCount); uint profit = this.sell(outcomeTokenIndex, outcomeTokenCount, minProfit); cost = outcomeTokenCount - profit; // Transfer outcome tokens to buyer @@ -178,7 +178,7 @@ contract StandardMarket is Market { require(eventContract.outcomeTokens(i).transfer(msg.sender, outcomeTokenCount)); // Send change back to buyer require(eventContract.collateralToken().transfer(msg.sender, profit)); - OutcomeTokenShortSale(msg.sender, outcomeTokenIndex, outcomeTokenCount, cost); + emit OutcomeTokenShortSale(msg.sender, outcomeTokenIndex, outcomeTokenCount, cost); } /// @dev Calculates fee to be paid to market maker @@ -186,7 +186,7 @@ contract StandardMarket is Market { /// @return Fee for trade function calcMarketFee(uint outcomeTokenCost) public - constant + view returns (uint) { return outcomeTokenCost * fee / FEE_RANGE; diff --git a/test/compilationTests/gnosis/Markets/StandardMarketFactory.sol b/test/compilationTests/gnosis/Markets/StandardMarketFactory.sol index 101c37a2..7245da89 100644 --- a/test/compilationTests/gnosis/Markets/StandardMarketFactory.sol +++ b/test/compilationTests/gnosis/Markets/StandardMarketFactory.sol @@ -1,4 +1,4 @@ -pragma solidity ^0.4.11; +pragma solidity >=0.0; import "../Markets/MarketFactory.sol"; import "../Markets/StandardMarket.sol"; @@ -20,6 +20,6 @@ contract StandardMarketFactory is MarketFactory { returns (Market market) { market = new StandardMarket(msg.sender, eventContract, marketMaker, fee); - MarketCreation(msg.sender, market, eventContract, marketMaker, fee); + emit MarketCreation(msg.sender, market, eventContract, marketMaker, fee); } } diff --git a/test/compilationTests/gnosis/Migrations.sol b/test/compilationTests/gnosis/Migrations.sol index 7e7fe8d4..396e3649 100644 --- a/test/compilationTests/gnosis/Migrations.sol +++ b/test/compilationTests/gnosis/Migrations.sol @@ -1,4 +1,4 @@ -pragma solidity ^0.4.4; +pragma solidity >=0.0; contract Migrations { address public owner; @@ -8,15 +8,15 @@ contract Migrations { if (msg.sender == owner) _; } - function Migrations() { + constructor() public { owner = msg.sender; } - function setCompleted(uint completed) restricted { + function setCompleted(uint completed) public restricted { last_completed_migration = completed; } - function upgrade(address new_address) restricted { + function upgrade(address new_address) public restricted { Migrations upgraded = Migrations(new_address); upgraded.setCompleted(last_completed_migration); } diff --git a/test/compilationTests/gnosis/Oracles/CentralizedOracle.sol b/test/compilationTests/gnosis/Oracles/CentralizedOracle.sol index 26acf526..ee0d50a3 100644 --- a/test/compilationTests/gnosis/Oracles/CentralizedOracle.sol +++ b/test/compilationTests/gnosis/Oracles/CentralizedOracle.sol @@ -1,4 +1,4 @@ -pragma solidity ^0.4.11; +pragma solidity >=0.0; import "../Oracles/Oracle.sol"; @@ -34,7 +34,7 @@ contract CentralizedOracle is Oracle { */ /// @dev Constructor sets owner address and IPFS hash /// @param _ipfsHash Hash identifying off chain event description - function CentralizedOracle(address _owner, bytes _ipfsHash) + constructor(address _owner, bytes memory _ipfsHash) public { // Description hash cannot be null @@ -52,7 +52,7 @@ contract CentralizedOracle is Oracle { // Result is not set yet require(!isSet); owner = newOwner; - OwnerReplacement(newOwner); + emit OwnerReplacement(newOwner); } /// @dev Sets event outcome @@ -65,14 +65,14 @@ contract CentralizedOracle is Oracle { require(!isSet); isSet = true; outcome = _outcome; - OutcomeAssignment(_outcome); + emit OutcomeAssignment(_outcome); } /// @dev Returns if winning outcome is set /// @return Is outcome set? function isOutcomeSet() public - constant + view returns (bool) { return isSet; @@ -82,7 +82,7 @@ contract CentralizedOracle is Oracle { /// @return Outcome function getOutcome() public - constant + view returns (int) { return outcome; diff --git a/test/compilationTests/gnosis/Oracles/CentralizedOracleFactory.sol b/test/compilationTests/gnosis/Oracles/CentralizedOracleFactory.sol index 62a12cf4..649aaee5 100644 --- a/test/compilationTests/gnosis/Oracles/CentralizedOracleFactory.sol +++ b/test/compilationTests/gnosis/Oracles/CentralizedOracleFactory.sol @@ -1,4 +1,4 @@ -pragma solidity ^0.4.11; +pragma solidity >=0.0; import "../Oracles/CentralizedOracle.sol"; @@ -17,11 +17,11 @@ contract CentralizedOracleFactory { /// @dev Creates a new centralized oracle contract /// @param ipfsHash Hash identifying off chain event description /// @return Oracle contract - function createCentralizedOracle(bytes ipfsHash) + function createCentralizedOracle(bytes memory ipfsHash) public returns (CentralizedOracle centralizedOracle) { centralizedOracle = new CentralizedOracle(msg.sender, ipfsHash); - CentralizedOracleCreation(msg.sender, centralizedOracle, ipfsHash); + emit CentralizedOracleCreation(msg.sender, centralizedOracle, ipfsHash); } } diff --git a/test/compilationTests/gnosis/Oracles/DifficultyOracle.sol b/test/compilationTests/gnosis/Oracles/DifficultyOracle.sol index 87351dfa..c1b43235 100644 --- a/test/compilationTests/gnosis/Oracles/DifficultyOracle.sol +++ b/test/compilationTests/gnosis/Oracles/DifficultyOracle.sol @@ -1,4 +1,4 @@ -pragma solidity ^0.4.11; +pragma solidity >=0.0; import "../Oracles/Oracle.sol"; @@ -22,7 +22,7 @@ contract DifficultyOracle is Oracle { */ /// @dev Contract constructor validates and sets target block number /// @param _blockNumber Target block number - function DifficultyOracle(uint _blockNumber) + constructor(uint _blockNumber) public { // Block has to be in the future @@ -37,14 +37,14 @@ contract DifficultyOracle is Oracle { // Block number was reached and outcome was not set yet require(block.number >= blockNumber && difficulty == 0); difficulty = block.difficulty; - OutcomeAssignment(difficulty); + emit OutcomeAssignment(difficulty); } /// @dev Returns if difficulty is set /// @return Is outcome set? function isOutcomeSet() public - constant + view returns (bool) { // Difficulty is always bigger than 0 @@ -55,7 +55,7 @@ contract DifficultyOracle is Oracle { /// @return Outcome function getOutcome() public - constant + view returns (int) { return int(difficulty); diff --git a/test/compilationTests/gnosis/Oracles/DifficultyOracleFactory.sol b/test/compilationTests/gnosis/Oracles/DifficultyOracleFactory.sol index 2e97362c..e04f904a 100644 --- a/test/compilationTests/gnosis/Oracles/DifficultyOracleFactory.sol +++ b/test/compilationTests/gnosis/Oracles/DifficultyOracleFactory.sol @@ -1,4 +1,4 @@ -pragma solidity ^0.4.11; +pragma solidity >=0.0; import "../Oracles/DifficultyOracle.sol"; @@ -22,6 +22,6 @@ contract DifficultyOracleFactory { returns (DifficultyOracle difficultyOracle) { difficultyOracle = new DifficultyOracle(blockNumber); - DifficultyOracleCreation(msg.sender, difficultyOracle, blockNumber); + emit DifficultyOracleCreation(msg.sender, difficultyOracle, blockNumber); } } diff --git a/test/compilationTests/gnosis/Oracles/FutarchyOracle.sol b/test/compilationTests/gnosis/Oracles/FutarchyOracle.sol index 524103d8..ad56dcdc 100644 --- a/test/compilationTests/gnosis/Oracles/FutarchyOracle.sol +++ b/test/compilationTests/gnosis/Oracles/FutarchyOracle.sol @@ -1,4 +1,4 @@ -pragma solidity ^0.4.11; +pragma solidity >=0.0; import "../Oracles/Oracle.sol"; import "../Events/EventFactory.sol"; import "../Markets/MarketFactory.sol"; @@ -55,7 +55,7 @@ contract FutarchyOracle is Oracle { /// @param marketMaker Market maker contract /// @param fee Market fee /// @param _deadline Decision deadline - function FutarchyOracle( + constructor( address _creator, EventFactory eventFactory, Token collateralToken, @@ -95,17 +95,17 @@ contract FutarchyOracle is Oracle { isCreator { // Buy all outcomes - require( categoricalEvent.collateralToken().transferFrom(creator, this, funding) - && categoricalEvent.collateralToken().approve(categoricalEvent, funding)); + require( categoricalEvent.collateralToken().transferFrom(creator, address(this), funding) + && categoricalEvent.collateralToken().approve(address(categoricalEvent), funding)); categoricalEvent.buyAllOutcomes(funding); // Fund each market with outcome tokens from categorical event for (uint8 i = 0; i < markets.length; i++) { Market market = markets[i]; // Approve funding for market - require(market.eventContract().collateralToken().approve(market, funding)); + require(market.eventContract().collateralToken().approve(address(market), funding)); market.fund(funding); } - FutarchyFunding(funding); + emit FutarchyFunding(funding); } /// @dev Closes market for winning outcome and redeems winnings and sends all collateral tokens to creator @@ -122,8 +122,8 @@ contract FutarchyOracle is Oracle { market.withdrawFees(); // Redeem collateral token for winning outcome tokens and transfer collateral tokens to creator categoricalEvent.redeemWinnings(); - require(categoricalEvent.collateralToken().transfer(creator, categoricalEvent.collateralToken().balanceOf(this))); - FutarchyClosing(); + require(categoricalEvent.collateralToken().transfer(creator, categoricalEvent.collateralToken().balanceOf(address(this)))); + emit FutarchyClosing(); } /// @dev Allows to set the oracle outcome based on the market with largest long position @@ -144,14 +144,14 @@ contract FutarchyOracle is Oracle { } winningMarketIndex = highestIndex; isSet = true; - OutcomeAssignment(winningMarketIndex); + emit OutcomeAssignment(winningMarketIndex); } /// @dev Returns if winning outcome is set /// @return Is outcome set? function isOutcomeSet() public - constant + view returns (bool) { return isSet; @@ -161,7 +161,7 @@ contract FutarchyOracle is Oracle { /// @return Outcome function getOutcome() public - constant + view returns (int) { return int(winningMarketIndex); diff --git a/test/compilationTests/gnosis/Oracles/FutarchyOracleFactory.sol b/test/compilationTests/gnosis/Oracles/FutarchyOracleFactory.sol index 62eab4f0..87bdf54b 100644 --- a/test/compilationTests/gnosis/Oracles/FutarchyOracleFactory.sol +++ b/test/compilationTests/gnosis/Oracles/FutarchyOracleFactory.sol @@ -1,4 +1,4 @@ -pragma solidity ^0.4.11; +pragma solidity >=0.0; import "../Oracles/FutarchyOracle.sol"; @@ -33,10 +33,10 @@ contract FutarchyOracleFactory { */ /// @dev Constructor sets event factory contract /// @param _eventFactory Event factory contract - function FutarchyOracleFactory(EventFactory _eventFactory) + constructor(EventFactory _eventFactory) public { - require(address(_eventFactory) != 0); + require(address(_eventFactory) != address(0)); eventFactory = _eventFactory; } @@ -78,7 +78,7 @@ contract FutarchyOracleFactory { fee, deadline ); - FutarchyOracleCreation( + emit FutarchyOracleCreation( msg.sender, futarchyOracle, collateralToken, diff --git a/test/compilationTests/gnosis/Oracles/MajorityOracle.sol b/test/compilationTests/gnosis/Oracles/MajorityOracle.sol index 1562ce48..6913713b 100644 --- a/test/compilationTests/gnosis/Oracles/MajorityOracle.sol +++ b/test/compilationTests/gnosis/Oracles/MajorityOracle.sol @@ -1,4 +1,4 @@ -pragma solidity ^0.4.11; +pragma solidity >=0.0; import "../Oracles/Oracle.sol"; @@ -16,14 +16,14 @@ contract MajorityOracle is Oracle { */ /// @dev Allows to create an oracle for a majority vote based on other oracles /// @param _oracles List of oracles taking part in the majority vote - function MajorityOracle(Oracle[] _oracles) + constructor(Oracle[] memory _oracles) public { // At least 2 oracles should be defined require(_oracles.length > 2); for (uint i = 0; i < _oracles.length; i++) // Oracle address cannot be null - require(address(_oracles[i]) != 0); + require(address(_oracles[i]) != address(0)); oracles = _oracles; } @@ -32,6 +32,7 @@ contract MajorityOracle is Oracle { /// @return Outcome function getStatusAndOutcome() public + view returns (bool outcomeSet, int outcome) { uint i; @@ -69,10 +70,10 @@ contract MajorityOracle is Oracle { /// @return Is outcome set? function isOutcomeSet() public - constant + view returns (bool) { - var (outcomeSet, ) = getStatusAndOutcome(); + (bool outcomeSet, ) = getStatusAndOutcome(); return outcomeSet; } @@ -80,10 +81,10 @@ contract MajorityOracle is Oracle { /// @return Outcome function getOutcome() public - constant + view returns (int) { - var (, winningOutcome) = getStatusAndOutcome(); + (, int winningOutcome) = getStatusAndOutcome(); return winningOutcome; } } diff --git a/test/compilationTests/gnosis/Oracles/MajorityOracleFactory.sol b/test/compilationTests/gnosis/Oracles/MajorityOracleFactory.sol index 0024516a..a4845a36 100644 --- a/test/compilationTests/gnosis/Oracles/MajorityOracleFactory.sol +++ b/test/compilationTests/gnosis/Oracles/MajorityOracleFactory.sol @@ -1,4 +1,4 @@ -pragma solidity ^0.4.11; +pragma solidity >=0.0; import "../Oracles/MajorityOracle.sol"; @@ -17,11 +17,11 @@ contract MajorityOracleFactory { /// @dev Creates a new majority oracle contract /// @param oracles List of oracles taking part in the majority vote /// @return Oracle contract - function createMajorityOracle(Oracle[] oracles) + function createMajorityOracle(Oracle[] memory oracles) public returns (MajorityOracle majorityOracle) { majorityOracle = new MajorityOracle(oracles); - MajorityOracleCreation(msg.sender, majorityOracle, oracles); + emit MajorityOracleCreation(msg.sender, majorityOracle, oracles); } } diff --git a/test/compilationTests/gnosis/Oracles/Oracle.sol b/test/compilationTests/gnosis/Oracles/Oracle.sol index cf96eb9f..835d889e 100644 --- a/test/compilationTests/gnosis/Oracles/Oracle.sol +++ b/test/compilationTests/gnosis/Oracles/Oracle.sol @@ -1,9 +1,9 @@ -pragma solidity ^0.4.11; +pragma solidity >=0.0; /// @title Abstract oracle contract - Functions to be implemented by oracles contract Oracle { - function isOutcomeSet() public constant returns (bool); - function getOutcome() public constant returns (int); + function isOutcomeSet() public view returns (bool); + function getOutcome() public view returns (int); } diff --git a/test/compilationTests/gnosis/Oracles/SignedMessageOracle.sol b/test/compilationTests/gnosis/Oracles/SignedMessageOracle.sol index d541ab46..900067cd 100644 --- a/test/compilationTests/gnosis/Oracles/SignedMessageOracle.sol +++ b/test/compilationTests/gnosis/Oracles/SignedMessageOracle.sol @@ -1,4 +1,4 @@ -pragma solidity ^0.4.11; +pragma solidity >=0.0; import "../Oracles/Oracle.sol"; @@ -38,7 +38,7 @@ contract SignedMessageOracle is Oracle { /// @param v Signature parameter /// @param r Signature parameter /// @param s Signature parameter - function SignedMessageOracle(bytes32 _descriptionHash, uint8 v, bytes32 r, bytes32 s) + constructor(bytes32 _descriptionHash, uint8 v, bytes32 r, bytes32 s) public { signer = ecrecover(_descriptionHash, v, r, s); @@ -58,10 +58,10 @@ contract SignedMessageOracle is Oracle { // Result is not set yet and nonce and signer are valid require( !isSet && _nonce > nonce - && signer == ecrecover(keccak256(descriptionHash, newSigner, _nonce), v, r, s)); + && signer == ecrecover(keccak256(abi.encodePacked(descriptionHash, newSigner, _nonce)), v, r, s)); nonce = _nonce; signer = newSigner; - SignerReplacement(newSigner); + emit SignerReplacement(newSigner); } /// @dev Sets outcome based on signed message @@ -74,17 +74,17 @@ contract SignedMessageOracle is Oracle { { // Result is not set yet and signer is valid require( !isSet - && signer == ecrecover(keccak256(descriptionHash, _outcome), v, r, s)); + && signer == ecrecover(keccak256(abi.encodePacked(descriptionHash, _outcome)), v, r, s)); isSet = true; outcome = _outcome; - OutcomeAssignment(_outcome); + emit OutcomeAssignment(_outcome); } /// @dev Returns if winning outcome /// @return Is outcome set? function isOutcomeSet() public - constant + view returns (bool) { return isSet; @@ -94,7 +94,7 @@ contract SignedMessageOracle is Oracle { /// @return Outcome function getOutcome() public - constant + view returns (int) { return outcome; diff --git a/test/compilationTests/gnosis/Oracles/SignedMessageOracleFactory.sol b/test/compilationTests/gnosis/Oracles/SignedMessageOracleFactory.sol index 0884d8ca..18617faa 100644 --- a/test/compilationTests/gnosis/Oracles/SignedMessageOracleFactory.sol +++ b/test/compilationTests/gnosis/Oracles/SignedMessageOracleFactory.sol @@ -1,4 +1,4 @@ -pragma solidity ^0.4.11; +pragma solidity >=0.0; import "../Oracles/SignedMessageOracle.sol"; @@ -26,6 +26,6 @@ contract SignedMessageOracleFactory { { signedMessageOracle = new SignedMessageOracle(descriptionHash, v, r, s); address oracle = ecrecover(descriptionHash, v, r, s); - SignedMessageOracleCreation(msg.sender, signedMessageOracle, oracle); + emit SignedMessageOracleCreation(msg.sender, signedMessageOracle, oracle); } } diff --git a/test/compilationTests/gnosis/Oracles/UltimateOracle.sol b/test/compilationTests/gnosis/Oracles/UltimateOracle.sol index fe8b4ec7..d4574715 100644 --- a/test/compilationTests/gnosis/Oracles/UltimateOracle.sol +++ b/test/compilationTests/gnosis/Oracles/UltimateOracle.sol @@ -1,4 +1,4 @@ -pragma solidity ^0.4.11; +pragma solidity >=0.0; import "../Oracles/Oracle.sol"; import "../Tokens/Token.sol"; import "../Utils/Math.sol"; @@ -46,7 +46,7 @@ contract UltimateOracle is Oracle { /// @param _challengePeriod Time to challenge oracle outcome /// @param _challengeAmount Amount to challenge the outcome /// @param _frontRunnerPeriod Time to overbid the front-runner - function UltimateOracle( + constructor( Oracle _forwardedOracle, Token _collateralToken, uint8 _spreadMultiplier, @@ -57,8 +57,8 @@ contract UltimateOracle is Oracle { public { // Validate inputs - require( address(_forwardedOracle) != 0 - && address(_collateralToken) != 0 + require( address(_forwardedOracle) != address(0) + && address(_collateralToken) != address(0) && _spreadMultiplier >= 2 && _challengePeriod > 0 && _challengeAmount > 0 @@ -81,7 +81,7 @@ contract UltimateOracle is Oracle { && forwardedOracle.isOutcomeSet()); forwardedOutcome = forwardedOracle.getOutcome(); forwardedOutcomeSetTimestamp = now; - ForwardedOracleOutcomeAssignment(forwardedOutcome); + emit ForwardedOracleOutcomeAssignment(forwardedOutcome); } /// @dev Allows to challenge the oracle outcome @@ -92,13 +92,13 @@ contract UltimateOracle is Oracle { // There was no challenge yet or the challenge period expired require( !isChallenged() && !isChallengePeriodOver() - && collateralToken.transferFrom(msg.sender, this, challengeAmount)); + && collateralToken.transferFrom(msg.sender, address(this), challengeAmount)); outcomeAmounts[msg.sender][_outcome] = challengeAmount; totalOutcomeAmounts[_outcome] = challengeAmount; totalAmount = challengeAmount; frontRunner = _outcome; frontRunnerSetTimestamp = now; - OutcomeChallenge(msg.sender, _outcome); + emit OutcomeChallenge(msg.sender, _outcome); } /// @dev Allows to challenge the oracle outcome @@ -113,7 +113,7 @@ contract UltimateOracle is Oracle { // Outcome is challenged and front runner period is not over yet and tokens can be transferred require( isChallenged() && !isFrontRunnerPeriodOver() - && collateralToken.transferFrom(msg.sender, this, amount)); + && collateralToken.transferFrom(msg.sender, address(this), amount)); outcomeAmounts[msg.sender][_outcome] = outcomeAmounts[msg.sender][_outcome].add(amount); totalOutcomeAmounts[_outcome] = totalOutcomeAmounts[_outcome].add(amount); totalAmount = totalAmount.add(amount); @@ -122,7 +122,7 @@ contract UltimateOracle is Oracle { frontRunner = _outcome; frontRunnerSetTimestamp = now; } - OutcomeVote(msg.sender, _outcome, amount); + emit OutcomeVote(msg.sender, _outcome, amount); } /// @dev Withdraws winnings for user @@ -137,13 +137,14 @@ contract UltimateOracle is Oracle { outcomeAmounts[msg.sender][frontRunner] = 0; // Transfer earnings to contributor require(collateralToken.transfer(msg.sender, amount)); - Withdrawal(msg.sender, amount); + emit Withdrawal(msg.sender, amount); } /// @dev Checks if time to challenge the outcome is over /// @return Is challenge period over? function isChallengePeriodOver() public + view returns (bool) { return forwardedOutcomeSetTimestamp != 0 && now.sub(forwardedOutcomeSetTimestamp) > challengePeriod; @@ -153,6 +154,7 @@ contract UltimateOracle is Oracle { /// @return Is front runner period over? function isFrontRunnerPeriodOver() public + view returns (bool) { return frontRunnerSetTimestamp != 0 && now.sub(frontRunnerSetTimestamp) > frontRunnerPeriod; @@ -162,6 +164,7 @@ contract UltimateOracle is Oracle { /// @return Is challenged? function isChallenged() public + view returns (bool) { return frontRunnerSetTimestamp != 0; @@ -171,7 +174,7 @@ contract UltimateOracle is Oracle { /// @return Is outcome set? function isOutcomeSet() public - constant + view returns (bool) { return isChallengePeriodOver() && !isChallenged() @@ -182,7 +185,7 @@ contract UltimateOracle is Oracle { /// @return Outcome function getOutcome() public - constant + view returns (int) { if (isFrontRunnerPeriodOver()) diff --git a/test/compilationTests/gnosis/Oracles/UltimateOracleFactory.sol b/test/compilationTests/gnosis/Oracles/UltimateOracleFactory.sol index 67f8a96e..352872ac 100644 --- a/test/compilationTests/gnosis/Oracles/UltimateOracleFactory.sol +++ b/test/compilationTests/gnosis/Oracles/UltimateOracleFactory.sol @@ -1,4 +1,4 @@ -pragma solidity ^0.4.11; +pragma solidity >=0.0; import "../Oracles/UltimateOracle.sol"; @@ -50,7 +50,7 @@ contract UltimateOracleFactory { challengeAmount, frontRunnerPeriod ); - UltimateOracleCreation( + emit UltimateOracleCreation( msg.sender, ultimateOracle, oracle, diff --git a/test/compilationTests/gnosis/Tokens/EtherToken.sol b/test/compilationTests/gnosis/Tokens/EtherToken.sol index f6e73e5a..987b62c6 100644 --- a/test/compilationTests/gnosis/Tokens/EtherToken.sol +++ b/test/compilationTests/gnosis/Tokens/EtherToken.sol @@ -1,4 +1,4 @@ -pragma solidity ^0.4.11; +pragma solidity >=0.0; import "../Tokens/StandardToken.sol"; @@ -30,7 +30,7 @@ contract EtherToken is StandardToken { { balances[msg.sender] = balances[msg.sender].add(msg.value); totalTokens = totalTokens.add(msg.value); - Deposit(msg.sender, msg.value); + emit Deposit(msg.sender, msg.value); } /// @dev Sells tokens in exchange for Ether, exchanging them 1:1 @@ -42,6 +42,6 @@ contract EtherToken is StandardToken { balances[msg.sender] = balances[msg.sender].sub(value); totalTokens = totalTokens.sub(value); msg.sender.transfer(value); - Withdrawal(msg.sender, value); + emit Withdrawal(msg.sender, value); } } diff --git a/test/compilationTests/gnosis/Tokens/OutcomeToken.sol b/test/compilationTests/gnosis/Tokens/OutcomeToken.sol index fd1fa590..11f4ece1 100644 --- a/test/compilationTests/gnosis/Tokens/OutcomeToken.sol +++ b/test/compilationTests/gnosis/Tokens/OutcomeToken.sol @@ -1,4 +1,4 @@ -pragma solidity ^0.4.11; +pragma solidity >=0.0; import "../Tokens/StandardToken.sol"; @@ -31,12 +31,12 @@ contract OutcomeToken is StandardToken { * Public functions */ /// @dev Constructor sets events contract address - function OutcomeToken() + constructor() public { eventContract = msg.sender; } - + /// @dev Events contract issues new tokens for address. Returns success /// @param _for Address of receiver /// @param outcomeTokenCount Number of tokens to issue @@ -46,7 +46,7 @@ contract OutcomeToken is StandardToken { { balances[_for] = balances[_for].add(outcomeTokenCount); totalTokens = totalTokens.add(outcomeTokenCount); - Issuance(_for, outcomeTokenCount); + emit Issuance(_for, outcomeTokenCount); } /// @dev Events contract revokes tokens for address. Returns success @@ -58,6 +58,6 @@ contract OutcomeToken is StandardToken { { balances[_for] = balances[_for].sub(outcomeTokenCount); totalTokens = totalTokens.sub(outcomeTokenCount); - Revocation(_for, outcomeTokenCount); + emit Revocation(_for, outcomeTokenCount); } } diff --git a/test/compilationTests/gnosis/Tokens/StandardToken.sol b/test/compilationTests/gnosis/Tokens/StandardToken.sol index fc899ca6..09916cc1 100644 --- a/test/compilationTests/gnosis/Tokens/StandardToken.sol +++ b/test/compilationTests/gnosis/Tokens/StandardToken.sol @@ -1,4 +1,4 @@ -pragma solidity ^0.4.11; +pragma solidity >=0.0; import "../Tokens/Token.sol"; import "../Utils/Math.sol"; @@ -30,7 +30,7 @@ contract StandardToken is Token { return false; balances[msg.sender] -= value; balances[to] += value; - Transfer(msg.sender, to, value); + emit Transfer(msg.sender, to, value); return true; } @@ -50,7 +50,7 @@ contract StandardToken is Token { balances[from] -= value; allowances[from][msg.sender] -= value; balances[to] += value; - Transfer(from, to, value); + emit Transfer(from, to, value); return true; } @@ -63,7 +63,7 @@ contract StandardToken is Token { returns (bool) { allowances[msg.sender][spender] = value; - Approval(msg.sender, spender, value); + emit Approval(msg.sender, spender, value); return true; } @@ -73,7 +73,7 @@ contract StandardToken is Token { /// @return Remaining allowance for spender function allowance(address owner, address spender) public - constant + view returns (uint) { return allowances[owner][spender]; @@ -84,7 +84,7 @@ contract StandardToken is Token { /// @return Balance of owner function balanceOf(address owner) public - constant + view returns (uint) { return balances[owner]; @@ -94,7 +94,7 @@ contract StandardToken is Token { /// @return Total supply function totalSupply() public - constant + view returns (uint) { return totalTokens; diff --git a/test/compilationTests/gnosis/Tokens/Token.sol b/test/compilationTests/gnosis/Tokens/Token.sol index 19bb618b..777a8d7c 100644 --- a/test/compilationTests/gnosis/Tokens/Token.sol +++ b/test/compilationTests/gnosis/Tokens/Token.sol @@ -1,5 +1,5 @@ /// Implements ERC 20 Token standard: https://github.com/ethereum/EIPs/issues/20 -pragma solidity ^0.4.11; +pragma solidity >=0.0; /// @title Abstract token contract - Functions to be implemented by token contracts @@ -17,7 +17,7 @@ contract Token { function transfer(address to, uint value) public returns (bool); function transferFrom(address from, address to, uint value) public returns (bool); function approve(address spender, uint value) public returns (bool); - function balanceOf(address owner) public constant returns (uint); - function allowance(address owner, address spender) public constant returns (uint); - function totalSupply() public constant returns (uint); + function balanceOf(address owner) public view returns (uint); + function allowance(address owner, address spender) public view returns (uint); + function totalSupply() public view returns (uint); } diff --git a/test/compilationTests/gnosis/Utils/Math.sol b/test/compilationTests/gnosis/Utils/Math.sol index 95d95346..2a741e53 100644 --- a/test/compilationTests/gnosis/Utils/Math.sol +++ b/test/compilationTests/gnosis/Utils/Math.sol @@ -1,4 +1,4 @@ -pragma solidity ^0.4.11; +pragma solidity >=0.0; /// @title Math library - Allows calculation of logarithmic and exponential functions @@ -22,7 +22,7 @@ library Math { /// @return e**x function exp(int x) public - constant + pure returns (uint) { // revert if x is > MAX_POWER, where @@ -107,7 +107,7 @@ library Math { /// @return ln(x) function ln(uint x) public - constant + pure returns (int) { require(x > 0); @@ -157,7 +157,7 @@ library Math { /// @return logarithmic value function floorLog2(uint x) public - constant + pure returns (int lo) { lo = -64; @@ -176,9 +176,9 @@ library Math { /// @dev Returns maximum of an array /// @param nums Numbers to look through /// @return Maximum number - function max(int[] nums) + function max(int[] memory nums) public - constant + pure returns (int max) { require(nums.length > 0); @@ -194,7 +194,7 @@ library Math { /// @return Did no overflow occur? function safeToAdd(uint a, uint b) public - constant + pure returns (bool) { return a + b >= a; @@ -206,7 +206,7 @@ library Math { /// @return Did no underflow occur? function safeToSub(uint a, uint b) public - constant + pure returns (bool) { return a >= b; @@ -218,7 +218,7 @@ library Math { /// @return Did no overflow occur? function safeToMul(uint a, uint b) public - constant + pure returns (bool) { return b == 0 || a * b / b == a; @@ -230,7 +230,7 @@ library Math { /// @return Sum function add(uint a, uint b) public - constant + pure returns (uint) { require(safeToAdd(a, b)); @@ -243,7 +243,7 @@ library Math { /// @return Difference function sub(uint a, uint b) public - constant + pure returns (uint) { require(safeToSub(a, b)); @@ -256,7 +256,7 @@ library Math { /// @return Product function mul(uint a, uint b) public - constant + pure returns (uint) { require(safeToMul(a, b)); @@ -269,7 +269,7 @@ library Math { /// @return Did no overflow occur? function safeToAdd(int a, int b) public - constant + pure returns (bool) { return (b >= 0 && a + b >= a) || (b < 0 && a + b < a); @@ -281,7 +281,7 @@ library Math { /// @return Did no underflow occur? function safeToSub(int a, int b) public - constant + pure returns (bool) { return (b >= 0 && a - b <= a) || (b < 0 && a - b > a); @@ -293,7 +293,7 @@ library Math { /// @return Did no overflow occur? function safeToMul(int a, int b) public - constant + pure returns (bool) { return (b == 0) || (a * b / b == a); @@ -305,7 +305,7 @@ library Math { /// @return Sum function add(int a, int b) public - constant + pure returns (int) { require(safeToAdd(a, b)); @@ -318,7 +318,7 @@ library Math { /// @return Difference function sub(int a, int b) public - constant + pure returns (int) { require(safeToSub(a, b)); @@ -331,7 +331,7 @@ library Math { /// @return Product function mul(int a, int b) public - constant + pure returns (int) { require(safeToMul(a, b)); diff --git a/test/compilationTests/milestonetracker/MilestoneTracker.sol b/test/compilationTests/milestonetracker/MilestoneTracker.sol index 318330df..891afcb1 100644 --- a/test/compilationTests/milestonetracker/MilestoneTracker.sol +++ b/test/compilationTests/milestonetracker/MilestoneTracker.sol @@ -1,4 +1,4 @@ -pragma solidity ^0.4.6; +pragma solidity >=0.0; /* Copyright 2016, Jordi Baylina @@ -22,7 +22,7 @@ pragma solidity ^0.4.6; /// @dev This contract tracks the -/// is rules the relation betwen a donor and a recipient +/// is rules the relation between a donor and a recipient /// in order to guaranty to the donor that the job will be done and to guaranty /// to the recipient that he will be paid @@ -83,14 +83,14 @@ contract MilestoneTracker { /// @dev The following modifiers only allow specific roles to call functions /// with these modifiers - modifier onlyRecipient { if (msg.sender != recipient) throw; _; } - modifier onlyArbitrator { if (msg.sender != arbitrator) throw; _; } - modifier onlyDonor { if (msg.sender != donor) throw; _; } + modifier onlyRecipient { if (msg.sender != recipient) revert(); _; } + modifier onlyArbitrator { if (msg.sender != arbitrator) revert(); _; } + modifier onlyDonor { if (msg.sender != donor) revert(); _; } /// @dev The following modifiers prevent functions from being called if the /// campaign has been canceled or if new milestones are being proposed - modifier campaignNotCanceled { if (campaignCanceled) throw; _; } - modifier notChanging { if (changingMilestones) throw; _; } + modifier campaignNotCanceled { if (campaignCanceled) revert(); _; } + modifier notChanging { if (changingMilestones) revert(); _; } // @dev Events to make the payment movements easy to find on the blockchain event NewMilestoneListProposed(); @@ -108,11 +108,11 @@ contract MilestoneTracker { /// @param _arbitrator Address assigned to be the arbitrator /// @param _donor Address assigned to be the donor /// @param _recipient Address assigned to be the recipient - function MilestoneTracker ( + constructor ( address _arbitrator, address _donor, address _recipient - ) { + ) public { arbitrator = _arbitrator; donor = _donor; recipient = _recipient; @@ -124,7 +124,7 @@ contract MilestoneTracker { ///////// /// @return The number of milestones ever created even if they were canceled - function numberOfMilestones() constant returns (uint) { + function numberOfMilestones() public view returns (uint) { return milestones.length; } @@ -135,19 +135,19 @@ contract MilestoneTracker { /// @notice `onlyArbitrator` Reassigns the arbitrator to a new address /// @param _newArbitrator The new arbitrator - function changeArbitrator(address _newArbitrator) onlyArbitrator { + function changeArbitrator(address _newArbitrator) public onlyArbitrator { arbitrator = _newArbitrator; } /// @notice `onlyDonor` Reassigns the `donor` to a new address /// @param _newDonor The new donor - function changeDonor(address _newDonor) onlyDonor { + function changeDonor(address _newDonor) public onlyDonor { donor = _newDonor; } /// @notice `onlyRecipient` Reassigns the `recipient` to a new address /// @param _newRecipient The new recipient - function changeRecipient(address _newRecipient) onlyRecipient { + function changeRecipient(address _newRecipient) public onlyRecipient { recipient = _newRecipient; } @@ -175,11 +175,11 @@ contract MilestoneTracker { /// uint reviewTime /// address paymentSource, /// bytes payData, - function proposeMilestones(bytes _newMilestones - ) onlyRecipient campaignNotCanceled { + function proposeMilestones(bytes memory _newMilestones + ) public onlyRecipient campaignNotCanceled { proposedMilestones = _newMilestones; changingMilestones = true; - NewMilestoneListProposed(); + emit NewMilestoneListProposed(); } @@ -189,23 +189,23 @@ contract MilestoneTracker { /// @notice `onlyRecipient` Cancels the proposed milestones and reactivates /// the previous set of milestones - function unproposeMilestones() onlyRecipient campaignNotCanceled { + function unproposeMilestones() public onlyRecipient campaignNotCanceled { delete proposedMilestones; changingMilestones = false; - NewMilestoneListUnproposed(); + emit NewMilestoneListUnproposed(); } /// @notice `onlyDonor` Approves the proposed milestone list - /// @param _hashProposals The sha3() of the proposed milestone list's + /// @param _hashProposals The keccak256() of the proposed milestone list's /// bytecode; this confirms that the `donor` knows the set of milestones /// they are approving function acceptProposedMilestones(bytes32 _hashProposals - ) onlyDonor campaignNotCanceled { + ) public onlyDonor campaignNotCanceled { uint i; - if (!changingMilestones) throw; - if (sha3(proposedMilestones) != _hashProposals) throw; + if (!changingMilestones) revert(); + if (keccak256(proposedMilestones) != _hashProposals) revert(); // Cancel all the unfinished milestones for (i=0; i<milestones.length; i++) { @@ -216,22 +216,22 @@ contract MilestoneTracker { // Decode the RLP encoded milestones and add them to the milestones list bytes memory mProposedMilestones = proposedMilestones; - var itmProposals = mProposedMilestones.toRLPItem(true); + RLP.RLPItem memory itmProposals = mProposedMilestones.toRLPItem(true); - if (!itmProposals.isList()) throw; + if (!itmProposals.isList()) revert(); - var itrProposals = itmProposals.iterator(); + RLP.Iterator memory itrProposals = itmProposals.iterator(); while(itrProposals.hasNext()) { - var itmProposal = itrProposals.next(); + RLP.RLPItem memory itmProposal = itrProposals.next(); - Milestone milestone = milestones[milestones.length ++]; + Milestone storage milestone = milestones[milestones.length ++]; - if (!itmProposal.isList()) throw; + if (!itmProposal.isList()) revert(); - var itrProposal = itmProposal.iterator(); + RLP.Iterator memory itrProposal = itmProposal.iterator(); milestone.description = itrProposal.next().toAscii(); milestone.url = itrProposal.next().toAscii(); @@ -249,37 +249,37 @@ contract MilestoneTracker { delete proposedMilestones; changingMilestones = false; - NewMilestoneListAccepted(); + emit NewMilestoneListAccepted(); } /// @notice `onlyRecipientOrLeadLink`Marks a milestone as DONE and /// ready for review /// @param _idMilestone ID of the milestone that has been completed function markMilestoneComplete(uint _idMilestone) - campaignNotCanceled notChanging + public campaignNotCanceled notChanging { - if (_idMilestone >= milestones.length) throw; - Milestone milestone = milestones[_idMilestone]; + if (_idMilestone >= milestones.length) revert(); + Milestone storage milestone = milestones[_idMilestone]; if ( (msg.sender != milestone.milestoneLeadLink) &&(msg.sender != recipient)) - throw; - if (milestone.status != MilestoneStatus.AcceptedAndInProgress) throw; - if (now < milestone.minCompletionDate) throw; - if (now > milestone.maxCompletionDate) throw; + revert(); + if (milestone.status != MilestoneStatus.AcceptedAndInProgress) revert(); + if (now < milestone.minCompletionDate) revert(); + if (now > milestone.maxCompletionDate) revert(); milestone.status = MilestoneStatus.Completed; milestone.doneTime = now; - ProposalStatusChanged(_idMilestone, milestone.status); + emit ProposalStatusChanged(_idMilestone, milestone.status); } /// @notice `onlyReviewer` Approves a specific milestone /// @param _idMilestone ID of the milestone that is approved function approveCompletedMilestone(uint _idMilestone) - campaignNotCanceled notChanging + public campaignNotCanceled notChanging { - if (_idMilestone >= milestones.length) throw; - Milestone milestone = milestones[_idMilestone]; + if (_idMilestone >= milestones.length) revert(); + Milestone storage milestone = milestones[_idMilestone]; if ((msg.sender != milestone.reviewer) || - (milestone.status != MilestoneStatus.Completed)) throw; + (milestone.status != MilestoneStatus.Completed)) revert(); authorizePayment(_idMilestone); } @@ -289,15 +289,15 @@ contract MilestoneTracker { /// state /// @param _idMilestone ID of the milestone that is being rejected function rejectMilestone(uint _idMilestone) - campaignNotCanceled notChanging + public campaignNotCanceled notChanging { - if (_idMilestone >= milestones.length) throw; - Milestone milestone = milestones[_idMilestone]; + if (_idMilestone >= milestones.length) revert(); + Milestone storage milestone = milestones[_idMilestone]; if ((msg.sender != milestone.reviewer) || - (milestone.status != MilestoneStatus.Completed)) throw; + (milestone.status != MilestoneStatus.Completed)) revert(); milestone.status = MilestoneStatus.AcceptedAndInProgress; - ProposalStatusChanged(_idMilestone, milestone.status); + emit ProposalStatusChanged(_idMilestone, milestone.status); } /// @notice `onlyRecipientOrLeadLink` Sends the milestone payment as @@ -305,15 +305,15 @@ contract MilestoneTracker { /// `reviewTime` has elapsed /// @param _idMilestone ID of the milestone to be paid out function requestMilestonePayment(uint _idMilestone - ) campaignNotCanceled notChanging { - if (_idMilestone >= milestones.length) throw; - Milestone milestone = milestones[_idMilestone]; + ) public campaignNotCanceled notChanging { + if (_idMilestone >= milestones.length) revert(); + Milestone storage milestone = milestones[_idMilestone]; if ( (msg.sender != milestone.milestoneLeadLink) &&(msg.sender != recipient)) - throw; + revert(); if ((milestone.status != MilestoneStatus.Completed) || (now < milestone.doneTime + milestone.reviewTime)) - throw; + revert(); authorizePayment(_idMilestone); } @@ -321,47 +321,47 @@ contract MilestoneTracker { /// @notice `onlyRecipient` Cancels a previously accepted milestone /// @param _idMilestone ID of the milestone to be canceled function cancelMilestone(uint _idMilestone) - onlyRecipient campaignNotCanceled notChanging + public onlyRecipient campaignNotCanceled notChanging { - if (_idMilestone >= milestones.length) throw; - Milestone milestone = milestones[_idMilestone]; + if (_idMilestone >= milestones.length) revert(); + Milestone storage milestone = milestones[_idMilestone]; if ((milestone.status != MilestoneStatus.AcceptedAndInProgress) && (milestone.status != MilestoneStatus.Completed)) - throw; + revert(); milestone.status = MilestoneStatus.Canceled; - ProposalStatusChanged(_idMilestone, milestone.status); + emit ProposalStatusChanged(_idMilestone, milestone.status); } /// @notice `onlyArbitrator` Forces a milestone to be paid out as long as it /// has not been paid or canceled /// @param _idMilestone ID of the milestone to be paid out function arbitrateApproveMilestone(uint _idMilestone - ) onlyArbitrator campaignNotCanceled notChanging { - if (_idMilestone >= milestones.length) throw; - Milestone milestone = milestones[_idMilestone]; + ) public onlyArbitrator campaignNotCanceled notChanging { + if (_idMilestone >= milestones.length) revert(); + Milestone storage milestone = milestones[_idMilestone]; if ((milestone.status != MilestoneStatus.AcceptedAndInProgress) && (milestone.status != MilestoneStatus.Completed)) - throw; + revert(); authorizePayment(_idMilestone); } /// @notice `onlyArbitrator` Cancels the entire campaign voiding all /// milestones. - function arbitrateCancelCampaign() onlyArbitrator campaignNotCanceled { + function arbitrateCancelCampaign() public onlyArbitrator campaignNotCanceled { campaignCanceled = true; - CampaignCanceled(); + emit CampaignCanceled(); } // @dev This internal function is executed when the milestone is paid out function authorizePayment(uint _idMilestone) internal { - if (_idMilestone >= milestones.length) throw; - Milestone milestone = milestones[_idMilestone]; + if (_idMilestone >= milestones.length) revert(); + Milestone storage milestone = milestones[_idMilestone]; // Recheck again to not pay twice - if (milestone.status == MilestoneStatus.AuthorizedForPayment) throw; + if (milestone.status == MilestoneStatus.AuthorizedForPayment) revert(); milestone.status = MilestoneStatus.AuthorizedForPayment; - if (!milestone.paymentSource.call.value(0)(milestone.payData)) - throw; - ProposalStatusChanged(_idMilestone, milestone.status); + (bool success,) = milestone.paymentSource.call.value(0)(milestone.payData); + require(success); + emit ProposalStatusChanged(_idMilestone, milestone.status); } } diff --git a/test/compilationTests/milestonetracker/RLP.sol b/test/compilationTests/milestonetracker/RLP.sol index 5bb27bb2..799233a1 100644 --- a/test/compilationTests/milestonetracker/RLP.sol +++ b/test/compilationTests/milestonetracker/RLP.sol @@ -1,4 +1,4 @@ -pragma solidity ^0.4.6; +pragma solidity >=0.0; /** * @title RLPReader @@ -30,27 +30,26 @@ library RLP { /* Iterator */ - function next(Iterator memory self) internal constant returns (RLPItem memory subItem) { + function next(Iterator memory self) internal view returns (RLPItem memory subItem) { if(hasNext(self)) { - var ptr = self._unsafe_nextPtr; - var itemLength = _itemLength(ptr); + uint ptr = self._unsafe_nextPtr; + uint itemLength = _itemLength(ptr); subItem._unsafe_memPtr = ptr; subItem._unsafe_length = itemLength; self._unsafe_nextPtr = ptr + itemLength; } else - throw; + revert(); } - function next(Iterator memory self, bool strict) internal constant returns (RLPItem memory subItem) { + function next(Iterator memory self, bool strict) internal view returns (RLPItem memory subItem) { subItem = next(self); if(strict && !_validate(subItem)) - throw; - return; + revert(); } - function hasNext(Iterator memory self) internal constant returns (bool) { - var item = self._unsafe_item; + function hasNext(Iterator memory self) internal view returns (bool) { + RLPItem memory item = self._unsafe_item; return self._unsafe_nextPtr < item._unsafe_memPtr + item._unsafe_length; } @@ -59,7 +58,7 @@ library RLP { /// @dev Creates an RLPItem from an array of RLP encoded bytes. /// @param self The RLP encoded bytes. /// @return An RLPItem - function toRLPItem(bytes memory self) internal constant returns (RLPItem memory) { + function toRLPItem(bytes memory self) internal view returns (RLPItem memory) { uint len = self.length; if (len == 0) { return RLPItem(0, 0); @@ -75,16 +74,16 @@ library RLP { /// @param self The RLP encoded bytes. /// @param strict Will throw if the data is not RLP encoded. /// @return An RLPItem - function toRLPItem(bytes memory self, bool strict) internal constant returns (RLPItem memory) { - var item = toRLPItem(self); + function toRLPItem(bytes memory self, bool strict) internal view returns (RLPItem memory) { + RLPItem memory item = toRLPItem(self); if(strict) { uint len = self.length; if(_payloadOffset(item) > len) - throw; + revert(); if(_itemLength(item._unsafe_memPtr) != len) - throw; + revert(); if(!_validate(item)) - throw; + revert(); } return item; } @@ -92,14 +91,14 @@ library RLP { /// @dev Check if the RLP item is null. /// @param self The RLP item. /// @return 'true' if the item is null. - function isNull(RLPItem memory self) internal constant returns (bool ret) { + function isNull(RLPItem memory self) internal view returns (bool ret) { return self._unsafe_length == 0; } /// @dev Check if the RLP item is a list. /// @param self The RLP item. /// @return 'true' if the item is a list. - function isList(RLPItem memory self) internal constant returns (bool ret) { + function isList(RLPItem memory self) internal view returns (bool ret) { if (self._unsafe_length == 0) return false; uint memPtr = self._unsafe_memPtr; @@ -111,7 +110,7 @@ library RLP { /// @dev Check if the RLP item is data. /// @param self The RLP item. /// @return 'true' if the item is data. - function isData(RLPItem memory self) internal constant returns (bool ret) { + function isData(RLPItem memory self) internal view returns (bool ret) { if (self._unsafe_length == 0) return false; uint memPtr = self._unsafe_memPtr; @@ -123,7 +122,7 @@ library RLP { /// @dev Check if the RLP item is empty (string or list). /// @param self The RLP item. /// @return 'true' if the item is null. - function isEmpty(RLPItem memory self) internal constant returns (bool ret) { + function isEmpty(RLPItem memory self) internal view returns (bool ret) { if(isNull(self)) return false; uint b0; @@ -137,7 +136,7 @@ library RLP { /// @dev Get the number of items in an RLP encoded list. /// @param self The RLP item. /// @return The number of items. - function items(RLPItem memory self) internal constant returns (uint) { + function items(RLPItem memory self) internal view returns (uint) { if (!isList(self)) return 0; uint b0; @@ -158,9 +157,9 @@ library RLP { /// @dev Create an iterator. /// @param self The RLP item. /// @return An 'Iterator' over the item. - function iterator(RLPItem memory self) internal constant returns (Iterator memory it) { + function iterator(RLPItem memory self) internal view returns (Iterator memory it) { if (!isList(self)) - throw; + revert(); uint ptr = self._unsafe_memPtr + _payloadOffset(self); it._unsafe_item = self; it._unsafe_nextPtr = ptr; @@ -169,22 +168,23 @@ library RLP { /// @dev Return the RLP encoded bytes. /// @param self The RLPItem. /// @return The bytes. - function toBytes(RLPItem memory self) internal constant returns (bytes memory bts) { - var len = self._unsafe_length; - if (len == 0) - return; - bts = new bytes(len); - _copyToBytes(self._unsafe_memPtr, bts, len); + function toBytes(RLPItem memory self) internal returns (bytes memory bts) { + uint len = self._unsafe_length; + if (len != 0) + { + bts = new bytes(len); + _copyToBytes(self._unsafe_memPtr, bts, len); + } } /// @dev Decode an RLPItem into bytes. This will not work if the /// RLPItem is a list. /// @param self The RLPItem. /// @return The decoded string. - function toData(RLPItem memory self) internal constant returns (bytes memory bts) { + function toData(RLPItem memory self) internal returns (bytes memory bts) { if(!isData(self)) - throw; - var (rStartPos, len) = _decode(self); + revert(); + (uint rStartPos, uint len) = _decode(self); bts = new bytes(len); _copyToBytes(rStartPos, bts, len); } @@ -193,12 +193,12 @@ library RLP { /// Warning: This is inefficient, as it requires that the list is read twice. /// @param self The RLP item. /// @return Array of RLPItems. - function toList(RLPItem memory self) internal constant returns (RLPItem[] memory list) { + function toList(RLPItem memory self) internal view returns (RLPItem[] memory list) { if(!isList(self)) - throw; - var numItems = items(self); + revert(); + uint numItems = items(self); list = new RLPItem[](numItems); - var it = iterator(self); + Iterator memory it = iterator(self); uint idx; while(hasNext(it)) { list[idx] = next(it); @@ -210,10 +210,10 @@ library RLP { /// RLPItem is a list. /// @param self The RLPItem. /// @return The decoded string. - function toAscii(RLPItem memory self) internal constant returns (string memory str) { + function toAscii(RLPItem memory self) internal returns (string memory str) { if(!isData(self)) - throw; - var (rStartPos, len) = _decode(self); + revert(); + (uint rStartPos, uint len) = _decode(self); bytes memory bts = new bytes(len); _copyToBytes(rStartPos, bts, len); str = string(bts); @@ -223,12 +223,12 @@ library RLP { /// RLPItem is a list. /// @param self The RLPItem. /// @return The decoded string. - function toUint(RLPItem memory self) internal constant returns (uint data) { + function toUint(RLPItem memory self) internal view returns (uint data) { if(!isData(self)) - throw; - var (rStartPos, len) = _decode(self); + revert(); + (uint rStartPos, uint len) = _decode(self); if (len > 32 || len == 0) - throw; + revert(); assembly { data := div(mload(rStartPos), exp(256, sub(32, len))) } @@ -238,18 +238,18 @@ library RLP { /// RLPItem is a list. /// @param self The RLPItem. /// @return The decoded string. - function toBool(RLPItem memory self) internal constant returns (bool data) { + function toBool(RLPItem memory self) internal view returns (bool data) { if(!isData(self)) - throw; - var (rStartPos, len) = _decode(self); + revert(); + (uint rStartPos, uint len) = _decode(self); if (len != 1) - throw; + revert(); uint temp; assembly { temp := byte(0, mload(rStartPos)) } if (temp > 1) - throw; + revert(); return temp == 1 ? true : false; } @@ -257,13 +257,13 @@ library RLP { /// RLPItem is a list. /// @param self The RLPItem. /// @return The decoded string. - function toByte(RLPItem memory self) internal constant returns (byte data) { + function toByte(RLPItem memory self) internal view returns (byte data) { if(!isData(self)) - throw; - var (rStartPos, len) = _decode(self); + revert(); + (uint rStartPos, uint len) = _decode(self); if (len != 1) - throw; - uint temp; + revert(); + uint8 temp; assembly { temp := byte(0, mload(rStartPos)) } @@ -274,7 +274,7 @@ library RLP { /// RLPItem is a list. /// @param self The RLPItem. /// @return The decoded string. - function toInt(RLPItem memory self) internal constant returns (int data) { + function toInt(RLPItem memory self) internal view returns (int data) { return int(toUint(self)); } @@ -282,7 +282,7 @@ library RLP { /// RLPItem is a list. /// @param self The RLPItem. /// @return The decoded string. - function toBytes32(RLPItem memory self) internal constant returns (bytes32 data) { + function toBytes32(RLPItem memory self) internal view returns (bytes32 data) { return bytes32(toUint(self)); } @@ -290,19 +290,19 @@ library RLP { /// RLPItem is a list. /// @param self The RLPItem. /// @return The decoded string. - function toAddress(RLPItem memory self) internal constant returns (address data) { + function toAddress(RLPItem memory self) internal view returns (address data) { if(!isData(self)) - throw; - var (rStartPos, len) = _decode(self); + revert(); + (uint rStartPos, uint len) = _decode(self); if (len != 20) - throw; + revert(); assembly { data := div(mload(rStartPos), exp(256, 12)) } } // Get the payload offset. - function _payloadOffset(RLPItem memory self) private constant returns (uint) { + function _payloadOffset(RLPItem memory self) private view returns (uint) { if(self._unsafe_length == 0) return 0; uint b0; @@ -320,7 +320,7 @@ library RLP { } // Get the full length of an RLP item. - function _itemLength(uint memPtr) private constant returns (uint len) { + function _itemLength(uint memPtr) private view returns (uint len) { uint b0; assembly { b0 := byte(0, mload(memPtr)) @@ -348,9 +348,9 @@ library RLP { } // Get start position and length of the data. - function _decode(RLPItem memory self) private constant returns (uint memPtr, uint len) { + function _decode(RLPItem memory self) private view returns (uint memPtr, uint len) { if(!isData(self)) - throw; + revert(); uint b0; uint start = self._unsafe_memPtr; assembly { @@ -359,9 +359,8 @@ library RLP { if (b0 < DATA_SHORT_START) { memPtr = start; len = 1; - return; } - if (b0 < DATA_LONG_START) { + else if (b0 < DATA_LONG_START) { len = self._unsafe_length - 1; memPtr = start + 1; } else { @@ -372,35 +371,31 @@ library RLP { len = self._unsafe_length - 1 - bLen; memPtr = start + bLen + 1; } - return; } // Assumes that enough memory has been allocated to store in target. - function _copyToBytes(uint btsPtr, bytes memory tgt, uint btsLen) private constant { + function _copyToBytes(uint btsPtr, bytes memory tgt, uint btsLen) private { // Exploiting the fact that 'tgt' was the last thing to be allocated, // we can write entire words, and just overwrite any excess. assembly { { - let i := 0 // Start at arr + 0x20 let words := div(add(btsLen, 31), 32) let rOffset := btsPtr let wOffset := add(tgt, 0x20) - tag_loop: - jumpi(end, eq(i, words)) + + // Start at arr + 0x20 + for { let i := 0 } not(eq(i, words)) { i := add(i, 1) } { let offset := mul(i, 0x20) mstore(add(wOffset, offset), mload(add(rOffset, offset))) - i := add(i, 1) } - jump(tag_loop) - end: mstore(add(tgt, add(0x20, mload(tgt))), 0) } } } // Check that an RLP item is valid. - function _validate(RLPItem memory self) private constant returns (bool ret) { + function _validate(RLPItem memory self) private view returns (bool ret) { // Check that RLP is well-formed. uint b0; uint b1; diff --git a/test/compilationTests/stringutils/strings.sol b/test/compilationTests/stringutils/strings.sol index 0a2d68bd..9e0518a7 100644 --- a/test/compilationTests/stringutils/strings.sol +++ b/test/compilationTests/stringutils/strings.sol @@ -33,13 +33,16 @@ * `s.splitNew('.')` leaves s unmodified, and returns two values * corresponding to the left and right parts of the string. */ + +pragma solidity >=0.0; + library strings { struct slice { uint _len; uint _ptr; } - function memcpy(uint dest, uint src, uint len) private { + function memcpy(uint dest, uint src, uint len) private pure { // Copy word-length chunks while possible for(; len >= 32; len -= 32) { assembly { @@ -63,7 +66,7 @@ library strings { * @param self The string to make a slice from. * @return A newly allocated slice containing the entire string. */ - function toSlice(string self) internal returns (slice) { + function toSlice(string memory self) internal pure returns (slice memory) { uint ptr; assembly { ptr := add(self, 0x20) @@ -76,27 +79,27 @@ library strings { * @param self The value to find the length of. * @return The length of the string, from 0 to 32. */ - function len(bytes32 self) internal returns (uint) { + function len(bytes32 self) internal pure returns (uint) { uint ret; if (self == 0) return 0; - if (self & 0xffffffffffffffffffffffffffffffff == 0) { + if (uint256(self) & 0xffffffffffffffffffffffffffffffff == 0) { ret += 16; self = bytes32(uint(self) / 0x100000000000000000000000000000000); } - if (self & 0xffffffffffffffff == 0) { + if (uint256(self) & 0xffffffffffffffff == 0) { ret += 8; self = bytes32(uint(self) / 0x10000000000000000); } - if (self & 0xffffffff == 0) { + if (uint256(self) & 0xffffffff == 0) { ret += 4; self = bytes32(uint(self) / 0x100000000); } - if (self & 0xffff == 0) { + if (uint256(self) & 0xffff == 0) { ret += 2; self = bytes32(uint(self) / 0x10000); } - if (self & 0xff == 0) { + if (uint256(self) & 0xff == 0) { ret += 1; } return 32 - ret; @@ -104,12 +107,12 @@ library strings { /* * @dev Returns a slice containing the entire bytes32, interpreted as a - * null-termintaed utf-8 string. + * null-terminated utf-8 string. * @param self The bytes32 value to convert to a slice. * @return A new slice containing the value of the input argument up to the * first null. */ - function toSliceB32(bytes32 self) internal returns (slice ret) { + function toSliceB32(bytes32 self) internal pure returns (slice memory ret) { // Allocate space for `self` in memory, copy it there, and point ret at it assembly { let ptr := mload(0x40) @@ -125,7 +128,7 @@ library strings { * @param self The slice to copy. * @return A new slice containing the same data as `self`. */ - function copy(slice self) internal returns (slice) { + function copy(slice memory self) internal pure returns (slice memory) { return slice(self._len, self._ptr); } @@ -134,8 +137,8 @@ library strings { * @param self The slice to copy. * @return A newly allocated string containing the slice's text. */ - function toString(slice self) internal returns (string) { - var ret = new string(self._len); + function toString(slice memory self) internal pure returns (string memory) { + string memory ret = new string(self._len); uint retptr; assembly { retptr := add(ret, 32) } @@ -151,11 +154,11 @@ library strings { * @param self The slice to operate on. * @return The length of the slice in runes. */ - function len(slice self) internal returns (uint) { + function len(slice memory self) internal pure returns (uint l) { // Starting at ptr-31 means the LSB will be the byte we care about - var ptr = self._ptr - 31; - var end = ptr + self._len; - for (uint len = 0; ptr < end; len++) { + uint ptr = self._ptr - 31; + uint end = ptr + self._len; + for (l = 0; ptr < end; l++) { uint8 b; assembly { b := and(mload(ptr), 0xFF) } if (b < 0x80) { @@ -172,7 +175,6 @@ library strings { ptr += 6; } } - return len; } /* @@ -180,7 +182,7 @@ library strings { * @param self The slice to operate on. * @return True if the slice is empty, False otherwise. */ - function empty(slice self) internal returns (bool) { + function empty(slice memory self) internal pure returns (bool) { return self._len == 0; } @@ -193,13 +195,13 @@ library strings { * @param other The second slice to compare. * @return The result of the comparison. */ - function compare(slice self, slice other) internal returns (int) { + function compare(slice memory self, slice memory other) internal pure returns (int) { uint shortest = self._len; if (other._len < self._len) shortest = other._len; - var selfptr = self._ptr; - var otherptr = other._ptr; + uint selfptr = self._ptr; + uint otherptr = other._ptr; for (uint idx = 0; idx < shortest; idx += 32) { uint a; uint b; @@ -209,8 +211,11 @@ library strings { } if (a != b) { // Mask out irrelevant bytes and check again - uint mask = ~(2 ** (8 * (32 - shortest + idx)) - 1); - var diff = (a & mask) - (b & mask); + uint256 mask = uint256(-1); // 0xffff... + if(shortest < 32) { + mask = ~(2 ** (8 * (32 - shortest + idx)) - 1); + } + uint256 diff = (a & mask) - (b & mask); if (diff != 0) return int(diff); } @@ -226,7 +231,7 @@ library strings { * @param self The second slice to compare. * @return True if the slices are equal, false otherwise. */ - function equals(slice self, slice other) internal returns (bool) { + function equals(slice memory self, slice memory other) internal pure returns (bool) { return compare(self, other) == 0; } @@ -237,7 +242,7 @@ library strings { * @param rune The slice that will contain the first rune. * @return `rune`. */ - function nextRune(slice self, slice rune) internal returns (slice) { + function nextRune(slice memory self, slice memory rune) internal pure returns (slice memory) { rune._ptr = self._ptr; if (self._len == 0) { @@ -245,31 +250,31 @@ library strings { return rune; } - uint len; + uint l; uint b; // Load the first byte of the rune into the LSBs of b assembly { b := and(mload(sub(mload(add(self, 32)), 31)), 0xFF) } if (b < 0x80) { - len = 1; + l = 1; } else if(b < 0xE0) { - len = 2; + l = 2; } else if(b < 0xF0) { - len = 3; + l = 3; } else { - len = 4; + l = 4; } // Check for truncated codepoints - if (len > self._len) { + if (l > self._len) { rune._len = self._len; self._ptr += self._len; self._len = 0; return rune; } - self._ptr += len; - self._len -= len; - rune._len = len; + self._ptr += l; + self._len -= l; + rune._len = l; return rune; } @@ -279,7 +284,7 @@ library strings { * @param self The slice to operate on. * @return A slice containing only the first rune from `self`. */ - function nextRune(slice self) internal returns (slice ret) { + function nextRune(slice memory self) internal pure returns (slice memory ret) { nextRune(self, ret); } @@ -288,40 +293,40 @@ library strings { * @param self The slice to operate on. * @return The number of the first codepoint in the slice. */ - function ord(slice self) internal returns (uint ret) { + function ord(slice memory self) internal pure returns (uint ret) { if (self._len == 0) { return 0; } uint word; - uint len; - uint div = 2 ** 248; + uint length; + uint divisor = 2 ** 248; // Load the rune into the MSBs of b assembly { word:= mload(mload(add(self, 32))) } - var b = word / div; + uint b = word / divisor; if (b < 0x80) { ret = b; - len = 1; + length = 1; } else if(b < 0xE0) { ret = b & 0x1F; - len = 2; + length = 2; } else if(b < 0xF0) { ret = b & 0x0F; - len = 3; + length = 3; } else { ret = b & 0x07; - len = 4; + length = 4; } // Check for truncated codepoints - if (len > self._len) { + if (length > self._len) { return 0; } - for (uint i = 1; i < len; i++) { - div = div / 256; - b = (word / div) & 0xFF; + for (uint i = 1; i < length; i++) { + divisor = divisor / 256; + b = (word / divisor) & 0xFF; if (b & 0xC0 != 0x80) { // Invalid UTF-8 sequence return 0; @@ -337,9 +342,9 @@ library strings { * @param self The slice to hash. * @return The hash of the slice. */ - function keccak(slice self) internal returns (bytes32 ret) { + function keccak(slice memory self) internal pure returns (bytes32 ret) { assembly { - ret := sha3(mload(add(self, 32)), mload(self)) + ret := keccak256(mload(add(self, 32)), mload(self)) } } @@ -349,7 +354,7 @@ library strings { * @param needle The slice to search for. * @return True if the slice starts with the provided text, false otherwise. */ - function startsWith(slice self, slice needle) internal returns (bool) { + function startsWith(slice memory self, slice memory needle) internal pure returns (bool) { if (self._len < needle._len) { return false; } @@ -360,10 +365,10 @@ library strings { bool equal; assembly { - let len := mload(needle) + let length := mload(needle) let selfptr := mload(add(self, 0x20)) let needleptr := mload(add(needle, 0x20)) - equal := eq(sha3(selfptr, len), sha3(needleptr, len)) + equal := eq(keccak256(selfptr, length), keccak256(needleptr, length)) } return equal; } @@ -375,7 +380,7 @@ library strings { * @param needle The slice to search for. * @return `self` */ - function beyond(slice self, slice needle) internal returns (slice) { + function beyond(slice memory self, slice memory needle) internal pure returns (slice memory) { if (self._len < needle._len) { return self; } @@ -383,10 +388,10 @@ library strings { bool equal = true; if (self._ptr != needle._ptr) { assembly { - let len := mload(needle) + let length := mload(needle) let selfptr := mload(add(self, 0x20)) let needleptr := mload(add(needle, 0x20)) - equal := eq(sha3(selfptr, len), sha3(needleptr, len)) + equal := eq(keccak256(selfptr, length), keccak256(needleptr, length)) } } @@ -404,12 +409,12 @@ library strings { * @param needle The slice to search for. * @return True if the slice starts with the provided text, false otherwise. */ - function endsWith(slice self, slice needle) internal returns (bool) { + function endsWith(slice memory self, slice memory needle) internal pure returns (bool) { if (self._len < needle._len) { return false; } - var selfptr = self._ptr + self._len - needle._len; + uint selfptr = self._ptr + self._len - needle._len; if (selfptr == needle._ptr) { return true; @@ -417,9 +422,9 @@ library strings { bool equal; assembly { - let len := mload(needle) + let length := mload(needle) let needleptr := mload(add(needle, 0x20)) - equal := eq(sha3(selfptr, len), sha3(needleptr, len)) + equal := eq(keccak256(selfptr, length), keccak256(needleptr, length)) } return equal; @@ -432,18 +437,18 @@ library strings { * @param needle The slice to search for. * @return `self` */ - function until(slice self, slice needle) internal returns (slice) { + function until(slice memory self, slice memory needle) internal pure returns (slice memory) { if (self._len < needle._len) { return self; } - var selfptr = self._ptr + self._len - needle._len; + uint selfptr = self._ptr + self._len - needle._len; bool equal = true; if (selfptr != needle._ptr) { assembly { - let len := mload(needle) + let length := mload(needle) let needleptr := mload(add(needle, 0x20)) - equal := eq(sha3(selfptr, len), sha3(needleptr, len)) + equal := eq(keccak256(selfptr, length), keccak256(needleptr, length)) } } @@ -456,34 +461,36 @@ library strings { // Returns the memory address of the first byte of the first occurrence of // `needle` in `self`, or the first byte after `self` if not found. - function findPtr(uint selflen, uint selfptr, uint needlelen, uint needleptr) private returns (uint) { - uint ptr; + function findPtr(uint selflen, uint selfptr, uint needlelen, uint needleptr) private pure returns (uint) { + uint ptr = selfptr; uint idx; if (needlelen <= selflen) { if (needlelen <= 32) { - // Optimized assembly for 68 gas per byte on short strings - assembly { - let mask := not(sub(exp(2, mul(8, sub(32, needlelen))), 1)) - let needledata := and(mload(needleptr), mask) - let end := add(selfptr, sub(selflen, needlelen)) - ptr := selfptr - loop: - jumpi(exit, eq(and(mload(ptr), mask), needledata)) - ptr := add(ptr, 1) - jumpi(loop, lt(sub(ptr, 1), end)) - ptr := add(selfptr, selflen) - exit: + bytes32 mask = bytes32(~(2 ** (8 * (32 - needlelen)) - 1)); + + bytes32 needledata; + assembly { needledata := and(mload(needleptr), mask) } + + uint end = selfptr + selflen - needlelen; + bytes32 ptrdata; + assembly { ptrdata := and(mload(ptr), mask) } + + while (ptrdata != needledata) { + if (ptr >= end) + return selfptr + selflen; + ptr++; + assembly { ptrdata := and(mload(ptr), mask) } } return ptr; } else { // For long needles, use hashing bytes32 hash; - assembly { hash := sha3(needleptr, needlelen) } - ptr = selfptr; + assembly { hash := keccak256(needleptr, needlelen) } + for (idx = 0; idx <= selflen - needlelen; idx++) { bytes32 testHash; - assembly { testHash := sha3(ptr, needlelen) } + assembly { testHash := keccak256(ptr, needlelen) } if (hash == testHash) return ptr; ptr += 1; @@ -495,35 +502,35 @@ library strings { // Returns the memory address of the first byte after the last occurrence of // `needle` in `self`, or the address of `self` if not found. - function rfindPtr(uint selflen, uint selfptr, uint needlelen, uint needleptr) private returns (uint) { + function rfindPtr(uint selflen, uint selfptr, uint needlelen, uint needleptr) private pure returns (uint) { uint ptr; if (needlelen <= selflen) { if (needlelen <= 32) { - // Optimized assembly for 69 gas per byte on short strings - assembly { - let mask := not(sub(exp(2, mul(8, sub(32, needlelen))), 1)) - let needledata := and(mload(needleptr), mask) - ptr := add(selfptr, sub(selflen, needlelen)) - loop: - jumpi(ret, eq(and(mload(ptr), mask), needledata)) - ptr := sub(ptr, 1) - jumpi(loop, gt(add(ptr, 1), selfptr)) - ptr := selfptr - jump(exit) - ret: - ptr := add(ptr, needlelen) - exit: + bytes32 mask = bytes32(~(2 ** (8 * (32 - needlelen)) - 1)); + + bytes32 needledata; + assembly { needledata := and(mload(needleptr), mask) } + + ptr = selfptr + selflen - needlelen; + bytes32 ptrdata; + assembly { ptrdata := and(mload(ptr), mask) } + + while (ptrdata != needledata) { + if (ptr <= selfptr) + return selfptr; + ptr--; + assembly { ptrdata := and(mload(ptr), mask) } } - return ptr; + return ptr + needlelen; } else { // For long needles, use hashing bytes32 hash; - assembly { hash := sha3(needleptr, needlelen) } + assembly { hash := keccak256(needleptr, needlelen) } ptr = selfptr + (selflen - needlelen); while (ptr >= selfptr) { bytes32 testHash; - assembly { testHash := sha3(ptr, needlelen) } + assembly { testHash := keccak256(ptr, needlelen) } if (hash == testHash) return ptr + needlelen; ptr -= 1; @@ -541,7 +548,7 @@ library strings { * @param needle The text to search for. * @return `self`. */ - function find(slice self, slice needle) internal returns (slice) { + function find(slice memory self, slice memory needle) internal pure returns (slice memory) { uint ptr = findPtr(self._len, self._ptr, needle._len, needle._ptr); self._len -= ptr - self._ptr; self._ptr = ptr; @@ -556,7 +563,7 @@ library strings { * @param needle The text to search for. * @return `self`. */ - function rfind(slice self, slice needle) internal returns (slice) { + function rfind(slice memory self, slice memory needle) internal pure returns (slice memory) { uint ptr = rfindPtr(self._len, self._ptr, needle._len, needle._ptr); self._len = ptr - self._ptr; return self; @@ -572,7 +579,7 @@ library strings { * @param token An output parameter to which the first token is written. * @return `token`. */ - function split(slice self, slice needle, slice token) internal returns (slice) { + function split(slice memory self, slice memory needle, slice memory token) internal pure returns (slice memory) { uint ptr = findPtr(self._len, self._ptr, needle._len, needle._ptr); token._ptr = self._ptr; token._len = ptr - self._ptr; @@ -595,7 +602,7 @@ library strings { * @param needle The text to search for in `self`. * @return The part of `self` up to the first occurrence of `delim`. */ - function split(slice self, slice needle) internal returns (slice token) { + function split(slice memory self, slice memory needle) internal pure returns (slice memory token) { split(self, needle, token); } @@ -609,7 +616,7 @@ library strings { * @param token An output parameter to which the first token is written. * @return `token`. */ - function rsplit(slice self, slice needle, slice token) internal returns (slice) { + function rsplit(slice memory self, slice memory needle, slice memory token) internal pure returns (slice memory) { uint ptr = rfindPtr(self._len, self._ptr, needle._len, needle._ptr); token._ptr = ptr; token._len = self._len - (ptr - self._ptr); @@ -631,7 +638,7 @@ library strings { * @param needle The text to search for in `self`. * @return The part of `self` after the last occurrence of `delim`. */ - function rsplit(slice self, slice needle) internal returns (slice token) { + function rsplit(slice memory self, slice memory needle) internal pure returns (slice memory token) { rsplit(self, needle, token); } @@ -641,10 +648,10 @@ library strings { * @param needle The text to search for in `self`. * @return The number of occurrences of `needle` found in `self`. */ - function count(slice self, slice needle) internal returns (uint count) { + function count(slice memory self, slice memory needle) internal pure returns (uint cnt) { uint ptr = findPtr(self._len, self._ptr, needle._len, needle._ptr) + needle._len; while (ptr <= self._ptr + self._len) { - count++; + cnt++; ptr = findPtr(self._len - (ptr - self._ptr), ptr, needle._len, needle._ptr) + needle._len; } } @@ -655,7 +662,7 @@ library strings { * @param needle The text to search for in `self`. * @return True if `needle` is found in `self`, false otherwise. */ - function contains(slice self, slice needle) internal returns (bool) { + function contains(slice memory self, slice memory needle) internal pure returns (bool) { return rfindPtr(self._len, self._ptr, needle._len, needle._ptr) != self._ptr; } @@ -666,8 +673,8 @@ library strings { * @param other The second slice to concatenate. * @return The concatenation of the two strings. */ - function concat(slice self, slice other) internal returns (string) { - var ret = new string(self._len + other._len); + function concat(slice memory self, slice memory other) internal pure returns (string memory) { + string memory ret = new string(self._len + other._len); uint retptr; assembly { retptr := add(ret, 32) } memcpy(retptr, self._ptr, self._len); @@ -683,19 +690,19 @@ library strings { * @return A newly allocated string containing all the slices in `parts`, * joined with `self`. */ - function join(slice self, slice[] parts) internal returns (string) { + function join(slice memory self, slice[] memory parts) internal pure returns (string memory) { if (parts.length == 0) return ""; - uint len = self._len * (parts.length - 1); + uint length = self._len * (parts.length - 1); for(uint i = 0; i < parts.length; i++) - len += parts[i]._len; + length += parts[i]._len; - var ret = new string(len); + string memory ret = new string(length); uint retptr; assembly { retptr := add(ret, 32) } - for(i = 0; i < parts.length; i++) { + for(uint i = 0; i < parts.length; i++) { memcpy(retptr, parts[i]._ptr, parts[i]._len); retptr += parts[i]._len; if (i < parts.length - 1) { diff --git a/test/compilationTests/zeppelin/Bounty.sol b/test/compilationTests/zeppelin/Bounty.sol deleted file mode 100644 index 4425b7a5..00000000 --- a/test/compilationTests/zeppelin/Bounty.sol +++ /dev/null @@ -1,78 +0,0 @@ -pragma solidity ^0.4.11; - - -import './payment/PullPayment.sol'; -import './lifecycle/Destructible.sol'; - - -/** - * @title Bounty - * @dev This bounty will pay out to a researcher if they break invariant logic of the contract. - */ -contract Bounty is PullPayment, Destructible { - bool public claimed; - mapping(address => address) public researchers; - - event TargetCreated(address createdAddress); - - /** - * @dev Fallback function allowing the contract to recieve funds, if they haven't already been claimed. - */ - function() payable { - if (claimed) { - throw; - } - } - - /** - * @dev Create and deploy the target contract (extension of Target contract), and sets the - * msg.sender as a researcher - * @return A target contract - */ - function createTarget() returns(Target) { - Target target = Target(deployContract()); - researchers[target] = msg.sender; - TargetCreated(target); - return target; - } - - /** - * @dev Internal function to deploy the target contract. - * @return A target contract address - */ - function deployContract() internal returns(address); - - /** - * @dev Sends the contract funds to the researcher that proved the contract is broken. - * @param target contract - */ - function claim(Target target) { - address researcher = researchers[target]; - if (researcher == 0) { - throw; - } - // Check Target contract invariants - if (target.checkInvariant()) { - throw; - } - asyncSend(researcher, this.balance); - claimed = true; - } - -} - - -/** - * @title Target - * @dev Your main contract should inherit from this class and implement the checkInvariant method. - */ -contract Target { - - /** - * @dev Checks all values a contract assumes to be true all the time. If this function returns - * false, the contract is broken in some way and is in an inconsistent state. - * In order to win the bounty, security researchers will try to cause this broken state. - * @return True if all invariant values are correct, false otherwise. - */ - function checkInvariant() returns(bool); -} diff --git a/test/compilationTests/zeppelin/DayLimit.sol b/test/compilationTests/zeppelin/DayLimit.sol deleted file mode 100644 index 3c8d5b0c..00000000 --- a/test/compilationTests/zeppelin/DayLimit.sol +++ /dev/null @@ -1,75 +0,0 @@ -pragma solidity ^0.4.11; - -/** - * @title DayLimit - * @dev Base contract that enables methods to be protected by placing a linear limit (specifiable) - * on a particular resource per calendar day. Is multiowned to allow the limit to be altered. - */ -contract DayLimit { - - uint256 public dailyLimit; - uint256 public spentToday; - uint256 public lastDay; - - /** - * @dev Constructor that sets the passed value as a dailyLimit. - * @param _limit uint256 to represent the daily limit. - */ - function DayLimit(uint256 _limit) { - dailyLimit = _limit; - lastDay = today(); - } - - /** - * @dev sets the daily limit. Does not alter the amount already spent today. - * @param _newLimit uint256 to represent the new limit. - */ - function _setDailyLimit(uint256 _newLimit) internal { - dailyLimit = _newLimit; - } - - /** - * @dev Resets the amount already spent today. - */ - function _resetSpentToday() internal { - spentToday = 0; - } - - /** - * @dev Checks to see if there is enough resource to spend today. If true, the resource may be expended. - * @param _value uint256 representing the amount of resource to spend. - * @return A boolean that is True if the resource was spended and false otherwise. - */ - function underLimit(uint256 _value) internal returns (bool) { - // reset the spend limit if we're on a different day to last time. - if (today() > lastDay) { - spentToday = 0; - lastDay = today(); - } - // check to see if there's enough left - if so, subtract and return true. - // overflow protection // dailyLimit check - if (spentToday + _value >= spentToday && spentToday + _value <= dailyLimit) { - spentToday += _value; - return true; - } - return false; - } - - /** - * @dev Private function to determine today's index - * @return uint256 of today's index. - */ - function today() private constant returns (uint256) { - return now / 1 days; - } - - /** - * @dev Simple modifier for daily limit. - */ - modifier limitedDaily(uint256 _value) { - if (!underLimit(_value)) { - throw; - } - _; - } -} diff --git a/test/compilationTests/zeppelin/LICENSE b/test/compilationTests/zeppelin/LICENSE deleted file mode 100644 index 85f53321..00000000 --- a/test/compilationTests/zeppelin/LICENSE +++ /dev/null @@ -1,22 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2016 Smart Contract Solutions, Inc. - -Permission is hereby granted, free of charge, to any person obtaining -a copy of this software and associated documentation files (the -"Software"), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to -the following conditions: - -The above copyright notice and this permission notice shall be included -in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, -TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE -SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/test/compilationTests/zeppelin/LimitBalance.sol b/test/compilationTests/zeppelin/LimitBalance.sol deleted file mode 100644 index 57477c74..00000000 --- a/test/compilationTests/zeppelin/LimitBalance.sol +++ /dev/null @@ -1,33 +0,0 @@ -pragma solidity ^0.4.11; - - -/** - * @title LimitBalance - * @dev Simple contract to limit the balance of child contract. - * @dev Note this doesn't prevent other contracts to send funds by using selfdestruct(address); - * @dev See: https://github.com/ConsenSys/smart-contract-best-practices#remember-that-ether-can-be-forcibly-sent-to-an-account - */ -contract LimitBalance { - - uint256 public limit; - - /** - * @dev Constructor that sets the passed value as a limit. - * @param _limit uint256 to represent the limit. - */ - function LimitBalance(uint256 _limit) { - limit = _limit; - } - - /** - * @dev Checks if limit was reached. Case true, it throws. - */ - modifier limitedPayable() { - if (this.balance > limit) { - throw; - } - _; - - } - -} diff --git a/test/compilationTests/zeppelin/MultisigWallet.sol b/test/compilationTests/zeppelin/MultisigWallet.sol deleted file mode 100644 index 939e70f2..00000000 --- a/test/compilationTests/zeppelin/MultisigWallet.sol +++ /dev/null @@ -1,127 +0,0 @@ -pragma solidity ^0.4.11; - - -import "./ownership/Multisig.sol"; -import "./ownership/Shareable.sol"; -import "./DayLimit.sol"; - - -/** - * MultisigWallet - * Usage: - * bytes32 h = Wallet(w).from(oneOwner).execute(to, value, data); - * Wallet(w).from(anotherOwner).confirm(h); - */ -contract MultisigWallet is Multisig, Shareable, DayLimit { - - struct Transaction { - address to; - uint256 value; - bytes data; - } - - /** - * Constructor, sets the owners addresses, number of approvals required, and daily spending limit - * @param _owners A list of owners. - * @param _required The amount required for a transaction to be approved. - */ - function MultisigWallet(address[] _owners, uint256 _required, uint256 _daylimit) - Shareable(_owners, _required) - DayLimit(_daylimit) { } - - /** - * @dev destroys the contract sending everything to `_to`. - */ - function destroy(address _to) onlymanyowners(keccak256(msg.data)) external { - selfdestruct(_to); - } - - /** - * @dev Fallback function, receives value and emits a deposit event. - */ - function() payable { - // just being sent some cash? - if (msg.value > 0) - Deposit(msg.sender, msg.value); - } - - /** - * @dev Outside-visible transaction entry point. Executes transaction immediately if below daily - * spending limit. If not, goes into multisig process. We provide a hash on return to allow the - * sender to provide shortcuts for the other confirmations (allowing them to avoid replicating - * the _to, _value, and _data arguments). They still get the option of using them if they want, - * anyways. - * @param _to The receiver address - * @param _value The value to send - * @param _data The data part of the transaction - */ - function execute(address _to, uint256 _value, bytes _data) external onlyOwner returns (bytes32 _r) { - // first, take the opportunity to check that we're under the daily limit. - if (underLimit(_value)) { - SingleTransact(msg.sender, _value, _to, _data); - // yes - just execute the call. - if (!_to.call.value(_value)(_data)) { - throw; - } - return 0; - } - // determine our operation hash. - _r = keccak256(msg.data, block.number); - if (!confirm(_r) && txs[_r].to == 0) { - txs[_r].to = _to; - txs[_r].value = _value; - txs[_r].data = _data; - ConfirmationNeeded(_r, msg.sender, _value, _to, _data); - } - } - - /** - * @dev Confirm a transaction by providing just the hash. We use the previous transactions map, - * txs, in order to determine the body of the transaction from the hash provided. - * @param _h The transaction hash to approve. - */ - function confirm(bytes32 _h) onlymanyowners(_h) returns (bool) { - if (txs[_h].to != 0) { - if (!txs[_h].to.call.value(txs[_h].value)(txs[_h].data)) { - throw; - } - MultiTransact(msg.sender, _h, txs[_h].value, txs[_h].to, txs[_h].data); - delete txs[_h]; - return true; - } - } - - /** - * @dev Updates the daily limit value. - * @param _newLimit uint256 to represent the new limit. - */ - function setDailyLimit(uint256 _newLimit) onlymanyowners(keccak256(msg.data)) external { - _setDailyLimit(_newLimit); - } - - /** - * @dev Resets the value spent to enable more spending - */ - function resetSpentToday() onlymanyowners(keccak256(msg.data)) external { - _resetSpentToday(); - } - - - // INTERNAL METHODS - /** - * @dev Clears the list of transactions pending approval. - */ - function clearPending() internal { - uint256 length = pendingsIndex.length; - for (uint256 i = 0; i < length; ++i) { - delete txs[pendingsIndex[i]]; - } - super.clearPending(); - } - - - // FIELDS - - // pending transactions we have at present. - mapping (bytes32 => Transaction) txs; -} diff --git a/test/compilationTests/zeppelin/README.md b/test/compilationTests/zeppelin/README.md deleted file mode 100644 index dee2f5ca..00000000 --- a/test/compilationTests/zeppelin/README.md +++ /dev/null @@ -1,3 +0,0 @@ -Zeppelin contracts, originally from - -https://github.com/OpenZeppelin/zeppelin-solidity diff --git a/test/compilationTests/zeppelin/ReentrancyGuard.sol b/test/compilationTests/zeppelin/ReentrancyGuard.sol deleted file mode 100644 index ca8b643b..00000000 --- a/test/compilationTests/zeppelin/ReentrancyGuard.sol +++ /dev/null @@ -1,34 +0,0 @@ -pragma solidity ^0.4.11; - -/** - * @title Helps contracts guard agains rentrancy attacks. - * @author Remco Bloemen <remco@2π.com> - * @notice If you mark a function `nonReentrant`, you should also - * mark it `external`. - */ -contract ReentrancyGuard { - - /** - * @dev We use a single lock for the whole contract. - */ - bool private rentrancy_lock = false; - - /** - * @dev Prevents a contract from calling itself, directly or indirectly. - * @notice If you mark a function `nonReentrant`, you should also - * mark it `external`. Calling one nonReentrant function from - * another is not supported. Instead, you can implement a - * `private` function doing the actual work, and a `external` - * wrapper marked as `nonReentrant`. - */ - modifier nonReentrant() { - if(rentrancy_lock == false) { - rentrancy_lock = true; - _; - rentrancy_lock = false; - } else { - throw; - } - } - -} diff --git a/test/compilationTests/zeppelin/crowdsale/CappedCrowdsale.sol b/test/compilationTests/zeppelin/crowdsale/CappedCrowdsale.sol deleted file mode 100644 index f04649f3..00000000 --- a/test/compilationTests/zeppelin/crowdsale/CappedCrowdsale.sol +++ /dev/null @@ -1,33 +0,0 @@ -pragma solidity ^0.4.11; - -import '../math/SafeMath.sol'; -import './Crowdsale.sol'; - -/** - * @title CappedCrowdsale - * @dev Extension of Crowsdale with a max amount of funds raised - */ -contract CappedCrowdsale is Crowdsale { - using SafeMath for uint256; - - uint256 public cap; - - function CappedCrowdsale(uint256 _cap) { - cap = _cap; - } - - // overriding Crowdsale#validPurchase to add extra cap logic - // @return true if investors can buy at the moment - function validPurchase() internal constant returns (bool) { - bool withinCap = weiRaised.add(msg.value) <= cap; - return super.validPurchase() && withinCap; - } - - // overriding Crowdsale#hasEnded to add cap logic - // @return true if crowdsale event has ended - function hasEnded() public constant returns (bool) { - bool capReached = weiRaised >= cap; - return super.hasEnded() || capReached; - } - -} diff --git a/test/compilationTests/zeppelin/crowdsale/Crowdsale.sol b/test/compilationTests/zeppelin/crowdsale/Crowdsale.sol deleted file mode 100644 index bee1efd2..00000000 --- a/test/compilationTests/zeppelin/crowdsale/Crowdsale.sol +++ /dev/null @@ -1,108 +0,0 @@ -pragma solidity ^0.4.11; - -import '../token/MintableToken.sol'; -import '../math/SafeMath.sol'; - -/** - * @title Crowdsale - * @dev Crowdsale is a base contract for managing a token crowdsale. - * Crowdsales have a start and end block, where investors can make - * token purchases and the crowdsale will assign them tokens based - * on a token per ETH rate. Funds collected are forwarded to a wallet - * as they arrive. - */ -contract Crowdsale { - using SafeMath for uint256; - - // The token being sold - MintableToken public token; - - // start and end block where investments are allowed (both inclusive) - uint256 public startBlock; - uint256 public endBlock; - - // address where funds are collected - address public wallet; - - // how many token units a buyer gets per wei - uint256 public rate; - - // amount of raised money in wei - uint256 public weiRaised; - - /** - * event for token purchase logging - * @param purchaser who paid for the tokens - * @param beneficiary who got the tokens - * @param value weis paid for purchase - * @param amount amount of tokens purchased - */ - event TokenPurchase(address indexed purchaser, address indexed beneficiary, uint256 value, uint256 amount); - - - function Crowdsale(uint256 _startBlock, uint256 _endBlock, uint256 _rate, address _wallet) { - require(_startBlock >= block.number); - require(_endBlock >= _startBlock); - require(_rate > 0); - require(_wallet != 0x0); - - token = createTokenContract(); - startBlock = _startBlock; - endBlock = _endBlock; - rate = _rate; - wallet = _wallet; - } - - // creates the token to be sold. - // override this method to have crowdsale of a specific mintable token. - function createTokenContract() internal returns (MintableToken) { - return new MintableToken(); - } - - - // fallback function can be used to buy tokens - function () payable { - buyTokens(msg.sender); - } - - // low level token purchase function - function buyTokens(address beneficiary) payable { - require(beneficiary != 0x0); - require(validPurchase()); - - uint256 weiAmount = msg.value; - uint256 updatedWeiRaised = weiRaised.add(weiAmount); - - // calculate token amount to be created - uint256 tokens = weiAmount.mul(rate); - - // update state - weiRaised = updatedWeiRaised; - - token.mint(beneficiary, tokens); - TokenPurchase(msg.sender, beneficiary, weiAmount, tokens); - - forwardFunds(); - } - - // send ether to the fund collection wallet - // override to create custom fund forwarding mechanisms - function forwardFunds() internal { - wallet.transfer(msg.value); - } - - // @return true if the transaction can buy tokens - function validPurchase() internal constant returns (bool) { - uint256 current = block.number; - bool withinPeriod = current >= startBlock && current <= endBlock; - bool nonZeroPurchase = msg.value != 0; - return withinPeriod && nonZeroPurchase; - } - - // @return true if crowdsale event has ended - function hasEnded() public constant returns (bool) { - return block.number > endBlock; - } - - -} diff --git a/test/compilationTests/zeppelin/crowdsale/FinalizableCrowdsale.sol b/test/compilationTests/zeppelin/crowdsale/FinalizableCrowdsale.sol deleted file mode 100644 index 1a736083..00000000 --- a/test/compilationTests/zeppelin/crowdsale/FinalizableCrowdsale.sol +++ /dev/null @@ -1,39 +0,0 @@ -pragma solidity ^0.4.11; - -import '../math/SafeMath.sol'; -import '../ownership/Ownable.sol'; -import './Crowdsale.sol'; - -/** - * @title FinalizableCrowdsale - * @dev Extension of Crowsdale where an owner can do extra work - * after finishing. By default, it will end token minting. - */ -contract FinalizableCrowdsale is Crowdsale, Ownable { - using SafeMath for uint256; - - bool public isFinalized = false; - - event Finalized(); - - // should be called after crowdsale ends, to do - // some extra finalization work - function finalize() onlyOwner { - require(!isFinalized); - require(hasEnded()); - - finalization(); - Finalized(); - - isFinalized = true; - } - - // end token minting on finalization - // override this with custom logic if needed - function finalization() internal { - token.finishMinting(); - } - - - -} diff --git a/test/compilationTests/zeppelin/crowdsale/RefundVault.sol b/test/compilationTests/zeppelin/crowdsale/RefundVault.sol deleted file mode 100644 index cc92ff9f..00000000 --- a/test/compilationTests/zeppelin/crowdsale/RefundVault.sol +++ /dev/null @@ -1,56 +0,0 @@ -pragma solidity ^0.4.11; - -import '../math/SafeMath.sol'; -import '../ownership/Ownable.sol'; - -/** - * @title RefundVault - * @dev This contract is used for storing funds while a crowdsale - * is in progress. Supports refunding the money if crowdsale fails, - * and forwarding it if crowdsale is successful. - */ -contract RefundVault is Ownable { - using SafeMath for uint256; - - enum State { Active, Refunding, Closed } - - mapping (address => uint256) public deposited; - address public wallet; - State public state; - - event Closed(); - event RefundsEnabled(); - event Refunded(address indexed beneficiary, uint256 weiAmount); - - function RefundVault(address _wallet) { - require(_wallet != 0x0); - wallet = _wallet; - state = State.Active; - } - - function deposit(address investor) onlyOwner payable { - require(state == State.Active); - deposited[investor] = deposited[investor].add(msg.value); - } - - function close() onlyOwner { - require(state == State.Active); - state = State.Closed; - Closed(); - wallet.transfer(this.balance); - } - - function enableRefunds() onlyOwner { - require(state == State.Active); - state = State.Refunding; - RefundsEnabled(); - } - - function refund(address investor) { - require(state == State.Refunding); - uint256 depositedValue = deposited[investor]; - deposited[investor] = 0; - investor.transfer(depositedValue); - Refunded(investor, depositedValue); - } -} diff --git a/test/compilationTests/zeppelin/crowdsale/RefundableCrowdsale.sol b/test/compilationTests/zeppelin/crowdsale/RefundableCrowdsale.sol deleted file mode 100644 index f45df1d3..00000000 --- a/test/compilationTests/zeppelin/crowdsale/RefundableCrowdsale.sol +++ /dev/null @@ -1,59 +0,0 @@ -pragma solidity ^0.4.11; - - -import '../math/SafeMath.sol'; -import './FinalizableCrowdsale.sol'; -import './RefundVault.sol'; - - -/** - * @title RefundableCrowdsale - * @dev Extension of Crowdsale contract that adds a funding goal, and - * the possibility of users getting a refund if goal is not met. - * Uses a RefundVault as the crowdsale's vault. - */ -contract RefundableCrowdsale is FinalizableCrowdsale { - using SafeMath for uint256; - - // minimum amount of funds to be raised in weis - uint256 public goal; - - // refund vault used to hold funds while crowdsale is running - RefundVault public vault; - - function RefundableCrowdsale(uint256 _goal) { - vault = new RefundVault(wallet); - goal = _goal; - } - - // We're overriding the fund forwarding from Crowdsale. - // In addition to sending the funds, we want to call - // the RefundVault deposit function - function forwardFunds() internal { - vault.deposit.value(msg.value)(msg.sender); - } - - // if crowdsale is unsuccessful, investors can claim refunds here - function claimRefund() { - require(isFinalized); - require(!goalReached()); - - vault.refund(msg.sender); - } - - // vault finalization task, called when owner calls finalize() - function finalization() internal { - if (goalReached()) { - vault.close(); - } else { - vault.enableRefunds(); - } - - super.finalization(); - } - - function goalReached() public constant returns (bool) { - return weiRaised >= goal; - } - -} diff --git a/test/compilationTests/zeppelin/lifecycle/Destructible.sol b/test/compilationTests/zeppelin/lifecycle/Destructible.sol deleted file mode 100644 index 3561e3b7..00000000 --- a/test/compilationTests/zeppelin/lifecycle/Destructible.sol +++ /dev/null @@ -1,25 +0,0 @@ -pragma solidity ^0.4.11; - - -import "../ownership/Ownable.sol"; - - -/** - * @title Destructible - * @dev Base contract that can be destroyed by owner. All funds in contract will be sent to the owner. - */ -contract Destructible is Ownable { - - function Destructible() payable { } - - /** - * @dev Transfers the current balance to the owner and terminates the contract. - */ - function destroy() onlyOwner { - selfdestruct(owner); - } - - function destroyAndSend(address _recipient) onlyOwner { - selfdestruct(_recipient); - } -} diff --git a/test/compilationTests/zeppelin/lifecycle/Migrations.sol b/test/compilationTests/zeppelin/lifecycle/Migrations.sol deleted file mode 100644 index d5b05308..00000000 --- a/test/compilationTests/zeppelin/lifecycle/Migrations.sol +++ /dev/null @@ -1,21 +0,0 @@ -pragma solidity ^0.4.11; - - -import '../ownership/Ownable.sol'; - -/** - * @title Migrations - * @dev This is a truffle contract, needed for truffle integration, not meant for use by Zeppelin users. - */ -contract Migrations is Ownable { - uint256 public lastCompletedMigration; - - function setCompleted(uint256 completed) onlyOwner { - lastCompletedMigration = completed; - } - - function upgrade(address newAddress) onlyOwner { - Migrations upgraded = Migrations(newAddress); - upgraded.setCompleted(lastCompletedMigration); - } -} diff --git a/test/compilationTests/zeppelin/lifecycle/Pausable.sol b/test/compilationTests/zeppelin/lifecycle/Pausable.sol deleted file mode 100644 index b14f8767..00000000 --- a/test/compilationTests/zeppelin/lifecycle/Pausable.sol +++ /dev/null @@ -1,51 +0,0 @@ -pragma solidity ^0.4.11; - - -import "../ownership/Ownable.sol"; - - -/** - * @title Pausable - * @dev Base contract which allows children to implement an emergency stop mechanism. - */ -contract Pausable is Ownable { - event Pause(); - event Unpause(); - - bool public paused = false; - - - /** - * @dev modifier to allow actions only when the contract IS paused - */ - modifier whenNotPaused() { - if (paused) throw; - _; - } - - /** - * @dev modifier to allow actions only when the contract IS NOT paused - */ - modifier whenPaused { - if (!paused) throw; - _; - } - - /** - * @dev called by the owner to pause, triggers stopped state - */ - function pause() onlyOwner whenNotPaused returns (bool) { - paused = true; - Pause(); - return true; - } - - /** - * @dev called by the owner to unpause, returns to normal state - */ - function unpause() onlyOwner whenPaused returns (bool) { - paused = false; - Unpause(); - return true; - } -} diff --git a/test/compilationTests/zeppelin/lifecycle/TokenDestructible.sol b/test/compilationTests/zeppelin/lifecycle/TokenDestructible.sol deleted file mode 100644 index fe0b46b6..00000000 --- a/test/compilationTests/zeppelin/lifecycle/TokenDestructible.sol +++ /dev/null @@ -1,36 +0,0 @@ -pragma solidity ^0.4.11; - - -import "../ownership/Ownable.sol"; -import "../token/ERC20Basic.sol"; - -/** - * @title TokenDestructible: - * @author Remco Bloemen <remco@2π.com> - * @dev Base contract that can be destroyed by owner. All funds in contract including - * listed tokens will be sent to the owner. - */ -contract TokenDestructible is Ownable { - - function TokenDestructible() payable { } - - /** - * @notice Terminate contract and refund to owner - * @param tokens List of addresses of ERC20 or ERC20Basic token contracts to - refund. - * @notice The called token contracts could try to re-enter this contract. Only - supply token contracts you trust. - */ - function destroy(address[] tokens) onlyOwner { - - // Transfer tokens to owner - for(uint256 i = 0; i < tokens.length; i++) { - ERC20Basic token = ERC20Basic(tokens[i]); - uint256 balance = token.balanceOf(this); - token.transfer(owner, balance); - } - - // Transfer Eth to owner and terminate contract - selfdestruct(owner); - } -} diff --git a/test/compilationTests/zeppelin/math/Math.sol b/test/compilationTests/zeppelin/math/Math.sol deleted file mode 100644 index 3d016c0a..00000000 --- a/test/compilationTests/zeppelin/math/Math.sol +++ /dev/null @@ -1,24 +0,0 @@ -pragma solidity ^0.4.11; - -/** - * @title Math - * @dev Assorted math operations - */ - -library Math { - function max64(uint64 a, uint64 b) internal constant returns (uint64) { - return a >= b ? a : b; - } - - function min64(uint64 a, uint64 b) internal constant returns (uint64) { - return a < b ? a : b; - } - - function max256(uint256 a, uint256 b) internal constant returns (uint256) { - return a >= b ? a : b; - } - - function min256(uint256 a, uint256 b) internal constant returns (uint256) { - return a < b ? a : b; - } -} diff --git a/test/compilationTests/zeppelin/math/SafeMath.sol b/test/compilationTests/zeppelin/math/SafeMath.sol deleted file mode 100644 index dc05ba28..00000000 --- a/test/compilationTests/zeppelin/math/SafeMath.sol +++ /dev/null @@ -1,32 +0,0 @@ -pragma solidity ^0.4.11; - - -/** - * @title SafeMath - * @dev Math operations with safety checks that throw on error - */ -library SafeMath { - function mul(uint256 a, uint256 b) internal returns (uint256) { - uint256 c = a * b; - assert(a == 0 || c / a == b); - return c; - } - - function div(uint256 a, uint256 b) internal returns (uint256) { - // assert(b > 0); // Solidity automatically throws when dividing by 0 - uint256 c = a / b; - // assert(a == b * c + a % b); // There is no case in which this doesn't hold - return c; - } - - function sub(uint256 a, uint256 b) internal returns (uint256) { - assert(b <= a); - return a - b; - } - - function add(uint256 a, uint256 b) internal returns (uint256) { - uint256 c = a + b; - assert(c >= a); - return c; - } -} diff --git a/test/compilationTests/zeppelin/ownership/Claimable.sol b/test/compilationTests/zeppelin/ownership/Claimable.sol deleted file mode 100644 index d063502d..00000000 --- a/test/compilationTests/zeppelin/ownership/Claimable.sol +++ /dev/null @@ -1,40 +0,0 @@ -pragma solidity ^0.4.11; - - -import './Ownable.sol'; - - -/** - * @title Claimable - * @dev Extension for the Ownable contract, where the ownership needs to be claimed. - * This allows the new owner to accept the transfer. - */ -contract Claimable is Ownable { - address public pendingOwner; - - /** - * @dev Modifier throws if called by any account other than the pendingOwner. - */ - modifier onlyPendingOwner() { - if (msg.sender != pendingOwner) { - throw; - } - _; - } - - /** - * @dev Allows the current owner to set the pendingOwner address. - * @param newOwner The address to transfer ownership to. - */ - function transferOwnership(address newOwner) onlyOwner { - pendingOwner = newOwner; - } - - /** - * @dev Allows the pendingOwner address to finalize the transfer. - */ - function claimOwnership() onlyPendingOwner { - owner = pendingOwner; - pendingOwner = 0x0; - } -} diff --git a/test/compilationTests/zeppelin/ownership/Contactable.sol b/test/compilationTests/zeppelin/ownership/Contactable.sol deleted file mode 100644 index 0db3ee07..00000000 --- a/test/compilationTests/zeppelin/ownership/Contactable.sol +++ /dev/null @@ -1,21 +0,0 @@ -pragma solidity ^0.4.11; - -import './Ownable.sol'; - -/** - * @title Contactable token - * @dev Basic version of a contactable contract, allowing the owner to provide a string with their - * contact information. - */ -contract Contactable is Ownable{ - - string public contactInformation; - - /** - * @dev Allows the owner to set a string with their contact information. - * @param info The contact information to attach to the contract. - */ - function setContactInformation(string info) onlyOwner{ - contactInformation = info; - } -} diff --git a/test/compilationTests/zeppelin/ownership/DelayedClaimable.sol b/test/compilationTests/zeppelin/ownership/DelayedClaimable.sol deleted file mode 100644 index f5fee614..00000000 --- a/test/compilationTests/zeppelin/ownership/DelayedClaimable.sol +++ /dev/null @@ -1,43 +0,0 @@ -pragma solidity ^0.4.11; - - -import './Claimable.sol'; - - -/** - * @title DelayedClaimable - * @dev Extension for the Claimable contract, where the ownership needs to be claimed before/after - * a certain block number. - */ -contract DelayedClaimable is Claimable { - - uint256 public end; - uint256 public start; - - /** - * @dev Used to specify the time period during which a pending - * owner can claim ownership. - * @param _start The earliest time ownership can be claimed. - * @param _end The latest time ownership can be claimed. - */ - function setLimits(uint256 _start, uint256 _end) onlyOwner { - if (_start > _end) - throw; - end = _end; - start = _start; - } - - - /** - * @dev Allows the pendingOwner address to finalize the transfer, as long as it is called within - * the specified start and end time. - */ - function claimOwnership() onlyPendingOwner { - if ((block.number > end) || (block.number < start)) - throw; - owner = pendingOwner; - pendingOwner = 0x0; - end = 0; - } - -} diff --git a/test/compilationTests/zeppelin/ownership/HasNoContracts.sol b/test/compilationTests/zeppelin/ownership/HasNoContracts.sol deleted file mode 100644 index b5bd649d..00000000 --- a/test/compilationTests/zeppelin/ownership/HasNoContracts.sol +++ /dev/null @@ -1,21 +0,0 @@ -pragma solidity ^0.4.11; - -import "./Ownable.sol"; - -/** - * @title Contracts that should not own Contracts - * @author Remco Bloemen <remco@2π.com> - * @dev Should contracts (anything Ownable) end up being owned by this contract, it allows the owner - * of this contract to reclaim ownership of the contracts. - */ -contract HasNoContracts is Ownable { - - /** - * @dev Reclaim ownership of Ownable contracts - * @param contractAddr The address of the Ownable to be reclaimed. - */ - function reclaimContract(address contractAddr) external onlyOwner { - Ownable contractInst = Ownable(contractAddr); - contractInst.transferOwnership(owner); - } -} diff --git a/test/compilationTests/zeppelin/ownership/HasNoEther.sol b/test/compilationTests/zeppelin/ownership/HasNoEther.sol deleted file mode 100644 index 2bcaf1b8..00000000 --- a/test/compilationTests/zeppelin/ownership/HasNoEther.sol +++ /dev/null @@ -1,44 +0,0 @@ -pragma solidity ^0.4.11; - -import "./Ownable.sol"; - -/** - * @title Contracts that should not own Ether - * @author Remco Bloemen <remco@2π.com> - * @dev This tries to block incoming ether to prevent accidental loss of Ether. Should Ether end up - * in the contract, it will allow the owner to reclaim this ether. - * @notice Ether can still be send to this contract by: - * calling functions labeled `payable` - * `selfdestruct(contract_address)` - * mining directly to the contract address -*/ -contract HasNoEther is Ownable { - - /** - * @dev Constructor that rejects incoming Ether - * @dev The `payable` flag is added so we can access `msg.value` without compiler warning. If we - * leave out payable, then Solidity will allow inheriting contracts to implement a payable - * constructor. By doing it this way we prevent a payable constructor from working. Alternatively - * we could use assembly to access msg.value. - */ - function HasNoEther() payable { - if(msg.value > 0) { - throw; - } - } - - /** - * @dev Disallows direct send by settings a default function without the `payable` flag. - */ - function() external { - } - - /** - * @dev Transfer all Ether held by the contract to the owner. - */ - function reclaimEther() external onlyOwner { - if(!owner.send(this.balance)) { - throw; - } - } -} diff --git a/test/compilationTests/zeppelin/ownership/HasNoTokens.sol b/test/compilationTests/zeppelin/ownership/HasNoTokens.sol deleted file mode 100644 index d1dc4b3e..00000000 --- a/test/compilationTests/zeppelin/ownership/HasNoTokens.sol +++ /dev/null @@ -1,34 +0,0 @@ -pragma solidity ^0.4.11; - -import "./Ownable.sol"; -import "../token/ERC20Basic.sol"; - -/** - * @title Contracts that should not own Tokens - * @author Remco Bloemen <remco@2π.com> - * @dev This blocks incoming ERC23 tokens to prevent accidental loss of tokens. - * Should tokens (any ERC20Basic compatible) end up in the contract, it allows the - * owner to reclaim the tokens. - */ -contract HasNoTokens is Ownable { - - /** - * @dev Reject all ERC23 compatible tokens - * @param from_ address The address that is transferring the tokens - * @param value_ uint256 the amount of the specified token - * @param data_ Bytes The data passed from the caller. - */ - function tokenFallback(address from_, uint256 value_, bytes data_) external { - throw; - } - - /** - * @dev Reclaim all ERC20Basic compatible tokens - * @param tokenAddr address The address of the token contract - */ - function reclaimToken(address tokenAddr) external onlyOwner { - ERC20Basic tokenInst = ERC20Basic(tokenAddr); - uint256 balance = tokenInst.balanceOf(this); - tokenInst.transfer(owner, balance); - } -} diff --git a/test/compilationTests/zeppelin/ownership/Multisig.sol b/test/compilationTests/zeppelin/ownership/Multisig.sol deleted file mode 100644 index 76c78411..00000000 --- a/test/compilationTests/zeppelin/ownership/Multisig.sol +++ /dev/null @@ -1,28 +0,0 @@ -pragma solidity ^0.4.11; - - -/** - * @title Multisig - * @dev Interface contract for multisig proxy contracts; see below for docs. - */ -contract Multisig { - // EVENTS - - // logged events: - // Funds has arrived into the wallet (record how much). - event Deposit(address _from, uint256 value); - // Single transaction going out of the wallet (record who signed for it, how much, and to whom it's going). - event SingleTransact(address owner, uint256 value, address to, bytes data); - // Multi-sig transaction going out of the wallet (record who signed for it last, the operation hash, how much, and to whom it's going). - event MultiTransact(address owner, bytes32 operation, uint256 value, address to, bytes data); - // Confirmation still needed for a transaction. - event ConfirmationNeeded(bytes32 operation, address initiator, uint256 value, address to, bytes data); - - - // FUNCTIONS - - // TODO: document - function changeOwner(address _from, address _to) external; - function execute(address _to, uint256 _value, bytes _data) external returns (bytes32); - function confirm(bytes32 _h) returns (bool); -} diff --git a/test/compilationTests/zeppelin/ownership/NoOwner.sol b/test/compilationTests/zeppelin/ownership/NoOwner.sol deleted file mode 100644 index 7215abf3..00000000 --- a/test/compilationTests/zeppelin/ownership/NoOwner.sol +++ /dev/null @@ -1,14 +0,0 @@ -pragma solidity ^0.4.11; - -import "./HasNoEther.sol"; -import "./HasNoTokens.sol"; -import "./HasNoContracts.sol"; - -/** - * @title Base contract for contracts that should not own things. - * @author Remco Bloemen <remco@2π.com> - * @dev Solves a class of errors where a contract accidentally becomes owner of Ether, Tokens or - * Owned contracts. See respective base contracts for details. - */ -contract NoOwner is HasNoEther, HasNoTokens, HasNoContracts { -} diff --git a/test/compilationTests/zeppelin/ownership/Ownable.sol b/test/compilationTests/zeppelin/ownership/Ownable.sol deleted file mode 100644 index f1628454..00000000 --- a/test/compilationTests/zeppelin/ownership/Ownable.sol +++ /dev/null @@ -1,43 +0,0 @@ -pragma solidity ^0.4.11; - - -/** - * @title Ownable - * @dev The Ownable contract has an owner address, and provides basic authorization control - * functions, this simplifies the implementation of "user permissions". - */ -contract Ownable { - address public owner; - - - /** - * @dev The Ownable constructor sets the original `owner` of the contract to the sender - * account. - */ - function Ownable() { - owner = msg.sender; - } - - - /** - * @dev Throws if called by any account other than the owner. - */ - modifier onlyOwner() { - if (msg.sender != owner) { - throw; - } - _; - } - - - /** - * @dev Allows the current owner to transfer control of the contract to a newOwner. - * @param newOwner The address to transfer ownership to. - */ - function transferOwnership(address newOwner) onlyOwner { - if (newOwner != address(0)) { - owner = newOwner; - } - } - -} diff --git a/test/compilationTests/zeppelin/ownership/Shareable.sol b/test/compilationTests/zeppelin/ownership/Shareable.sol deleted file mode 100644 index 9fdaccfd..00000000 --- a/test/compilationTests/zeppelin/ownership/Shareable.sol +++ /dev/null @@ -1,189 +0,0 @@ -pragma solidity ^0.4.11; - - -/** - * @title Shareable - * @dev inheritable "property" contract that enables methods to be protected by requiring the - * acquiescence of either a single, or, crucially, each of a number of, designated owners. - * @dev Usage: use modifiers onlyowner (just own owned) or onlymanyowners(hash), whereby the same hash must be provided by some number (specified in constructor) of the set of owners (specified in the constructor) before the interior is executed. - */ -contract Shareable { - - // struct for the status of a pending operation. - struct PendingState { - uint256 yetNeeded; - uint256 ownersDone; - uint256 index; - } - - // the number of owners that must confirm the same operation before it is run. - uint256 public required; - - // list of owners - address[256] owners; - // index on the list of owners to allow reverse lookup - mapping(address => uint256) ownerIndex; - // the ongoing operations. - mapping(bytes32 => PendingState) pendings; - bytes32[] pendingsIndex; - - - // this contract only has six types of events: it can accept a confirmation, in which case - // we record owner and operation (hash) alongside it. - event Confirmation(address owner, bytes32 operation); - event Revoke(address owner, bytes32 operation); - - - // simple single-sig function modifier. - modifier onlyOwner { - if (!isOwner(msg.sender)) { - throw; - } - _; - } - - /** - * @dev Modifier for multisig functions. - * @param _operation The operation must have an intrinsic hash in order that later attempts can be - * realised as the same underlying operation and thus count as confirmations. - */ - modifier onlymanyowners(bytes32 _operation) { - if (confirmAndCheck(_operation)) { - _; - } - } - - /** - * @dev Constructor is given the number of sigs required to do protected "onlymanyowners" - * transactions as well as the selection of addresses capable of confirming them. - * @param _owners A list of owners. - * @param _required The amount required for a transaction to be approved. - */ - function Shareable(address[] _owners, uint256 _required) { - owners[1] = msg.sender; - ownerIndex[msg.sender] = 1; - for (uint256 i = 0; i < _owners.length; ++i) { - owners[2 + i] = _owners[i]; - ownerIndex[_owners[i]] = 2 + i; - } - required = _required; - if (required > owners.length) { - throw; - } - } - - /** - * @dev Revokes a prior confirmation of the given operation. - * @param _operation A string identifying the operation. - */ - function revoke(bytes32 _operation) external { - uint256 index = ownerIndex[msg.sender]; - // make sure they're an owner - if (index == 0) { - return; - } - uint256 ownerIndexBit = 2**index; - var pending = pendings[_operation]; - if (pending.ownersDone & ownerIndexBit > 0) { - pending.yetNeeded++; - pending.ownersDone -= ownerIndexBit; - Revoke(msg.sender, _operation); - } - } - - /** - * @dev Gets an owner by 0-indexed position (using numOwners as the count) - * @param ownerIndex uint256 The index of the owner - * @return The address of the owner - */ - function getOwner(uint256 ownerIndex) external constant returns (address) { - return address(owners[ownerIndex + 1]); - } - - /** - * @dev Checks if given address is an owner. - * @param _addr address The address which you want to check. - * @return True if the address is an owner and fase otherwise. - */ - function isOwner(address _addr) constant returns (bool) { - return ownerIndex[_addr] > 0; - } - - /** - * @dev Function to check is specific owner has already confirme the operation. - * @param _operation The operation identifier. - * @param _owner The owner address. - * @return True if the owner has confirmed and false otherwise. - */ - function hasConfirmed(bytes32 _operation, address _owner) constant returns (bool) { - var pending = pendings[_operation]; - uint256 index = ownerIndex[_owner]; - - // make sure they're an owner - if (index == 0) { - return false; - } - - // determine the bit to set for this owner. - uint256 ownerIndexBit = 2**index; - return !(pending.ownersDone & ownerIndexBit == 0); - } - - /** - * @dev Confirm and operation and checks if it's already executable. - * @param _operation The operation identifier. - * @return Returns true when operation can be executed. - */ - function confirmAndCheck(bytes32 _operation) internal returns (bool) { - // determine what index the present sender is: - uint256 index = ownerIndex[msg.sender]; - // make sure they're an owner - if (index == 0) { - throw; - } - - var pending = pendings[_operation]; - // if we're not yet working on this operation, switch over and reset the confirmation status. - if (pending.yetNeeded == 0) { - // reset count of confirmations needed. - pending.yetNeeded = required; - // reset which owners have confirmed (none) - set our bitmap to 0. - pending.ownersDone = 0; - pending.index = pendingsIndex.length++; - pendingsIndex[pending.index] = _operation; - } - // determine the bit to set for this owner. - uint256 ownerIndexBit = 2**index; - // make sure we (the message sender) haven't confirmed this operation previously. - if (pending.ownersDone & ownerIndexBit == 0) { - Confirmation(msg.sender, _operation); - // ok - check if count is enough to go ahead. - if (pending.yetNeeded <= 1) { - // enough confirmations: reset and run interior. - delete pendingsIndex[pendings[_operation].index]; - delete pendings[_operation]; - return true; - } else { - // not enough: record that this owner in particular confirmed. - pending.yetNeeded--; - pending.ownersDone |= ownerIndexBit; - } - } - return false; - } - - - /** - * @dev Clear the pending list. - */ - function clearPending() internal { - uint256 length = pendingsIndex.length; - for (uint256 i = 0; i < length; ++i) { - if (pendingsIndex[i] != 0) { - delete pendings[pendingsIndex[i]]; - } - } - delete pendingsIndex; - } - -} diff --git a/test/compilationTests/zeppelin/payment/PullPayment.sol b/test/compilationTests/zeppelin/payment/PullPayment.sol deleted file mode 100644 index ba710b53..00000000 --- a/test/compilationTests/zeppelin/payment/PullPayment.sol +++ /dev/null @@ -1,50 +0,0 @@ -pragma solidity ^0.4.11; - - -import '../math/SafeMath.sol'; - - -/** - * @title PullPayment - * @dev Base contract supporting async send for pull payments. Inherit from this - * contract and use asyncSend instead of send. - */ -contract PullPayment { - using SafeMath for uint256; - - mapping(address => uint256) public payments; - uint256 public totalPayments; - - /** - * @dev Called by the payer to store the sent amount as credit to be pulled. - * @param dest The destination address of the funds. - * @param amount The amount to transfer. - */ - function asyncSend(address dest, uint256 amount) internal { - payments[dest] = payments[dest].add(amount); - totalPayments = totalPayments.add(amount); - } - - /** - * @dev withdraw accumulated balance, called by payee. - */ - function withdrawPayments() { - address payee = msg.sender; - uint256 payment = payments[payee]; - - if (payment == 0) { - throw; - } - - if (this.balance < payment) { - throw; - } - - totalPayments = totalPayments.sub(payment); - payments[payee] = 0; - - if (!payee.send(payment)) { - throw; - } - } -} diff --git a/test/compilationTests/zeppelin/token/BasicToken.sol b/test/compilationTests/zeppelin/token/BasicToken.sol deleted file mode 100644 index 5618227a..00000000 --- a/test/compilationTests/zeppelin/token/BasicToken.sol +++ /dev/null @@ -1,37 +0,0 @@ -pragma solidity ^0.4.11; - - -import './ERC20Basic.sol'; -import '../math/SafeMath.sol'; - - -/** - * @title Basic token - * @dev Basic version of StandardToken, with no allowances. - */ -contract BasicToken is ERC20Basic { - using SafeMath for uint256; - - mapping(address => uint256) balances; - - /** - * @dev transfer token for a specified address - * @param _to The address to transfer to. - * @param _value The amount to be transferred. - */ - function transfer(address _to, uint256 _value) { - balances[msg.sender] = balances[msg.sender].sub(_value); - balances[_to] = balances[_to].add(_value); - Transfer(msg.sender, _to, _value); - } - - /** - * @dev Gets the balance of the specified address. - * @param _owner The address to query the the balance of. - * @return An uint256 representing the amount owned by the passed address. - */ - function balanceOf(address _owner) constant returns (uint256 balance) { - return balances[_owner]; - } - -} diff --git a/test/compilationTests/zeppelin/token/ERC20.sol b/test/compilationTests/zeppelin/token/ERC20.sol deleted file mode 100644 index 1045ac35..00000000 --- a/test/compilationTests/zeppelin/token/ERC20.sol +++ /dev/null @@ -1,16 +0,0 @@ -pragma solidity ^0.4.11; - - -import './ERC20Basic.sol'; - - -/** - * @title ERC20 interface - * @dev see https://github.com/ethereum/EIPs/issues/20 - */ -contract ERC20 is ERC20Basic { - function allowance(address owner, address spender) constant returns (uint256); - function transferFrom(address from, address to, uint256 value); - function approve(address spender, uint256 value); - event Approval(address indexed owner, address indexed spender, uint256 value); -} diff --git a/test/compilationTests/zeppelin/token/ERC20Basic.sol b/test/compilationTests/zeppelin/token/ERC20Basic.sol deleted file mode 100644 index 0e98779e..00000000 --- a/test/compilationTests/zeppelin/token/ERC20Basic.sol +++ /dev/null @@ -1,14 +0,0 @@ -pragma solidity ^0.4.11; - - -/** - * @title ERC20Basic - * @dev Simpler version of ERC20 interface - * @dev see https://github.com/ethereum/EIPs/issues/20 - */ -contract ERC20Basic { - uint256 public totalSupply; - function balanceOf(address who) constant returns (uint256); - function transfer(address to, uint256 value); - event Transfer(address indexed from, address indexed to, uint256 value); -} diff --git a/test/compilationTests/zeppelin/token/LimitedTransferToken.sol b/test/compilationTests/zeppelin/token/LimitedTransferToken.sol deleted file mode 100644 index ee5032c9..00000000 --- a/test/compilationTests/zeppelin/token/LimitedTransferToken.sol +++ /dev/null @@ -1,57 +0,0 @@ -pragma solidity ^0.4.11; - -import "./ERC20.sol"; - -/** - * @title LimitedTransferToken - * @dev LimitedTransferToken defines the generic interface and the implementation to limit token - * transferability for different events. It is intended to be used as a base class for other token - * contracts. - * LimitedTransferToken has been designed to allow for different limiting factors, - * this can be achieved by recursively calling super.transferableTokens() until the base class is - * hit. For example: - * function transferableTokens(address holder, uint64 time) constant public returns (uint256) { - * return min256(unlockedTokens, super.transferableTokens(holder, time)); - * } - * A working example is VestedToken.sol: - * https://github.com/OpenZeppelin/zeppelin-solidity/blob/master/contracts/token/VestedToken.sol - */ - -contract LimitedTransferToken is ERC20 { - - /** - * @dev Checks whether it can transfer or otherwise throws. - */ - modifier canTransfer(address _sender, uint256 _value) { - if (_value > transferableTokens(_sender, uint64(now))) throw; - _; - } - - /** - * @dev Checks modifier and allows transfer if tokens are not locked. - * @param _to The address that will recieve the tokens. - * @param _value The amount of tokens to be transferred. - */ - function transfer(address _to, uint256 _value) canTransfer(msg.sender, _value) { - super.transfer(_to, _value); - } - - /** - * @dev Checks modifier and allows transfer if tokens are not locked. - * @param _from The address that will send the tokens. - * @param _to The address that will recieve the tokens. - * @param _value The amount of tokens to be transferred. - */ - function transferFrom(address _from, address _to, uint256 _value) canTransfer(_from, _value) { - super.transferFrom(_from, _to, _value); - } - - /** - * @dev Default transferable tokens function returns all tokens for a holder (no limit). - * @dev Overwriting transferableTokens(address holder, uint64 time) is the way to provide the - * specific logic for limiting token transferability for a holder over time. - */ - function transferableTokens(address holder, uint64 time) constant public returns (uint256) { - return balanceOf(holder); - } -} diff --git a/test/compilationTests/zeppelin/token/MintableToken.sol b/test/compilationTests/zeppelin/token/MintableToken.sol deleted file mode 100644 index 505d13c3..00000000 --- a/test/compilationTests/zeppelin/token/MintableToken.sol +++ /dev/null @@ -1,50 +0,0 @@ -pragma solidity ^0.4.11; - - -import './StandardToken.sol'; -import '../ownership/Ownable.sol'; - - - -/** - * @title Mintable token - * @dev Simple ERC20 Token example, with mintable token creation - * @dev Issue: * https://github.com/OpenZeppelin/zeppelin-solidity/issues/120 - * Based on code by TokenMarketNet: https://github.com/TokenMarketNet/ico/blob/master/contracts/MintableToken.sol - */ - -contract MintableToken is StandardToken, Ownable { - event Mint(address indexed to, uint256 amount); - event MintFinished(); - - bool public mintingFinished = false; - - - modifier canMint() { - if(mintingFinished) throw; - _; - } - - /** - * @dev Function to mint tokens - * @param _to The address that will recieve the minted tokens. - * @param _amount The amount of tokens to mint. - * @return A boolean that indicates if the operation was successful. - */ - function mint(address _to, uint256 _amount) onlyOwner canMint returns (bool) { - totalSupply = totalSupply.add(_amount); - balances[_to] = balances[_to].add(_amount); - Mint(_to, _amount); - return true; - } - - /** - * @dev Function to stop minting new tokens. - * @return True if the operation was successful. - */ - function finishMinting() onlyOwner returns (bool) { - mintingFinished = true; - MintFinished(); - return true; - } -} diff --git a/test/compilationTests/zeppelin/token/PausableToken.sol b/test/compilationTests/zeppelin/token/PausableToken.sol deleted file mode 100644 index 8ee114e1..00000000 --- a/test/compilationTests/zeppelin/token/PausableToken.sol +++ /dev/null @@ -1,21 +0,0 @@ -pragma solidity ^0.4.11; - -import './StandardToken.sol'; -import '../lifecycle/Pausable.sol'; - -/** - * Pausable token - * - * Simple ERC20 Token example, with pausable token creation - **/ - -contract PausableToken is StandardToken, Pausable { - - function transfer(address _to, uint _value) whenNotPaused { - super.transfer(_to, _value); - } - - function transferFrom(address _from, address _to, uint _value) whenNotPaused { - super.transferFrom(_from, _to, _value); - } -} diff --git a/test/compilationTests/zeppelin/token/SimpleToken.sol b/test/compilationTests/zeppelin/token/SimpleToken.sol deleted file mode 100644 index 898cb21d..00000000 --- a/test/compilationTests/zeppelin/token/SimpleToken.sol +++ /dev/null @@ -1,28 +0,0 @@ -pragma solidity ^0.4.11; - - -import "./StandardToken.sol"; - - -/** - * @title SimpleToken - * @dev Very simple ERC20 Token example, where all tokens are pre-assigned to the creator. - * Note they can later distribute these tokens as they wish using `transfer` and other - * `StandardToken` functions. - */ -contract SimpleToken is StandardToken { - - string public name = "SimpleToken"; - string public symbol = "SIM"; - uint256 public decimals = 18; - uint256 public INITIAL_SUPPLY = 10000; - - /** - * @dev Contructor that gives msg.sender all of existing tokens. - */ - function SimpleToken() { - totalSupply = INITIAL_SUPPLY; - balances[msg.sender] = INITIAL_SUPPLY; - } - -} diff --git a/test/compilationTests/zeppelin/token/StandardToken.sol b/test/compilationTests/zeppelin/token/StandardToken.sol deleted file mode 100644 index b1b49fe3..00000000 --- a/test/compilationTests/zeppelin/token/StandardToken.sol +++ /dev/null @@ -1,65 +0,0 @@ -pragma solidity ^0.4.11; - - -import './BasicToken.sol'; -import './ERC20.sol'; - - -/** - * @title Standard ERC20 token - * - * @dev Implementation of the basic standard token. - * @dev https://github.com/ethereum/EIPs/issues/20 - * @dev Based on code by FirstBlood: https://github.com/Firstbloodio/token/blob/master/smart_contract/FirstBloodToken.sol - */ -contract StandardToken is ERC20, BasicToken { - - mapping (address => mapping (address => uint256)) allowed; - - - /** - * @dev Transfer tokens from one address to another - * @param _from address The address which you want to send tokens from - * @param _to address The address which you want to transfer to - * @param _value uint256 the amout of tokens to be transfered - */ - function transferFrom(address _from, address _to, uint256 _value) { - var _allowance = allowed[_from][msg.sender]; - - // Check is not needed because sub(_allowance, _value) will already throw if this condition is not met - // if (_value > _allowance) throw; - - balances[_to] = balances[_to].add(_value); - balances[_from] = balances[_from].sub(_value); - allowed[_from][msg.sender] = _allowance.sub(_value); - Transfer(_from, _to, _value); - } - - /** - * @dev Aprove the passed address to spend the specified amount of tokens on behalf of msg.sender. - * @param _spender The address which will spend the funds. - * @param _value The amount of tokens to be spent. - */ - function approve(address _spender, uint256 _value) { - - // To change the approve amount you first have to reduce the addresses` - // allowance to zero by calling `approve(_spender, 0)` if it is not - // already 0 to mitigate the race condition described here: - // https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 - if ((_value != 0) && (allowed[msg.sender][_spender] != 0)) throw; - - allowed[msg.sender][_spender] = _value; - Approval(msg.sender, _spender, _value); - } - - /** - * @dev Function to check the amount of tokens that an owner allowed to a spender. - * @param _owner address The address which owns the funds. - * @param _spender address The address which will spend the funds. - * @return A uint256 specifing the amount of tokens still avaible for the spender. - */ - function allowance(address _owner, address _spender) constant returns (uint256 remaining) { - return allowed[_owner][_spender]; - } - -} diff --git a/test/compilationTests/zeppelin/token/TokenTimelock.sol b/test/compilationTests/zeppelin/token/TokenTimelock.sol deleted file mode 100644 index 595bf8d0..00000000 --- a/test/compilationTests/zeppelin/token/TokenTimelock.sol +++ /dev/null @@ -1,41 +0,0 @@ -pragma solidity ^0.4.11; - - -import './ERC20Basic.sol'; - -/** - * @title TokenTimelock - * @dev TokenTimelock is a token holder contract that will allow a - * beneficiary to extract the tokens after a given release time - */ -contract TokenTimelock { - - // ERC20 basic token contract being held - ERC20Basic token; - - // beneficiary of tokens after they are released - address beneficiary; - - // timestamp when token release is enabled - uint releaseTime; - - function TokenTimelock(ERC20Basic _token, address _beneficiary, uint _releaseTime) { - require(_releaseTime > now); - token = _token; - beneficiary = _beneficiary; - releaseTime = _releaseTime; - } - - /** - * @dev beneficiary claims tokens held by time lock - */ - function claim() { - require(msg.sender == beneficiary); - require(now >= releaseTime); - - uint amount = token.balanceOf(this); - require(amount > 0); - - token.transfer(beneficiary, amount); - } -} diff --git a/test/compilationTests/zeppelin/token/VestedToken.sol b/test/compilationTests/zeppelin/token/VestedToken.sol deleted file mode 100644 index b7748b09..00000000 --- a/test/compilationTests/zeppelin/token/VestedToken.sol +++ /dev/null @@ -1,248 +0,0 @@ -pragma solidity ^0.4.11; - -import "../math/Math.sol"; -import "./StandardToken.sol"; -import "./LimitedTransferToken.sol"; - -/** - * @title Vested token - * @dev Tokens that can be vested for a group of addresses. - */ -contract VestedToken is StandardToken, LimitedTransferToken { - - uint256 MAX_GRANTS_PER_ADDRESS = 20; - - struct TokenGrant { - address granter; // 20 bytes - uint256 value; // 32 bytes - uint64 cliff; - uint64 vesting; - uint64 start; // 3 * 8 = 24 bytes - bool revokable; - bool burnsOnRevoke; // 2 * 1 = 2 bits? or 2 bytes? - } // total 78 bytes = 3 sstore per operation (32 per sstore) - - mapping (address => TokenGrant[]) public grants; - - event NewTokenGrant(address indexed from, address indexed to, uint256 value, uint256 grantId); - - /** - * @dev Grant tokens to a specified address - * @param _to address The address which the tokens will be granted to. - * @param _value uint256 The amount of tokens to be granted. - * @param _start uint64 Time of the beginning of the grant. - * @param _cliff uint64 Time of the cliff period. - * @param _vesting uint64 The vesting period. - */ - function grantVestedTokens( - address _to, - uint256 _value, - uint64 _start, - uint64 _cliff, - uint64 _vesting, - bool _revokable, - bool _burnsOnRevoke - ) public { - - // Check for date inconsistencies that may cause unexpected behavior - if (_cliff < _start || _vesting < _cliff) { - throw; - } - - if (tokenGrantsCount(_to) > MAX_GRANTS_PER_ADDRESS) throw; // To prevent a user being spammed and have his balance locked (out of gas attack when calculating vesting). - - uint256 count = grants[_to].push( - TokenGrant( - _revokable ? msg.sender : 0, // avoid storing an extra 20 bytes when it is non-revokable - _value, - _cliff, - _vesting, - _start, - _revokable, - _burnsOnRevoke - ) - ); - - transfer(_to, _value); - - NewTokenGrant(msg.sender, _to, _value, count - 1); - } - - /** - * @dev Revoke the grant of tokens of a specifed address. - * @param _holder The address which will have its tokens revoked. - * @param _grantId The id of the token grant. - */ - function revokeTokenGrant(address _holder, uint256 _grantId) public { - TokenGrant grant = grants[_holder][_grantId]; - - if (!grant.revokable) { // Check if grant was revokable - throw; - } - - if (grant.granter != msg.sender) { // Only granter can revoke it - throw; - } - - address receiver = grant.burnsOnRevoke ? 0xdead : msg.sender; - - uint256 nonVested = nonVestedTokens(grant, uint64(now)); - - // remove grant from array - delete grants[_holder][_grantId]; - grants[_holder][_grantId] = grants[_holder][grants[_holder].length.sub(1)]; - grants[_holder].length -= 1; - - balances[receiver] = balances[receiver].add(nonVested); - balances[_holder] = balances[_holder].sub(nonVested); - - Transfer(_holder, receiver, nonVested); - } - - - /** - * @dev Calculate the total amount of transferable tokens of a holder at a given time - * @param holder address The address of the holder - * @param time uint64 The specific time. - * @return An uint256 representing a holder's total amount of transferable tokens. - */ - function transferableTokens(address holder, uint64 time) constant public returns (uint256) { - uint256 grantIndex = tokenGrantsCount(holder); - - if (grantIndex == 0) return balanceOf(holder); // shortcut for holder without grants - - // Iterate through all the grants the holder has, and add all non-vested tokens - uint256 nonVested = 0; - for (uint256 i = 0; i < grantIndex; i++) { - nonVested = SafeMath.add(nonVested, nonVestedTokens(grants[holder][i], time)); - } - - // Balance - totalNonVested is the amount of tokens a holder can transfer at any given time - uint256 vestedTransferable = SafeMath.sub(balanceOf(holder), nonVested); - - // Return the minimum of how many vested can transfer and other value - // in case there are other limiting transferability factors (default is balanceOf) - return Math.min256(vestedTransferable, super.transferableTokens(holder, time)); - } - - /** - * @dev Check the amount of grants that an address has. - * @param _holder The holder of the grants. - * @return A uint256 representing the total amount of grants. - */ - function tokenGrantsCount(address _holder) constant returns (uint256 index) { - return grants[_holder].length; - } - - /** - * @dev Calculate amount of vested tokens at a specifc time. - * @param tokens uint256 The amount of tokens grantted. - * @param time uint64 The time to be checked - * @param start uint64 A time representing the begining of the grant - * @param cliff uint64 The cliff period. - * @param vesting uint64 The vesting period. - * @return An uint256 representing the amount of vested tokensof a specif grant. - * transferableTokens - * | _/-------- vestedTokens rect - * | _/ - * | _/ - * | _/ - * | _/ - * | / - * | .| - * | . | - * | . | - * | . | - * | . | - * | . | - * +===+===========+---------+----------> time - * Start Clift Vesting - */ - function calculateVestedTokens( - uint256 tokens, - uint256 time, - uint256 start, - uint256 cliff, - uint256 vesting) constant returns (uint256) - { - // Shortcuts for before cliff and after vesting cases. - if (time < cliff) return 0; - if (time >= vesting) return tokens; - - // Interpolate all vested tokens. - // As before cliff the shortcut returns 0, we can use just calculate a value - // in the vesting rect (as shown in above's figure) - - // vestedTokens = tokens * (time - start) / (vesting - start) - uint256 vestedTokens = SafeMath.div( - SafeMath.mul( - tokens, - SafeMath.sub(time, start) - ), - SafeMath.sub(vesting, start) - ); - - return vestedTokens; - } - - /** - * @dev Get all information about a specifc grant. - * @param _holder The address which will have its tokens revoked. - * @param _grantId The id of the token grant. - * @return Returns all the values that represent a TokenGrant(address, value, start, cliff, - * revokability, burnsOnRevoke, and vesting) plus the vested value at the current time. - */ - function tokenGrant(address _holder, uint256 _grantId) constant returns (address granter, uint256 value, uint256 vested, uint64 start, uint64 cliff, uint64 vesting, bool revokable, bool burnsOnRevoke) { - TokenGrant grant = grants[_holder][_grantId]; - - granter = grant.granter; - value = grant.value; - start = grant.start; - cliff = grant.cliff; - vesting = grant.vesting; - revokable = grant.revokable; - burnsOnRevoke = grant.burnsOnRevoke; - - vested = vestedTokens(grant, uint64(now)); - } - - /** - * @dev Get the amount of vested tokens at a specific time. - * @param grant TokenGrant The grant to be checked. - * @param time The time to be checked - * @return An uint256 representing the amount of vested tokens of a specific grant at a specific time. - */ - function vestedTokens(TokenGrant grant, uint64 time) private constant returns (uint256) { - return calculateVestedTokens( - grant.value, - uint256(time), - uint256(grant.start), - uint256(grant.cliff), - uint256(grant.vesting) - ); - } - - /** - * @dev Calculate the amount of non vested tokens at a specific time. - * @param grant TokenGrant The grant to be checked. - * @param time uint64 The time to be checked - * @return An uint256 representing the amount of non vested tokens of a specifc grant on the - * passed time frame. - */ - function nonVestedTokens(TokenGrant grant, uint64 time) private constant returns (uint256) { - return grant.value.sub(vestedTokens(grant, time)); - } - - /** - * @dev Calculate the date when the holder can trasfer all its tokens - * @param holder address The address of the holder - * @return An uint256 representing the date of the last transferable tokens. - */ - function lastTokenIsTransferableDate(address holder) constant public returns (uint64 date) { - date = uint64(now); - uint256 grantIndex = grants[holder].length; - for (uint256 i = 0; i < grantIndex; i++) { - date = Math.max64(grants[holder][i].vesting, date); - } - } -} diff --git a/test/contracts/AuctionRegistrar.cpp b/test/contracts/AuctionRegistrar.cpp index 5e4991e2..eb274c09 100644 --- a/test/contracts/AuctionRegistrar.cpp +++ b/test/contracts/AuctionRegistrar.cpp @@ -40,23 +40,23 @@ namespace { static char const* registrarCode = R"DELIMITER( -pragma solidity ^0.4.0; +pragma solidity >=0.4.0 <0.6.0; contract NameRegister { - function addr(string _name) constant returns (address o_owner); - function name(address _owner) constant returns (string o_name); + function addr(string memory _name) public view returns (address o_owner); + function name(address _owner) public view returns (string memory o_name); } contract Registrar is NameRegister { event Changed(string indexed name); event PrimaryChanged(string indexed name, address indexed addr); - function owner(string _name) constant returns (address o_owner); - function addr(string _name) constant returns (address o_address); - function subRegistrar(string _name) constant returns (address o_subRegistrar); - function content(string _name) constant returns (bytes32 o_content); + function owner(string memory _name) public view returns (address o_owner); + function addr(string memory _name) public view returns (address o_address); + function subRegistrar(string memory _name) public view returns (address o_subRegistrar); + function content(string memory _name) public view returns (bytes32 o_content); - function name(address _owner) constant returns (string o_name); + function name(address _owner) public view returns (string memory o_name); } contract AuctionSystem { @@ -64,13 +64,13 @@ contract AuctionSystem { event NewBid(string indexed _name, address _bidder, uint _value); /// Function that is called once an auction ends. - function onAuctionEnd(string _name) internal; + function onAuctionEnd(string memory _name) internal; - function bid(string _name, address _bidder, uint _value) internal { - var auction = m_auctions[_name]; + function bid(string memory _name, address payable _bidder, uint _value) internal { + Auction storage auction = m_auctions[_name]; if (auction.endDate > 0 && now > auction.endDate) { - AuctionEnded(_name, auction.highestBidder); + emit AuctionEnded(_name, auction.highestBidder); onAuctionEnd(_name); delete m_auctions[_name]; return; @@ -84,14 +84,14 @@ contract AuctionSystem { auction.highestBidder = _bidder; auction.endDate = now + c_biddingTime; - NewBid(_name, _bidder, _value); + emit NewBid(_name, _bidder, _value); } } uint constant c_biddingTime = 7 days; struct Auction { - address highestBidder; + address payable highestBidder; uint highestBid; uint secondHighestBid; uint sumOfBids; @@ -102,91 +102,91 @@ contract AuctionSystem { contract GlobalRegistrar is Registrar, AuctionSystem { struct Record { - address owner; + address payable owner; address primary; address subRegistrar; bytes32 content; uint renewalDate; } - uint constant c_renewalInterval = 1 years; + uint constant c_renewalInterval = 365 days; uint constant c_freeBytes = 12; - function Registrar() { + function Registrar() public { // TODO: Populate with hall-of-fame. } - function onAuctionEnd(string _name) internal { - var auction = m_auctions[_name]; - var record = m_toRecord[_name]; - var previousOwner = record.owner; + function onAuctionEnd(string memory _name) internal { + Auction storage auction = m_auctions[_name]; + Record storage record = m_toRecord[_name]; + address previousOwner = record.owner; record.renewalDate = now + c_renewalInterval; record.owner = auction.highestBidder; - Changed(_name); - if (previousOwner != 0) { + emit Changed(_name); + if (previousOwner != 0x0000000000000000000000000000000000000000) { if (!record.owner.send(auction.sumOfBids - auction.highestBid / 100)) - throw; + revert(); } else { if (!auction.highestBidder.send(auction.highestBid - auction.secondHighestBid)) - throw; + revert(); } } - function reserve(string _name) external payable { + function reserve(string calldata _name) external payable { if (bytes(_name).length == 0) - throw; + revert(); bool needAuction = requiresAuction(_name); if (needAuction) { if (now < m_toRecord[_name].renewalDate) - throw; + revert(); bid(_name, msg.sender, msg.value); } else { - Record record = m_toRecord[_name]; - if (record.owner != 0) - throw; + Record storage record = m_toRecord[_name]; + if (record.owner != 0x0000000000000000000000000000000000000000) + revert(); m_toRecord[_name].owner = msg.sender; - Changed(_name); + emit Changed(_name); } } - function requiresAuction(string _name) internal returns (bool) { + function requiresAuction(string memory _name) internal returns (bool) { return bytes(_name).length < c_freeBytes; } - modifier onlyrecordowner(string _name) { if (m_toRecord[_name].owner == msg.sender) _; } + modifier onlyrecordowner(string memory _name) { if (m_toRecord[_name].owner == msg.sender) _; } - function transfer(string _name, address _newOwner) onlyrecordowner(_name) { + function transfer(string memory _name, address payable _newOwner) onlyrecordowner(_name) public { m_toRecord[_name].owner = _newOwner; - Changed(_name); + emit Changed(_name); } - function disown(string _name) onlyrecordowner(_name) { + function disown(string memory _name) onlyrecordowner(_name) public { if (stringsEqual(m_toName[m_toRecord[_name].primary], _name)) { - PrimaryChanged(_name, m_toRecord[_name].primary); + emit PrimaryChanged(_name, m_toRecord[_name].primary); m_toName[m_toRecord[_name].primary] = ""; } delete m_toRecord[_name]; - Changed(_name); + emit Changed(_name); } - function setAddress(string _name, address _a, bool _primary) onlyrecordowner(_name) { + function setAddress(string memory _name, address _a, bool _primary) onlyrecordowner(_name) public { m_toRecord[_name].primary = _a; if (_primary) { - PrimaryChanged(_name, _a); + emit PrimaryChanged(_name, _a); m_toName[_a] = _name; } - Changed(_name); + emit Changed(_name); } - function setSubRegistrar(string _name, address _registrar) onlyrecordowner(_name) { + function setSubRegistrar(string memory _name, address _registrar) onlyrecordowner(_name) public { m_toRecord[_name].subRegistrar = _registrar; - Changed(_name); + emit Changed(_name); } - function setContent(string _name, bytes32 _content) onlyrecordowner(_name) { + function setContent(string memory _name, bytes32 _content) onlyrecordowner(_name) public { m_toRecord[_name].content = _content; - Changed(_name); + emit Changed(_name); } function stringsEqual(string storage _a, string memory _b) internal returns (bool) { @@ -201,11 +201,11 @@ contract GlobalRegistrar is Registrar, AuctionSystem { return true; } - function owner(string _name) constant returns (address) { return m_toRecord[_name].owner; } - function addr(string _name) constant returns (address) { return m_toRecord[_name].primary; } - function subRegistrar(string _name) constant returns (address) { return m_toRecord[_name].subRegistrar; } - function content(string _name) constant returns (bytes32) { return m_toRecord[_name].content; } - function name(address _addr) constant returns (string o_name) { return m_toName[_addr]; } + function owner(string memory _name) public view returns (address) { return m_toRecord[_name].owner; } + function addr(string memory _name) public view returns (address) { return m_toRecord[_name].primary; } + function subRegistrar(string memory _name) public view returns (address) { return m_toRecord[_name].subRegistrar; } + function content(string memory _name) public view returns (bytes32) { return m_toRecord[_name].content; } + function name(address _addr) public view returns (string memory o_name) { return m_toName[_addr]; } mapping (address => string) m_toName; mapping (string => Record) m_toRecord; @@ -223,6 +223,7 @@ protected: s_compiledRegistrar.reset(new bytes(compileContract(registrarCode, "GlobalRegistrar"))); sendMessage(*s_compiledRegistrar, true); + BOOST_REQUIRE(m_transactionSuccessful); BOOST_REQUIRE(!m_output.empty()); } diff --git a/test/contracts/FixedFeeRegistrar.cpp b/test/contracts/FixedFeeRegistrar.cpp index a3a27c37..e82f389f 100644 --- a/test/contracts/FixedFeeRegistrar.cpp +++ b/test/contracts/FixedFeeRegistrar.cpp @@ -53,15 +53,15 @@ static char const* registrarCode = R"DELIMITER( // @authors: // Gav Wood <g@ethdev.com> -pragma solidity ^0.4.0; +pragma solidity >=0.4.0 <0.6.0; contract Registrar { event Changed(string indexed name); - function owner(string _name) constant returns (address o_owner); - function addr(string _name) constant returns (address o_address); - function subRegistrar(string _name) constant returns (address o_subRegistrar); - function content(string _name) constant returns (bytes32 o_content); + function owner(string memory _name) public view returns (address o_owner); + function addr(string memory _name) public view returns (address o_address); + function subRegistrar(string memory _name) public view returns (address o_subRegistrar); + function content(string memory _name) public view returns (bytes32 o_content); } contract FixedFeeRegistrar is Registrar { @@ -72,53 +72,53 @@ contract FixedFeeRegistrar is Registrar { address owner; } - modifier onlyrecordowner(string _name) { if (m_record(_name).owner == msg.sender) _; } + modifier onlyrecordowner(string memory _name) { if (m_record(_name).owner == msg.sender) _; } - function reserve(string _name) payable { - Record rec = m_record(_name); - if (rec.owner == 0 && msg.value >= c_fee) { + function reserve(string memory _name) public payable { + Record storage rec = m_record(_name); + if (rec.owner == 0x0000000000000000000000000000000000000000 && msg.value >= c_fee) { rec.owner = msg.sender; - Changed(_name); + emit Changed(_name); } } - function disown(string _name, address _refund) onlyrecordowner(_name) { - delete m_recordData[uint(keccak256(_name)) / 8]; + function disown(string memory _name, address payable _refund) onlyrecordowner(_name) public { + delete m_recordData[uint(keccak256(bytes(_name))) / 8]; if (!_refund.send(c_fee)) - throw; - Changed(_name); + revert(); + emit Changed(_name); } - function transfer(string _name, address _newOwner) onlyrecordowner(_name) { + function transfer(string memory _name, address _newOwner) onlyrecordowner(_name) public { m_record(_name).owner = _newOwner; - Changed(_name); + emit Changed(_name); } - function setAddr(string _name, address _a) onlyrecordowner(_name) { + function setAddr(string memory _name, address _a) onlyrecordowner(_name) public { m_record(_name).addr = _a; - Changed(_name); + emit Changed(_name); } - function setSubRegistrar(string _name, address _registrar) onlyrecordowner(_name) { + function setSubRegistrar(string memory _name, address _registrar) onlyrecordowner(_name) public { m_record(_name).subRegistrar = _registrar; - Changed(_name); + emit Changed(_name); } - function setContent(string _name, bytes32 _content) onlyrecordowner(_name) { + function setContent(string memory _name, bytes32 _content) onlyrecordowner(_name) public { m_record(_name).content = _content; - Changed(_name); + emit Changed(_name); } - function record(string _name) constant returns (address o_addr, address o_subRegistrar, bytes32 o_content, address o_owner) { - Record rec = m_record(_name); + function record(string memory _name) public view returns (address o_addr, address o_subRegistrar, bytes32 o_content, address o_owner) { + Record storage rec = m_record(_name); o_addr = rec.addr; o_subRegistrar = rec.subRegistrar; o_content = rec.content; o_owner = rec.owner; } - function addr(string _name) constant returns (address) { return m_record(_name).addr; } - function subRegistrar(string _name) constant returns (address) { return m_record(_name).subRegistrar; } - function content(string _name) constant returns (bytes32) { return m_record(_name).content; } - function owner(string _name) constant returns (address) { return m_record(_name).owner; } + function addr(string memory _name) public view returns (address) { return m_record(_name).addr; } + function subRegistrar(string memory _name) public view returns (address) { return m_record(_name).subRegistrar; } + function content(string memory _name) public view returns (bytes32) { return m_record(_name).content; } + function owner(string memory _name) public view returns (address) { return m_record(_name).owner; } Record[2**253] m_recordData; - function m_record(string _name) constant internal returns (Record storage o_record) { - return m_recordData[uint(keccak256(_name)) / 8]; + function m_record(string memory _name) view internal returns (Record storage o_record) { + return m_recordData[uint(keccak256(bytes(_name))) / 8]; } uint constant c_fee = 69 ether; } @@ -135,6 +135,7 @@ protected: s_compiledRegistrar.reset(new bytes(compileContract(registrarCode, "FixedFeeRegistrar"))); sendMessage(*s_compiledRegistrar, true); + BOOST_REQUIRE(m_transactionSuccessful); BOOST_REQUIRE(!m_output.empty()); } u256 const m_fee = u256("69000000000000000000"); diff --git a/test/contracts/LLL_ENS.cpp b/test/contracts/LLL_ENS.cpp index 028d58c8..cfd6970c 100644 --- a/test/contracts/LLL_ENS.cpp +++ b/test/contracts/LLL_ENS.cpp @@ -28,7 +28,7 @@ #define ACCOUNT(n) h256(account(n), h256::AlignRight) using namespace std; -using namespace dev::eth; +using namespace dev::lll; namespace dev { @@ -349,6 +349,7 @@ protected: BOOST_REQUIRE(errors.empty()); } sendMessage(*s_compiledEns, true); + BOOST_REQUIRE(m_transactionSuccessful); BOOST_REQUIRE(!m_output.empty()); } diff --git a/test/contracts/LLL_ERC20.cpp b/test/contracts/LLL_ERC20.cpp index 60b43e4f..6c6762dd 100644 --- a/test/contracts/LLL_ERC20.cpp +++ b/test/contracts/LLL_ERC20.cpp @@ -33,7 +33,7 @@ #define SUCCESS encodeArgs(1) using namespace std; -using namespace dev::eth; +using namespace dev::lll; namespace dev { @@ -400,6 +400,7 @@ protected: BOOST_REQUIRE(errors.empty()); } sendMessage(*s_compiledErc20, true); + BOOST_REQUIRE(m_transactionSuccessful); BOOST_REQUIRE(!m_output.empty()); } @@ -629,18 +630,22 @@ BOOST_AUTO_TEST_CASE(bad_data) // Correct data: transfer(address _to, 1). sendMessage((bytes)fromHex("a9059cbb") + (bytes)fromHex("000000000000000000000000123456789a123456789a123456789a123456789a") + encodeArgs(1), false, 0); + BOOST_CHECK(m_transactionSuccessful); BOOST_CHECK(m_output == SUCCESS); // Too little data (address is truncated by one byte). sendMessage((bytes)fromHex("a9059cbb") + (bytes)fromHex("000000000000000000000000123456789a123456789a123456789a12345678") + encodeArgs(1), false, 0); + BOOST_CHECK(!m_transactionSuccessful); BOOST_CHECK(m_output != SUCCESS); // Too much data (address is extended with a zero byte). sendMessage((bytes)fromHex("a9059cbb") + (bytes)fromHex("000000000000000000000000123456789a123456789a123456789a123456789a00") + encodeArgs(1), false, 0); + BOOST_CHECK(!m_transactionSuccessful); BOOST_CHECK(m_output != SUCCESS); // Invalid address (a bit above the 160th is set). sendMessage((bytes)fromHex("a9059cbb") + (bytes)fromHex("000000000000000000000100123456789a123456789a123456789a123456789a") + encodeArgs(1), false, 0); + BOOST_CHECK(!m_transactionSuccessful); BOOST_CHECK(m_output != SUCCESS); } diff --git a/test/contracts/Wallet.cpp b/test/contracts/Wallet.cpp index 1031e8f1..9fe02e46 100644 --- a/test/contracts/Wallet.cpp +++ b/test/contracts/Wallet.cpp @@ -56,7 +56,7 @@ static char const* walletCode = R"DELIMITER( // some number (specified in constructor) of the set of owners (specified in the constructor, modifiable) before the // interior is executed. -pragma solidity ^0.4.0; +pragma solidity >=0.4.0 <0.6.0; contract multiowned { @@ -101,7 +101,7 @@ contract multiowned { // constructor is given number of sigs required to do protected "onlymanyowners" transactions // as well as the selection of addresses capable of confirming them. - function multiowned(address[] _owners, uint _required) { + constructor(address[] memory _owners, uint _required) public { m_numOwners = _owners.length + 1; m_owners[1] = uint(msg.sender); m_ownerIndex[uint(msg.sender)] = 1; @@ -119,11 +119,11 @@ contract multiowned { // make sure they're an owner if (ownerIndex == 0) return; uint ownerIndexBit = 2**ownerIndex; - var pending = m_pending[_operation]; + PendingState storage pending = m_pending[_operation]; if (pending.ownersDone & ownerIndexBit > 0) { pending.yetNeeded++; pending.ownersDone -= ownerIndexBit; - Revoke(msg.sender, _operation); + emit Revoke(msg.sender, _operation); } } @@ -137,7 +137,7 @@ contract multiowned { m_owners[ownerIndex] = uint(_to); m_ownerIndex[uint(_from)] = 0; m_ownerIndex[uint(_to)] = ownerIndex; - OwnerChanged(_from, _to); + emit OwnerChanged(_from, _to); } function addOwner(address _owner) onlymanyowners(keccak256(msg.data)) external { @@ -151,7 +151,7 @@ contract multiowned { m_numOwners++; m_owners[m_numOwners] = uint(_owner); m_ownerIndex[uint(_owner)] = m_numOwners; - OwnerAdded(_owner); + emit OwnerAdded(_owner); } function removeOwner(address _owner) onlymanyowners(keccak256(msg.data)) external { @@ -163,22 +163,22 @@ contract multiowned { m_ownerIndex[uint(_owner)] = 0; clearPending(); reorganizeOwners(); //make sure m_numOwner is equal to the number of owners and always points to the optimal free slot - OwnerRemoved(_owner); + emit OwnerRemoved(_owner); } function changeRequirement(uint _newRequired) onlymanyowners(keccak256(msg.data)) external { if (_newRequired > m_numOwners) return; m_required = _newRequired; clearPending(); - RequirementChanged(_newRequired); + emit RequirementChanged(_newRequired); } - function isOwner(address _addr) returns (bool) { + function isOwner(address _addr) public returns (bool) { return m_ownerIndex[uint(_addr)] > 0; } - function hasConfirmed(bytes32 _operation, address _owner) constant returns (bool) { - var pending = m_pending[_operation]; + function hasConfirmed(bytes32 _operation, address _owner) public view returns (bool) { + PendingState storage pending = m_pending[_operation]; uint ownerIndex = m_ownerIndex[uint(_owner)]; // make sure they're an owner @@ -199,9 +199,9 @@ contract multiowned { // determine what index the present sender is: uint ownerIndex = m_ownerIndex[uint(msg.sender)]; // make sure they're an owner - if (ownerIndex == 0) return; + if (ownerIndex == 0) return false; - var pending = m_pending[_operation]; + PendingState storage pending = m_pending[_operation]; // if we're not yet working on this operation, switch over and reset the confirmation status. if (pending.yetNeeded == 0) { // reset count of confirmations needed. @@ -215,7 +215,7 @@ contract multiowned { uint ownerIndexBit = 2**ownerIndex; // make sure we (the message sender) haven't confirmed this operation previously. if (pending.ownersDone & ownerIndexBit == 0) { - Confirmation(msg.sender, _operation); + emit Confirmation(msg.sender, _operation); // ok - check if count is enough to go ahead. if (pending.yetNeeded <= 1) { // enough confirmations: reset and run interior. @@ -228,6 +228,7 @@ contract multiowned { // not enough: record that this owner in particular confirmed. pending.yetNeeded--; pending.ownersDone |= ownerIndexBit; + return false; } } } @@ -288,7 +289,7 @@ contract daylimit is multiowned { // METHODS // constructor - stores initial daily limit and records the present day's index. - function daylimit(uint _limit) { + constructor(uint _limit) public { m_dailyLimit = _limit; m_lastDay = today(); } @@ -319,7 +320,7 @@ contract daylimit is multiowned { return false; } // determines today's index. - function today() private constant returns (uint) { return now / 1 days; } + function today() private view returns (uint) { return now / 1 days; } // FIELDS @@ -347,8 +348,8 @@ contract multisig { // TODO: document function changeOwner(address _from, address _to) external; - function execute(address _to, uint _value, bytes _data) external returns (bytes32); - function confirm(bytes32 _h) returns (bool); + function execute(address _to, uint _value, bytes calldata _data) external returns (bytes32); + function confirm(bytes32 _h) public returns (bool); } // usage: @@ -369,50 +370,50 @@ contract Wallet is multisig, multiowned, daylimit { // constructor - just pass on the owner array to the multiowned and // the limit to daylimit - function Wallet(address[] _owners, uint _required, uint _daylimit) payable + constructor(address[] memory _owners, uint _required, uint _daylimit) public payable multiowned(_owners, _required) daylimit(_daylimit) { } // destroys the contract sending everything to `_to`. - function kill(address _to) onlymanyowners(keccak256(msg.data)) external { + function kill(address payable _to) onlymanyowners(keccak256(msg.data)) external { selfdestruct(_to); } // gets called when no other function matches - function() payable { + function() external payable { // just being sent some cash? if (msg.value > 0) - Deposit(msg.sender, msg.value); + emit Deposit(msg.sender, msg.value); } - // Outside-visible transact entry point. Executes transacion immediately if below daily spend limit. + // Outside-visible transact entry point. Executes transaction immediately if below daily spend limit. // If not, goes into multisig process. We provide a hash on return to allow the sender to provide // shortcuts for the other confirmations (allowing them to avoid replicating the _to, _value // and _data arguments). They still get the option of using them if they want, anyways. - function execute(address _to, uint _value, bytes _data) external onlyowner returns (bytes32 _r) { + function execute(address _to, uint _value, bytes calldata _data) external onlyowner returns (bytes32 _r) { // first, take the opportunity to check that we're under the daily limit. if (underLimit(_value)) { - SingleTransact(msg.sender, _value, _to, _data); + emit SingleTransact(msg.sender, _value, _to, _data); // yes - just execute the call. _to.call.value(_value)(_data); return 0; } // determine our operation hash. - _r = keccak256(msg.data, block.number); - if (!confirm(_r) && m_txs[_r].to == 0) { + _r = keccak256(abi.encodePacked(msg.data, block.number)); + if (!confirm(_r) && m_txs[_r].to == 0x0000000000000000000000000000000000000000) { m_txs[_r].to = _to; m_txs[_r].value = _value; m_txs[_r].data = _data; - ConfirmationNeeded(_r, msg.sender, _value, _to, _data); + emit ConfirmationNeeded(_r, msg.sender, _value, _to, _data); } } // confirm a transaction through just the hash. we use the previous transactions map, m_txs, in order // to determine the body of the transaction from the hash provided. - function confirm(bytes32 _h) onlymanyowners(_h) returns (bool) { - if (m_txs[_h].to != 0) { + function confirm(bytes32 _h) onlymanyowners(_h) public returns (bool) { + if (m_txs[_h].to != 0x0000000000000000000000000000000000000000) { m_txs[_h].to.call.value(m_txs[_h].value)(m_txs[_h].data); - MultiTransact(msg.sender, _h, m_txs[_h].value, m_txs[_h].to, m_txs[_h].data); + emit MultiTransact(msg.sender, _h, m_txs[_h].value, m_txs[_h].to, m_txs[_h].data); delete m_txs[_h]; return true; } @@ -451,6 +452,7 @@ protected: bytes args = encodeArgs(u256(0x60), _required, _dailyLimit, u256(_owners.size()), _owners); sendMessage(*s_compiledWallet + args, true, _value); + BOOST_REQUIRE(m_transactionSuccessful); BOOST_REQUIRE(!m_output.empty()); } }; diff --git a/test/externalTests.sh b/test/externalTests.sh index cd6deb75..93581925 100755 --- a/test/externalTests.sh +++ b/test/externalTests.sh @@ -40,21 +40,52 @@ function test_truffle { name="$1" repo="$2" + branch="$3" echo "Running $name tests..." DIR=$(mktemp -d) ( - git clone --depth 1 "$repo" "$DIR" + if [ -n "$branch" ] + then + echo "Cloning $branch of $repo..." + git clone --depth 1 "$repo" -b "$branch" "$DIR" + else + echo "Cloning $repo..." + git clone --depth 1 "$repo" "$DIR" + fi cd "$DIR" + echo "Current commit hash: `git rev-parse HEAD`" npm install - find . -name soljson.js -exec cp "$SOLJSON" {} \; - if [ "$name" == "Gnosis" ]; then + # Replace solc package by master + for d in node_modules node_modules/truffle/node_modules + do + ( + cd $d + rm -rf solc + git clone --depth 1 https://github.com/ethereum/solc-js.git solc + cp "$SOLJSON" solc/ + ) + done + if [ "$name" == "Zeppelin" -o "$name" == "Gnosis" ]; then + echo "Replaced fixed-version pragmas..." # Replace fixed-version pragmas in Gnosis (part of Consensys best practice) - find contracts test -name '*.sol' -type f -print0 | xargs -0 sed -i -e 's/pragma solidity 0/pragma solidity ^0/' + find contracts test -name '*.sol' -type f -print0 | xargs -0 sed -i -e 's/pragma solidity [\^0-9\.]*/pragma solidity >=0.0/' + fi + assertsol="node_modules/truffle/build/Assert.sol" + if [ -f "$assertsol" ] + then + echo "Replace Truffle's Assert.sol with a known good version" + rm "$assertsol" + wget https://raw.githubusercontent.com/trufflesuite/truffle-core/ef31bcaa15dbd9bd0f6a0070a5c63f271cde2dbc/lib/testing/Assert.sol -o "$assertsol" fi + # Change "compileStandard" to "compile" + sed -i s/solc.compileStandard/solc.compile/ "node_modules/truffle/build/cli.bundled.js" npm run test ) rm -rf "$DIR" } -test_truffle Gnosis https://github.com/gnosis/gnosis-contracts.git -test_truffle Zeppelin https://github.com/OpenZeppelin/zeppelin-solidity.git +# Using our temporary fork here. Hopefully to be merged into upstream after the 0.5.0 release. +test_truffle Zeppelin https://github.com/axic/openzeppelin-solidity.git solidity-050 + +# Disabled temporarily as it needs to be updated to latest Truffle first. +#test_truffle Gnosis https://github.com/axic/pm-contracts.git solidity-050 diff --git a/test/libdevcore/IterateReplacing.cpp b/test/libdevcore/IterateReplacing.cpp new file mode 100644 index 00000000..08cd1e22 --- /dev/null +++ b/test/libdevcore/IterateReplacing.cpp @@ -0,0 +1,97 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see <http://www.gnu.org/licenses/>. +*/ +/** + * Unit tests for the iterateReplacing function + */ + +#include <libdevcore/CommonData.h> + +#include <test/Options.h> + +using namespace std; + +namespace dev +{ +namespace test +{ + +BOOST_AUTO_TEST_SUITE(IterateReplacing) + +BOOST_AUTO_TEST_CASE(no_replacement) +{ + vector<string> v{"abc", "def", "ghi"}; + function<boost::optional<vector<string>>(string&)> f = [](string&) -> boost::optional<vector<string>> { return {}; }; + iterateReplacing(v, f); + vector<string> expectation{"abc", "def", "ghi"}; + BOOST_CHECK(v == expectation); +} + +BOOST_AUTO_TEST_CASE(empty_input) +{ + vector<string> v; + function<boost::optional<vector<string>>(string&)> f = [](string&) -> boost::optional<vector<string>> { return {}; }; + iterateReplacing(v, f); + vector<string> expectation; + BOOST_CHECK(v == expectation); +} + +BOOST_AUTO_TEST_CASE(delete_some) +{ + vector<string> v{"abc", "def", "ghi"}; + function<boost::optional<vector<string>>(string&)> f = [](string& _s) -> boost::optional<vector<string>> { + if (_s == "def") + return vector<string>(); + else + return {}; + }; + iterateReplacing(v, f); + vector<string> expectation{"abc", "ghi"}; + BOOST_CHECK(v == expectation); +} + +BOOST_AUTO_TEST_CASE(inject_some_start) +{ + vector<string> v{"abc", "def", "ghi"}; + function<boost::optional<vector<string>>(string&)> f = [](string& _s) -> boost::optional<vector<string>> { + if (_s == "abc") + return vector<string>{"x", "y"}; + else + return {}; + }; + iterateReplacing(v, f); + vector<string> expectation{"x", "y", "def", "ghi"}; + BOOST_CHECK(v == expectation); +} + +BOOST_AUTO_TEST_CASE(inject_some_end) +{ + vector<string> v{"abc", "def", "ghi"}; + function<boost::optional<vector<string>>(string&)> f = [](string& _s) -> boost::optional<vector<string>> { + if (_s == "ghi") + return vector<string>{"x", "y"}; + else + return {}; + }; + iterateReplacing(v, f); + vector<string> expectation{"abc", "def", "x", "y"}; + BOOST_CHECK(v == expectation); +} + +BOOST_AUTO_TEST_SUITE_END() + +} +} diff --git a/test/libdevcore/StringUtils.cpp b/test/libdevcore/StringUtils.cpp index 9ee93d02..76c11b82 100644 --- a/test/libdevcore/StringUtils.cpp +++ b/test/libdevcore/StringUtils.cpp @@ -48,6 +48,7 @@ BOOST_AUTO_TEST_CASE(test_similarity) BOOST_CHECK_EQUAL(stringWithinDistance("abc", "abcdef", 2), false); BOOST_CHECK_EQUAL(stringWithinDistance("abcd", "wxyz", 2), false); BOOST_CHECK_EQUAL(stringWithinDistance("", "", 2), true); + BOOST_CHECK_EQUAL(stringWithinDistance("YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY", "YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYZ", 2, 6400), false); } BOOST_AUTO_TEST_CASE(test_dldistance) @@ -81,6 +82,24 @@ BOOST_AUTO_TEST_CASE(test_alternatives_list) BOOST_CHECK_EQUAL(quotedAlternativesList(strings), "\"a\", \"b\", \"c\" or \"d\""); } +BOOST_AUTO_TEST_CASE(test_human_readable_join) +{ + BOOST_CHECK_EQUAL(joinHumanReadable(vector<string>({})), ""); + BOOST_CHECK_EQUAL(joinHumanReadable(vector<string>({"a"})), "a"); + BOOST_CHECK_EQUAL(joinHumanReadable(vector<string>({"a", "b"})), "a, b"); + BOOST_CHECK_EQUAL(joinHumanReadable(vector<string>({"a", "b", "c"})), "a, b, c"); + + BOOST_CHECK_EQUAL(joinHumanReadable(vector<string>({}), "; "), ""); + BOOST_CHECK_EQUAL(joinHumanReadable(vector<string>({"a"}), "; "), "a"); + BOOST_CHECK_EQUAL(joinHumanReadable(vector<string>({"a", "b"}), "; "), "a; b"); + BOOST_CHECK_EQUAL(joinHumanReadable(vector<string>({"a", "b", "c"}), "; "), "a; b; c"); + + BOOST_CHECK_EQUAL(joinHumanReadable(vector<string>({}), "; ", " or "), ""); + BOOST_CHECK_EQUAL(joinHumanReadable(vector<string>({"a"}), "; ", " or "), "a"); + BOOST_CHECK_EQUAL(joinHumanReadable(vector<string>({"a", "b"}), "; ", " or "), "a or b"); + BOOST_CHECK_EQUAL(joinHumanReadable(vector<string>({"a", "b", "c"}), "; ", " or "), "a; b or c"); +} + BOOST_AUTO_TEST_SUITE_END() diff --git a/test/libdevcore/UTF8.cpp b/test/libdevcore/UTF8.cpp index 6de4570f..daf47bb0 100644 --- a/test/libdevcore/UTF8.cpp +++ b/test/libdevcore/UTF8.cpp @@ -148,7 +148,7 @@ hélló Ḕ ḕ Ḗ ḗ Ḙ ḙ Ḛ -ἐ ἑ ἒ ἓ ἔ ἕ +ἐ ἑ ἒ ἓ ἔ ἕ ₠ ₡ ₢ ₣ ₤ ₥ @@ -164,9 +164,9 @@ hélló ␀ ␁ ␂ ␃ ␄ ␅ -⑀ ⑁ ⑂ ⑃ ⑄ +⑀ ⑁ ⑂ ⑃ ⑄ -① ② ③ ④ ⑤ +① ② ③ ④ ⑤ ╘ ╙ ╚ ╛ ╜ ╝ @@ -200,7 +200,7 @@ hélló שּׁ שּׂ אַ אָ אּ -ﮄ ﮅ ﮆ ﮇ ﮈ ﮉ +ﮄ ﮅ ﮆ ﮇ ﮈ ﮉ ﺵ ﺶ ﺷ ﺸ diff --git a/test/libevmasm/Assembler.cpp b/test/libevmasm/Assembler.cpp new file mode 100644 index 00000000..1c041596 --- /dev/null +++ b/test/libevmasm/Assembler.cpp @@ -0,0 +1,152 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see <http://www.gnu.org/licenses/>. +*/ +/** + * @author Alex Beregszaszi + * @date 2018 + * Tests for the assembler. + */ + +#include <libdevcore/JSON.h> +#include <libevmasm/Assembly.h> + +#include <boost/test/unit_test.hpp> + +#include <string> +#include <tuple> +#include <memory> + +using namespace std; +using namespace dev::eth; + +namespace dev +{ +namespace solidity +{ +namespace test +{ + +namespace +{ + void checkCompilation(::dev::eth::Assembly const& _assembly) + { + LinkerObject output = _assembly.assemble(); + BOOST_CHECK(output.bytecode.size() > 0); + BOOST_CHECK(output.toHex().length() > 0); + } +} + +BOOST_AUTO_TEST_SUITE(Assembler) + +BOOST_AUTO_TEST_CASE(all_assembly_items) +{ + Assembly _assembly; + _assembly.setSourceLocation(SourceLocation(1, 3, make_shared<string>("root.asm"))); + + Assembly _subAsm; + _subAsm.setSourceLocation(SourceLocation(6, 8, make_shared<string>("sub.asm"))); + _subAsm.append(Instruction::INVALID); + shared_ptr<Assembly> _subAsmPtr = make_shared<Assembly>(_subAsm); + + // Tag + auto tag = _assembly.newTag(); + _assembly.append(tag); + // Operation + _assembly.append(u256(1)); + _assembly.append(u256(2)); + // Push + _assembly.append(Instruction::KECCAK256); + // PushProgramSize + _assembly.appendProgramSize(); + // PushLibraryAddress + _assembly.appendLibraryAddress("someLibrary"); + // PushTag + Operation + _assembly.appendJump(tag); + // PushString + _assembly.append("Unused feature for pushing string"); + // PushData + _assembly.append(bytes{0x1, 0x2, 0x3, 0x4}); + // PushSubSize + auto sub = _assembly.appendSubroutine(_subAsmPtr); + // PushSub + _assembly.pushSubroutineOffset(size_t(sub.data())); + // PushDeployTimeAddress + _assembly.append(PushDeployTimeAddress); + // Operation + _assembly.append(Instruction::STOP); + _assembly.appendAuxiliaryDataToEnd(bytes{0x42, 0x66}); + _assembly.appendAuxiliaryDataToEnd(bytes{0xee, 0xaa}); + + checkCompilation(_assembly); + + BOOST_CHECK_EQUAL( + _assembly.assemble().toHex(), + "5b6001600220606773__$bf005014d9d0f534b8fcb268bd84c491a2$__" + "6000567f556e75736564206665617475726520666f722070757368696e" + "6720737472696e605f6001605e73000000000000000000000000000000000000000000fe" + "fe010203044266eeaa" + ); + BOOST_CHECK_EQUAL( + _assembly.assemblyString(), + " /* \"root.asm\":1:3 */\n" + "tag_1:\n" + " keccak256(0x2, 0x1)\n" + " bytecodeSize\n" + " linkerSymbol(\"bf005014d9d0f534b8fcb268bd84c491a2380f4acd260d1ccfe9cd8201f7e994\")\n" + " jump(tag_1)\n" + " data_027497964124140851e8a9992ba16b5c1aaf9730b78d6036c8d65e3bb5ea4c8f\n" + " data_a6885b3731702da62e8e4a8f584ac46a7f6822f4e2ba50fba902f67b1588d23b\n" + " dataSize(sub_0)\n" + " dataOffset(sub_0)\n" + " deployTimeAddress()\n" + " stop\n" + "stop\n" + "data_a6885b3731702da62e8e4a8f584ac46a7f6822f4e2ba50fba902f67b1588d23b 01020304\n" + "\n" + "sub_0: assembly {\n" + " /* \"sub.asm\":6:8 */\n" + " invalid\n" + "}\n" + "\n" + "auxdata: 0x4266eeaa\n" + ); + BOOST_CHECK_EQUAL( + dev::jsonCompactPrint(_assembly.assemblyJSON()), + "{\".auxdata\":\"4266eeaa\",\".code\":[{\"begin\":1,\"end\":3,\"name\":\"tag\",\"value\":\"1\"}," + "{\"begin\":1,\"end\":3,\"name\":\"JUMPDEST\"}," + "{\"begin\":1,\"end\":3,\"name\":\"PUSH\",\"value\":\"1\"}," + "{\"begin\":1,\"end\":3,\"name\":\"PUSH\",\"value\":\"2\"}," + "{\"begin\":1,\"end\":3,\"name\":\"KECCAK256\"}," + "{\"begin\":1,\"end\":3,\"name\":\"PUSHSIZE\"}," + "{\"begin\":1,\"end\":3,\"name\":\"PUSHLIB\",\"value\":\"someLibrary\"}," + "{\"begin\":1,\"end\":3,\"name\":\"PUSH [tag]\",\"value\":\"1\"}," + "{\"begin\":1,\"end\":3,\"name\":\"JUMP\"}," + "{\"begin\":1,\"end\":3,\"name\":\"PUSH tag\",\"value\":\"Unused feature for pushing string\"}," + "{\"begin\":1,\"end\":3,\"name\":\"PUSH data\",\"value\":\"A6885B3731702DA62E8E4A8F584AC46A7F6822F4E2BA50FBA902F67B1588D23B\"}," + "{\"begin\":1,\"end\":3,\"name\":\"PUSH #[$]\",\"value\":\"0000000000000000000000000000000000000000000000000000000000000000\"}," + "{\"begin\":1,\"end\":3,\"name\":\"PUSH [$]\",\"value\":\"0000000000000000000000000000000000000000000000000000000000000000\"}," + "{\"begin\":1,\"end\":3,\"name\":\"PUSHDEPLOYADDRESS\"}," + "{\"begin\":1,\"end\":3,\"name\":\"STOP\"}]," + "\".data\":{\"0\":{\".code\":[{\"begin\":6,\"end\":8,\"name\":\"INVALID\"}]}," + "\"A6885B3731702DA62E8E4A8F584AC46A7F6822F4E2BA50FBA902F67B1588D23B\":\"01020304\"}}" + ); +} + +BOOST_AUTO_TEST_SUITE_END() + +} +} +} // end namespaces diff --git a/test/libevmasm/Optimiser.cpp b/test/libevmasm/Optimiser.cpp index 4b399a14..c01e8758 100644 --- a/test/libevmasm/Optimiser.cpp +++ b/test/libevmasm/Optimiser.cpp @@ -30,7 +30,6 @@ #include <libevmasm/Assembly.h> #include <boost/test/unit_test.hpp> -#include <boost/lexical_cast.hpp> #include <string> #include <tuple> @@ -967,6 +966,31 @@ BOOST_AUTO_TEST_CASE(peephole_swap_comparison) } } +BOOST_AUTO_TEST_CASE(peephole_truthy_and) +{ + AssemblyItems items{ + AssemblyItem(Tag, 1), + Instruction::BALANCE, + u256(0), + Instruction::NOT, + Instruction::AND, + AssemblyItem(PushTag, 1), + Instruction::JUMPI + }; + AssemblyItems expectation{ + AssemblyItem(Tag, 1), + Instruction::BALANCE, + AssemblyItem(PushTag, 1), + Instruction::JUMPI + }; + PeepholeOptimiser peepOpt(items); + BOOST_REQUIRE(peepOpt.optimise()); + BOOST_CHECK_EQUAL_COLLECTIONS( + items.begin(), items.end(), + expectation.begin(), expectation.end() + ); +} + BOOST_AUTO_TEST_CASE(jumpdest_removal) { AssemblyItems items{ diff --git a/test/libjulia/Common.cpp b/test/libjulia/Common.cpp deleted file mode 100644 index 24519b01..00000000 --- a/test/libjulia/Common.cpp +++ /dev/null @@ -1,95 +0,0 @@ -/* - This file is part of solidity. - - solidity is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - solidity is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with solidity. If not, see <http://www.gnu.org/licenses/>. -*/ -/** - * @date 2017 - * Common functions the iulia tests. - */ - -#include <test/libjulia/Common.h> - -#include <test/Options.h> - -#include <libjulia/optimiser/Disambiguator.h> - -#include <libsolidity/parsing/Scanner.h> - -#include <libsolidity/inlineasm/AsmParser.h> -#include <libsolidity/inlineasm/AsmAnalysis.h> -#include <libsolidity/inlineasm/AsmPrinter.h> - -#include <libsolidity/interface/SourceReferenceFormatter.h> -#include <libsolidity/interface/ErrorReporter.h> - -#include <boost/test/unit_test.hpp> - -using namespace std; -using namespace dev::julia; -using namespace dev::solidity; - -void dev::julia::test::printErrors(ErrorList const& _errors, Scanner const& _scanner) -{ - SourceReferenceFormatter formatter(cout, [&](std::string const&) -> Scanner const& { return _scanner; }); - - for (auto const& error: _errors) - formatter.printExceptionInformation( - *error, - (error->type() == Error::Type::Warning) ? "Warning" : "Error" - ); -} - - -pair<shared_ptr<Block>, shared_ptr<assembly::AsmAnalysisInfo>> dev::julia::test::parse(string const& _source, bool _julia) -{ - auto flavour = _julia ? assembly::AsmFlavour::IULIA : assembly::AsmFlavour::Strict; - ErrorList errors; - ErrorReporter errorReporter(errors); - auto scanner = make_shared<Scanner>(CharStream(_source), ""); - auto parserResult = assembly::Parser(errorReporter, flavour).parse(scanner, false); - if (parserResult) - { - BOOST_REQUIRE(errorReporter.errors().empty()); - auto analysisInfo = make_shared<assembly::AsmAnalysisInfo>(); - assembly::AsmAnalyzer analyzer( - *analysisInfo, - errorReporter, - dev::test::Options::get().evmVersion(), - boost::none, - flavour - ); - if (analyzer.analyze(*parserResult)) - { - BOOST_REQUIRE(errorReporter.errors().empty()); - return make_pair(parserResult, analysisInfo); - } - } - printErrors(errors, *scanner); - BOOST_FAIL("Invalid source."); - - // Unreachable. - return {}; -} - -assembly::Block dev::julia::test::disambiguate(string const& _source, bool _julia) -{ - auto result = parse(_source, _julia); - return boost::get<Block>(Disambiguator(*result.second)(*result.first)); -} - -string dev::julia::test::format(string const& _source, bool _julia) -{ - return assembly::AsmPrinter(_julia)(*parse(_source, _julia).first); -} diff --git a/test/libjulia/Common.h b/test/libjulia/Common.h deleted file mode 100644 index 1371101c..00000000 --- a/test/libjulia/Common.h +++ /dev/null @@ -1,55 +0,0 @@ -/* - This file is part of solidity. - - solidity is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - solidity is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with solidity. If not, see <http://www.gnu.org/licenses/>. -*/ -/** - * @date 2017 - * Common functions the iulia tests. - */ - -#pragma once - -#include <libsolidity/inlineasm/AsmData.h> - -#include <string> -#include <vector> -#include <memory> - -namespace dev -{ -namespace solidity -{ -class Scanner; -class Error; -using ErrorList = std::vector<std::shared_ptr<Error const>>; -namespace assembly -{ -struct AsmAnalysisInfo; -} -} -namespace julia -{ -namespace test -{ - -void printErrors(solidity::ErrorList const& _errors, solidity::Scanner const& _scanner); -std::pair<std::shared_ptr<solidity::assembly::Block>, std::shared_ptr<solidity::assembly::AsmAnalysisInfo>> -parse(std::string const& _source, bool _julia = true); -solidity::assembly::Block disambiguate(std::string const& _source, bool _julia = true); -std::string format(std::string const& _source, bool _julia = true); - -} -} -} diff --git a/test/libjulia/CommonSubexpression.cpp b/test/libjulia/CommonSubexpression.cpp deleted file mode 100644 index 8a575c48..00000000 --- a/test/libjulia/CommonSubexpression.cpp +++ /dev/null @@ -1,102 +0,0 @@ -/* - This file is part of solidity. - - solidity is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - solidity is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with solidity. If not, see <http://www.gnu.org/licenses/>. -*/ -/** - * Unit tests for the common subexpression eliminator optimizer stage. - */ - -#include <test/libjulia/Common.h> - -#include <libjulia/optimiser/CommonSubexpressionEliminator.h> - -#include <libsolidity/inlineasm/AsmPrinter.h> - -#include <boost/test/unit_test.hpp> - -#include <boost/range/adaptors.hpp> -#include <boost/algorithm/string/join.hpp> - -using namespace std; -using namespace dev; -using namespace dev::julia; -using namespace dev::julia::test; -using namespace dev::solidity; - - -#define CHECK(_original, _expectation)\ -do\ -{\ - assembly::AsmPrinter p;\ - Block b = disambiguate(_original, false);\ - (CommonSubexpressionEliminator{})(b);\ - string result = p(b);\ - BOOST_CHECK_EQUAL(result, format(_expectation, false));\ -}\ -while(false) - -BOOST_AUTO_TEST_SUITE(IuliaCSE) - -BOOST_AUTO_TEST_CASE(smoke_test) -{ - CHECK("{ }", "{ }"); -} - -BOOST_AUTO_TEST_CASE(trivial) -{ - CHECK( - "{ let a := mul(1, codesize()) let b := mul(1, codesize()) }", - "{ let a := mul(1, codesize()) let b := a }" - ); -} - -BOOST_AUTO_TEST_CASE(non_movable_instr) -{ - CHECK( - "{ let a := mload(1) let b := mload(1) }", - "{ let a := mload(1) let b := mload(1) }" - ); -} - -BOOST_AUTO_TEST_CASE(non_movable_instr2) -{ - CHECK( - "{ let a := gas() let b := gas() }", - "{ let a := gas() let b := gas() }" - ); -} - -BOOST_AUTO_TEST_CASE(branches_if) -{ - CHECK( - "{ let b := 1 if b { b := 1 } let c := 1 }", - "{ let b := 1 if b { b := b } let c := 1 }" - ); -} - -BOOST_AUTO_TEST_CASE(branches_for) -{ - CHECK( - "{ let a := 1 let b := codesize()" - "for { } lt(1, codesize()) { mstore(1, codesize()) a := add(a, codesize()) }" - "{ mstore(1, codesize()) } mstore(1, codesize()) }", - - "{ let a := 1 let b := codesize()" - "for { } lt(1, b) { mstore(1, b) a := add(a, b) }" - "{ mstore(1, b) } mstore(1, b) }" - ); -} - -BOOST_AUTO_TEST_SUITE_END() diff --git a/test/libjulia/Disambiguator.cpp b/test/libjulia/Disambiguator.cpp deleted file mode 100644 index a6338449..00000000 --- a/test/libjulia/Disambiguator.cpp +++ /dev/null @@ -1,105 +0,0 @@ -/* - This file is part of solidity. - - solidity is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - solidity is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with solidity. If not, see <http://www.gnu.org/licenses/>. -*/ -/** - * @date 2017 - * Unit tests for the iulia name disambiguator. - */ - -#include <test/libjulia/Common.h> - -#include <libsolidity/inlineasm/AsmPrinter.h> - -#include <boost/test/unit_test.hpp> - -using namespace std; -using namespace dev::julia::test; -using namespace dev::solidity; - -#define CHECK(_original, _expectation)\ -do\ -{\ - assembly::AsmPrinter p(true);\ - string result = p(disambiguate(_original));\ - BOOST_CHECK_EQUAL(result, format(_expectation));\ - BOOST_CHECK_EQUAL(result, p(disambiguate(result)));\ -}\ -while(false) - -BOOST_AUTO_TEST_SUITE(IuliaDisambiguator) - -BOOST_AUTO_TEST_CASE(smoke_test) -{ - CHECK("{ }", "{ }"); -} - -BOOST_AUTO_TEST_CASE(variables) -{ - CHECK( - "{ { let a:u256 } { let a:u256 } }", - "{ { let a:u256 } { let a_1:u256 } }" - ); -} - -BOOST_AUTO_TEST_CASE(variables_clash) -{ - CHECK( - "{ { let a:u256 let a_1:u256 } { let a:u256 } }", - "{ { let a:u256 let a_1:u256 } { let a_2:u256 } }" - ); -} - -BOOST_AUTO_TEST_CASE(variables_inside_functions) -{ - CHECK( - "{ { let c:u256 let b:u256 } function f(a:u256, c:u256) -> b:u256 { let x:u256 } { let a:u256 let x:u256 } }", - "{ { let c:u256 let b:u256 } function f(a:u256, c_1:u256) -> b_1:u256 { let x:u256 } { let a_1:u256 let x_1:u256 } }" - ); -} - -BOOST_AUTO_TEST_CASE(function_call) -{ - CHECK( - "{ { let a:u256, b:u256, c:u256, d:u256, f:u256 } { function f(a:u256) -> c:u256, d:u256 { let b:u256, c_1:u256 := f(a) } } }", - "{ { let a:u256, b:u256, c:u256, d:u256, f:u256 } { function f_1(a_1:u256) -> c_1:u256, d_1:u256 { let b_1:u256, c_1_1:u256 := f_1(a_1) } } }" - ); -} - -BOOST_AUTO_TEST_CASE(for_statement) -{ - CHECK( - "{ { let a:u256, b:u256 } { for { let a:u256 } a { a := a } { let b:u256 := a } } }", - "{ { let a:u256, b:u256 } { for { let a_1:u256 } a_1 { a_1 := a_1 } { let b_1:u256 := a_1 } } }" - ); -} - -BOOST_AUTO_TEST_CASE(switch_statement) -{ - CHECK( - "{ { let a:u256, b:u256, c:u256 } { let a:u256 switch a case 0:u256 { let b:u256 := a } default { let c:u256 := a } } }", - "{ { let a:u256, b:u256, c:u256 } { let a_1:u256 switch a_1 case 0:u256 { let b_1:u256 := a_1 } default { let c_1:u256 := a_1 } } }" - ); -} - -BOOST_AUTO_TEST_CASE(if_statement) -{ - CHECK( - "{ { let a:u256, b:u256, c:u256 } { let a:bool if a { let b:bool := a } } }", - "{ { let a:u256, b:u256, c:u256 } { let a_1:bool if a_1 { let b_1:bool := a_1 } } }" - ); -} - -BOOST_AUTO_TEST_SUITE_END() diff --git a/test/libjulia/FunctionGrouper.cpp b/test/libjulia/FunctionGrouper.cpp deleted file mode 100644 index 78f382cb..00000000 --- a/test/libjulia/FunctionGrouper.cpp +++ /dev/null @@ -1,85 +0,0 @@ -/* - This file is part of solidity. - - solidity is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - solidity is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with solidity. If not, see <http://www.gnu.org/licenses/>. -*/ -/** - * @date 2017 - * Unit tests for the iulia function grouper. - */ - -#include <test/libjulia/Common.h> - -#include <libjulia/optimiser/FunctionGrouper.h> - -#include <libsolidity/inlineasm/AsmPrinter.h> - -#include <boost/test/unit_test.hpp> - -using namespace std; -using namespace dev::julia; -using namespace dev::julia::test; -using namespace dev::solidity; - -#define CHECK(_original, _expectation)\ -do\ -{\ - assembly::AsmPrinter p(true);\ - Block b = disambiguate(_original);\ - (FunctionGrouper{})(b);\ - string result = p(b);\ - BOOST_CHECK_EQUAL(result, format(_expectation));\ -}\ -while(false) - -BOOST_AUTO_TEST_SUITE(IuliaFunctionGrouper) - -BOOST_AUTO_TEST_CASE(smoke_test) -{ - CHECK("{ }", "{ { } }"); -} - -BOOST_AUTO_TEST_CASE(single_fun) -{ - CHECK( - "{ let a:u256 function f() {} }", - "{ { let a:u256 } function f() {} }" - ); -} - -BOOST_AUTO_TEST_CASE(multi_fun_mixed) -{ - CHECK( - "{ let a:u256 function f() { let b:u256 } let c:u256 function g() { let d:u256 } let e:u256 }", - "{ { let a:u256 let c:u256 let e:u256 } function f() { let b:u256 } function g() { let d:u256 } }" - ); -} - -BOOST_AUTO_TEST_CASE(nested_fun) -{ - CHECK( - "{ let a:u256 function f() { let b:u256 function g() { let c:u256} let d:u256 } }", - "{ { let a:u256 } function f() { let b:u256 function g() { let c:u256} let d:u256 } }" - ); -} - -BOOST_AUTO_TEST_CASE(empty_block) -{ - CHECK( - "{ let a:u256 { } function f() -> x:bool { let b:u256 := 4:u256 {} for {} f() {} {} } }", - "{ { let a:u256 { } } function f() -> x:bool { let b:u256 := 4:u256 {} for {} f() {} {} } }" - ); -} - -BOOST_AUTO_TEST_SUITE_END() diff --git a/test/libjulia/FunctionHoister.cpp b/test/libjulia/FunctionHoister.cpp deleted file mode 100644 index 3d6fff85..00000000 --- a/test/libjulia/FunctionHoister.cpp +++ /dev/null @@ -1,85 +0,0 @@ -/* - This file is part of solidity. - - solidity is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - solidity is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with solidity. If not, see <http://www.gnu.org/licenses/>. -*/ -/** - * @date 2017 - * Unit tests for the iulia function hoister. - */ - -#include <test/libjulia/Common.h> - -#include <libjulia/optimiser/FunctionHoister.h> - -#include <libsolidity/inlineasm/AsmPrinter.h> - -#include <boost/test/unit_test.hpp> - -using namespace std; -using namespace dev::julia; -using namespace dev::julia::test; -using namespace dev::solidity; - -#define CHECK(_original, _expectation)\ -do\ -{\ - assembly::AsmPrinter p(true);\ - Block b = disambiguate(_original);\ - (FunctionHoister{})(b);\ - string result = p(b);\ - BOOST_CHECK_EQUAL(result, format(_expectation));\ -}\ -while(false) - -BOOST_AUTO_TEST_SUITE(IuliaFunctionHoister) - -BOOST_AUTO_TEST_CASE(smoke_test) -{ - CHECK("{ }", "{ }"); -} - -BOOST_AUTO_TEST_CASE(single_fun) -{ - CHECK( - "{ let a:u256 function f() {} }", - "{ let a:u256 function f() {} }" - ); -} - -BOOST_AUTO_TEST_CASE(multi_fun_mixed) -{ - CHECK( - "{ let a:u256 function f() { let b:u256 } let c:u256 function g() { let d:u256 } let e:u256 }", - "{ let a:u256 let c:u256 let e:u256 function f() { let b:u256 } function g() { let d:u256 } }" - ); -} - -BOOST_AUTO_TEST_CASE(nested_fun) -{ - CHECK( - "{ let a:u256 function f() { let b:u256 function g() { let c:u256} let d:u256 } }", - "{ let a:u256 function g() { let c:u256 } function f() { let b:u256 let d:u256 } }" - ); -} - -BOOST_AUTO_TEST_CASE(empty_block) -{ - CHECK( - "{ let a:u256 { } function f() -> x:bool { let b:u256 := 4:u256 {} for {} f() {} {} } }", - "{ let a:u256 function f() -> x:bool { let b:u256 := 4:u256 for {} f() {} {} } }" - ); -} - -BOOST_AUTO_TEST_SUITE_END() diff --git a/test/libjulia/Inliner.cpp b/test/libjulia/Inliner.cpp deleted file mode 100644 index 464dcd93..00000000 --- a/test/libjulia/Inliner.cpp +++ /dev/null @@ -1,343 +0,0 @@ -/* - This file is part of solidity. - - solidity is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - solidity is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with solidity. If not, see <http://www.gnu.org/licenses/>. -*/ -/** - * @date 2017 - * Unit tests for the iulia function inliner. - */ - -#include <test/libjulia/Common.h> - -#include <libjulia/optimiser/ExpressionInliner.h> -#include <libjulia/optimiser/InlinableExpressionFunctionFinder.h> -#include <libjulia/optimiser/FullInliner.h> -#include <libjulia/optimiser/FunctionHoister.h> -#include <libjulia/optimiser/FunctionGrouper.h> - -#include <libsolidity/inlineasm/AsmPrinter.h> - -#include <boost/test/unit_test.hpp> - -#include <boost/range/adaptors.hpp> -#include <boost/algorithm/string/join.hpp> - -using namespace std; -using namespace dev; -using namespace dev::julia; -using namespace dev::julia::test; -using namespace dev::solidity; - -namespace -{ -string inlinableFunctions(string const& _source) -{ - auto ast = disambiguate(_source); - - InlinableExpressionFunctionFinder funFinder; - funFinder(ast); - - return boost::algorithm::join( - funFinder.inlinableFunctions() | boost::adaptors::map_keys, - "," - ); -} - -string inlineFunctions(string const& _source, bool _julia = true) -{ - auto ast = disambiguate(_source, _julia); - ExpressionInliner(ast).run(); - return assembly::AsmPrinter(_julia)(ast); -} -string fullInline(string const& _source, bool _julia = true) -{ - Block ast = disambiguate(_source, _julia); - (FunctionHoister{})(ast); - (FunctionGrouper{})(ast);\ - FullInliner(ast).run(); - return assembly::AsmPrinter(_julia)(ast); -} -} - - -BOOST_AUTO_TEST_SUITE(IuliaInlinableFunctionFilter) - -BOOST_AUTO_TEST_CASE(smoke_test) -{ - BOOST_CHECK_EQUAL(inlinableFunctions("{ }"), ""); -} - -BOOST_AUTO_TEST_CASE(simple) -{ - BOOST_CHECK_EQUAL(inlinableFunctions("{ function f() -> x:u256 { x := 2:u256 } }"), "f"); - BOOST_CHECK_EQUAL(inlinableFunctions("{" - "function g(a:u256) -> b:u256 { b := a }" - "function f() -> x:u256 { x := g(2:u256) }" - "}"), "f,g"); -} - -BOOST_AUTO_TEST_CASE(simple_inside_structures) -{ - BOOST_CHECK_EQUAL(inlinableFunctions("{" - "switch 2:u256 " - "case 2:u256 {" - "function g(a:u256) -> b:u256 { b := a }" - "function f() -> x:u256 { x := g(2:u256) }" - "}" - "}"), "f,g"); - BOOST_CHECK_EQUAL(inlinableFunctions("{" - "for {" - "function g(a:u256) -> b:u256 { b := a }" - "} 1:u256 {" - "function f() -> x:u256 { x := g(2:u256) }" - "}" - "{" - "function h() -> y:u256 { y := 2:u256 }" - "}" - "}"), "f,g,h"); -} - -BOOST_AUTO_TEST_CASE(negative) -{ - BOOST_CHECK_EQUAL(inlinableFunctions("{ function f() -> x:u256 { } }"), ""); - BOOST_CHECK_EQUAL(inlinableFunctions("{ function f() -> x:u256 { x := 2:u256 {} } }"), ""); - BOOST_CHECK_EQUAL(inlinableFunctions("{ function f() -> x:u256 { x := f() } }"), ""); - BOOST_CHECK_EQUAL(inlinableFunctions("{ function f() -> x:u256 { x := x } }"), ""); - BOOST_CHECK_EQUAL(inlinableFunctions("{ function f() -> x:u256, y:u256 { x := 2:u256 } }"), ""); -} - - -BOOST_AUTO_TEST_SUITE_END() - -BOOST_AUTO_TEST_SUITE(IuliaFunctionInliner) - -BOOST_AUTO_TEST_CASE(simple) -{ - BOOST_CHECK_EQUAL( - inlineFunctions("{ function f() -> x:u256 { x := 2:u256 } let y:u256 := f() }"), - format("{ function f() -> x:u256 { x := 2:u256 } let y:u256 := 2:u256 }") - ); -} - -BOOST_AUTO_TEST_CASE(with_args) -{ - BOOST_CHECK_EQUAL( - inlineFunctions("{ function f(a:u256) -> x:u256 { x := a } let y:u256 := f(7:u256) }"), - format("{ function f(a:u256) -> x:u256 { x := a } let y:u256 := 7:u256 }") - ); -} - -BOOST_AUTO_TEST_CASE(no_inline_with_mload) -{ - // Does not inline because mload could be moved out of sequence - BOOST_CHECK_EQUAL( - inlineFunctions("{ function f(a) -> x { x := a } let y := f(mload(2)) }", false), - format("{ function f(a) -> x { x := a } let y := f(mload(2)) }", false) - ); -} - -BOOST_AUTO_TEST_CASE(no_move_with_side_effects) -{ - // The calls to g and h cannot be moved because g and h are not movable. Therefore, the call - // to f is not inlined. - BOOST_CHECK_EQUAL( - inlineFunctions("{" - "function f(a, b) -> x { x := add(b, a) }" - "function g() -> y { y := mload(0) mstore(0, 4) }" - "function h() -> z { mstore(0, 4) z := mload(0) }" - "let r := f(g(), h())" - "}", false), - format("{" - "function f(a, b) -> x { x := add(b, a) }" - "function g() -> y { y := mload(0) mstore(0, 4) }" - "function h() -> z { mstore(0, 4) z := mload(0) }" - "let r := f(g(), h())" - "}", false) - ); -} - -BOOST_AUTO_TEST_CASE(complex_with_evm) -{ - BOOST_CHECK_EQUAL( - inlineFunctions("{ function f(a) -> x { x := add(a, a) } let y := f(calldatasize()) }", false), - format("{ function f(a) -> x { x := add(a, a) } let y := add(calldatasize(), calldatasize()) }", false) - ); -} - -BOOST_AUTO_TEST_CASE(double_calls) -{ - BOOST_CHECK_EQUAL( - inlineFunctions("{" - "function f(a) -> x { x := add(a, a) }" - "function g(b, c) -> y { y := mul(mload(c), f(b)) }" - "let y := g(calldatasize(), 7)" - "}", false), - format("{" - "function f(a) -> x { x := add(a, a) }" - "function g(b, c) -> y { y := mul(mload(c), add(b, b)) }" - "let y_1 := mul(mload(7), add(calldatasize(), calldatasize()))" - "}", false) - ); -} - -BOOST_AUTO_TEST_CASE(double_recursive_calls) -{ - BOOST_CHECK_EQUAL( - inlineFunctions("{" - "function f(a, r) -> x { x := g(a, g(r, r)) }" - "function g(b, s) -> y { y := f(b, f(s, s)) }" - "let y := g(calldatasize(), 7)" - "}", false), - format("{" - "function f(a, r) -> x { x := g(a, f(r, f(r, r))) }" - "function g(b, s) -> y { y := f(b, g(s, f(s, f(s, s))))}" - "let y_1 := f(calldatasize(), g(7, f(7, f(7, 7))))" - "}", false) - ); -} - -BOOST_AUTO_TEST_SUITE_END() - -BOOST_AUTO_TEST_SUITE(IuliaFullInliner) - -BOOST_AUTO_TEST_CASE(simple) -{ - BOOST_CHECK_EQUAL( - fullInline("{" - "function f(a) -> x { let r := mul(a, a) x := add(r, r) }" - "let y := add(f(sload(mload(2))), mload(7))" - "}", false), - format("{" - "{" - "let _1 := mload(7)" - "let f_a := sload(mload(2))" - "let f_x" - "{" - "let f_r := mul(f_a, f_a)" - "f_x := add(f_r, f_r)" - "}" - "let y := add(f_x, _1)" - "}" - "function f(a) -> x" - "{" - "let r := mul(a, a)" - "x := add(r, r)" - "}" - "}", false) - ); -} - -BOOST_AUTO_TEST_CASE(multi_fun) -{ - BOOST_CHECK_EQUAL( - fullInline("{" - "function f(a) -> x { x := add(a, a) }" - "function g(b, c) -> y { y := mul(mload(c), f(b)) }" - "let y := g(f(3), 7)" - "}", false), - format("{" - "{" - "let g_c := 7 " - "let f_a_1 := 3 " - "let f_x_1 " - "{ f_x_1 := add(f_a_1, f_a_1) } " - "let g_y " - "{" - "let g_f_a := f_x_1 " - "let g_f_x " - "{" - "g_f_x := add(g_f_a, g_f_a)" - "}" - "g_y := mul(mload(g_c), g_f_x)" - "}" - "let y_1 := g_y" - "}" - "function f(a) -> x" - "{" - "x := add(a, a)" - "}" - "function g(b, c) -> y" - "{" - "let f_a := b " - "let f_x " - "{" - "f_x := add(f_a, f_a)" - "}" - "y := mul(mload(c), f_x)" - "}" - "}", false) - ); -} - -BOOST_AUTO_TEST_CASE(move_up_rightwards_arguments) -{ - BOOST_CHECK_EQUAL( - fullInline("{" - "function f(a, b, c) -> x { x := add(a, b) x := mul(x, c) }" - "let y := add(mload(1), add(f(mload(2), mload(3), mload(4)), mload(5)))" - "}", false), - format("{" - "{" - "let _1 := mload(5)" - "let f_c := mload(4)" - "let f_b := mload(3)" - "let f_a := mload(2)" - "let f_x" - "{" - "f_x := add(f_a, f_b)" - "f_x := mul(f_x, f_c)" - "}" - "let y := add(mload(1), add(f_x, _1))" - "}" - "function f(a, b, c) -> x" - "{" - "x := add(a, b)" - "x := mul(x, c)" - "}" - "}", false) - ); -} - -BOOST_AUTO_TEST_CASE(pop_result) -{ - // This tests that `pop(r)` is removed. - BOOST_CHECK_EQUAL( - fullInline("{" - "function f(a) -> x { let r := mul(a, a) x := add(r, r) }" - "pop(add(f(7), 2))" - "}", false), - format("{" - "{" - "let _1 := 2 " - "let f_a := 7 " - "let f_x " - "{" - "let f_r := mul(f_a, f_a) " - "f_x := add(f_r, f_r)" - "}" - "{" - "}" - "}" - "function f(a) -> x" - "{" - "let r := mul(a, a) " - "x := add(r, r)" - "}" - "}", false) - ); -} - - -BOOST_AUTO_TEST_SUITE_END() diff --git a/test/libjulia/MainFunction.cpp b/test/libjulia/MainFunction.cpp deleted file mode 100644 index c26b002d..00000000 --- a/test/libjulia/MainFunction.cpp +++ /dev/null @@ -1,87 +0,0 @@ -/* - This file is part of solidity. - - solidity is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - solidity is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with solidity. If not, see <http://www.gnu.org/licenses/>. -*/ -/** - * @date 2018 - * Unit tests for the Julia MainFunction transformation. - */ - -#include <test/libjulia/Common.h> - -#include <libjulia/optimiser/FunctionGrouper.h> -#include <libjulia/optimiser/MainFunction.h> - -#include <libsolidity/inlineasm/AsmPrinter.h> - -#include <boost/test/unit_test.hpp> - -using namespace std; -using namespace dev::julia; -using namespace dev::julia::test; -using namespace dev::solidity; - -#define CHECK(_original, _expectation)\ -do\ -{\ - assembly::AsmPrinter p(true);\ - Block b = disambiguate(_original);\ - (FunctionGrouper{})(b);\ - (MainFunction{})(b);\ - string result = p(b);\ - BOOST_CHECK_EQUAL(result, format(_expectation));\ -}\ -while(false) - -BOOST_AUTO_TEST_SUITE(JuliaMainFunction) - -BOOST_AUTO_TEST_CASE(smoke_test) -{ - CHECK("{ }", "{ function main() { } }"); -} - -BOOST_AUTO_TEST_CASE(single_fun) -{ - CHECK( - "{ let a:u256 function f() {} }", - "{ function main() { let a:u256 } function f() {} }" - ); -} - -BOOST_AUTO_TEST_CASE(multi_fun_mixed) -{ - CHECK( - "{ let a:u256 function f() { let b:u256 } let c:u256 function g() { let d:u256 } let e:u256 }", - "{ function main() { let a:u256 let c:u256 let e:u256 } function f() { let b:u256 } function g() { let d:u256 } }" - ); -} - -BOOST_AUTO_TEST_CASE(nested_fun) -{ - CHECK( - "{ let a:u256 function f() { let b:u256 function g() { let c:u256} let d:u256 } }", - "{ function main() { let a:u256 } function f() { let b:u256 function g() { let c:u256} let d:u256 } }" - ); -} - -BOOST_AUTO_TEST_CASE(empty_block) -{ - CHECK( - "{ let a:u256 { } function f() -> x:bool { let b:u256 := 4:u256 {} for {} f() {} {} } }", - "{ function main() { let a:u256 { } } function f() -> x:bool { let b:u256 := 4:u256 {} for {} f() {} {} } }" - ); -} - -BOOST_AUTO_TEST_SUITE_END() diff --git a/test/libjulia/Parser.cpp b/test/libjulia/Parser.cpp deleted file mode 100644 index 96261dec..00000000 --- a/test/libjulia/Parser.cpp +++ /dev/null @@ -1,306 +0,0 @@ -/* - This file is part of solidity. - - solidity is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - solidity is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with solidity. If not, see <http://www.gnu.org/licenses/>. -*/ -/** - * @date 2017 - * Unit tests for parsing Julia. - */ - -#include <test/Options.h> - -#include <test/libsolidity/ErrorCheck.h> - -#include <libsolidity/inlineasm/AsmParser.h> -#include <libsolidity/inlineasm/AsmAnalysis.h> -#include <libsolidity/inlineasm/AsmAnalysisInfo.h> -#include <libsolidity/parsing/Scanner.h> -#include <libsolidity/interface/ErrorReporter.h> - -#include <boost/optional.hpp> -#include <boost/algorithm/string/replace.hpp> - -#include <string> -#include <memory> - -using namespace std; - -namespace dev -{ -namespace solidity -{ -namespace test -{ - -namespace -{ - -bool parse(string const& _source, ErrorReporter& errorReporter) -{ - try - { - auto scanner = make_shared<Scanner>(CharStream(_source)); - auto parserResult = assembly::Parser(errorReporter, assembly::AsmFlavour::IULIA).parse(scanner, false); - if (parserResult) - { - assembly::AsmAnalysisInfo analysisInfo; - return (assembly::AsmAnalyzer( - analysisInfo, - errorReporter, - dev::test::Options::get().evmVersion(), - boost::none, - assembly::AsmFlavour::IULIA - )).analyze(*parserResult); - } - } - catch (FatalError const&) - { - BOOST_FAIL("Fatal error leaked."); - } - return false; -} - -boost::optional<Error> parseAndReturnFirstError(string const& _source, bool _allowWarnings = true) -{ - ErrorList errors; - ErrorReporter errorReporter(errors); - if (!parse(_source, errorReporter)) - { - BOOST_REQUIRE_EQUAL(errors.size(), 1); - return *errors.front(); - } - else - { - // If success is true, there might still be an error in the assembly stage. - if (_allowWarnings && Error::containsOnlyWarnings(errors)) - return {}; - else if (!errors.empty()) - { - if (!_allowWarnings) - BOOST_CHECK_EQUAL(errors.size(), 1); - return *errors.front(); - } - } - return {}; -} - -bool successParse(std::string const& _source, bool _allowWarnings = true) -{ - return !parseAndReturnFirstError(_source, _allowWarnings); -} - -Error expectError(std::string const& _source, bool _allowWarnings = false) -{ - - auto error = parseAndReturnFirstError(_source, _allowWarnings); - BOOST_REQUIRE(error); - return *error; -} - -} - -#define CHECK_ERROR(text, typ, substring) \ -do \ -{ \ - Error err = expectError((text), false); \ - BOOST_CHECK(err.type() == (Error::Type::typ)); \ - BOOST_CHECK(searchErrorMessage(err, (substring))); \ -} while(0) - -BOOST_AUTO_TEST_SUITE(JuliaParser) - -BOOST_AUTO_TEST_CASE(smoke_test) -{ - BOOST_CHECK(successParse("{ }")); -} - -BOOST_AUTO_TEST_CASE(vardecl) -{ - BOOST_CHECK(successParse("{ let x:u256 := 7:u256 }")); -} - -BOOST_AUTO_TEST_CASE(vardecl_bool) -{ - BOOST_CHECK(successParse("{ let x:bool := true:bool }")); - BOOST_CHECK(successParse("{ let x:bool := false:bool }")); -} - -BOOST_AUTO_TEST_CASE(vardecl_empty) -{ - BOOST_CHECK(successParse("{ let x:u256 }")); -} - -BOOST_AUTO_TEST_CASE(assignment) -{ - BOOST_CHECK(successParse("{ let x:u256 := 2:u256 let y:u256 := x }")); -} - -BOOST_AUTO_TEST_CASE(vardecl_complex) -{ - BOOST_CHECK(successParse("{ function add(a:u256, b:u256) -> c:u256 {} let y:u256 := 2:u256 let x:u256 := add(7:u256, add(6:u256, y)) }")); -} - -BOOST_AUTO_TEST_CASE(blocks) -{ - BOOST_CHECK(successParse("{ let x:u256 := 7:u256 { let y:u256 := 3:u256 } { let z:u256 := 2:u256 } }")); -} - -BOOST_AUTO_TEST_CASE(function_definitions) -{ - BOOST_CHECK(successParse("{ function f() { } function g(a:u256) -> x:u256 { } }")); -} - -BOOST_AUTO_TEST_CASE(function_definitions_multiple_args) -{ - BOOST_CHECK(successParse("{ function f(a:u256, d:u256) { } function g(a:u256, d:u256) -> x:u256, y:u256 { } }")); -} - -BOOST_AUTO_TEST_CASE(function_calls) -{ - BOOST_CHECK(successParse("{ function f(a:u256) -> b:u256 {} function g(a:u256, b:u256, c:u256) {} function x() { g(1:u256, 2:u256, f(3:u256)) x() } }")); -} - -BOOST_AUTO_TEST_CASE(tuple_assignment) -{ - BOOST_CHECK(successParse("{ function f() -> a:u256, b:u256, c:u256 {} let x:u256, y:u256, z:u256 := f() }")); -} - -BOOST_AUTO_TEST_CASE(label) -{ - CHECK_ERROR("{ label: }", ParserError, "Labels are not supported."); -} - -BOOST_AUTO_TEST_CASE(instructions) -{ - CHECK_ERROR("{ pop }", ParserError, "Call or assignment expected."); -} - -BOOST_AUTO_TEST_CASE(push) -{ - CHECK_ERROR("{ 0x42:u256 }", ParserError, "Call or assignment expected."); -} - -BOOST_AUTO_TEST_CASE(assign_from_stack) -{ - CHECK_ERROR("{ =: x:u256 }", ParserError, "Literal or identifier expected."); -} - -BOOST_AUTO_TEST_CASE(empty_call) -{ - CHECK_ERROR("{ () }", ParserError, "Literal or identifier expected."); -} - -BOOST_AUTO_TEST_CASE(tokens_as_identifers) -{ - BOOST_CHECK(successParse("{ let return:u256 := 1:u256 }")); - BOOST_CHECK(successParse("{ let byte:u256 := 1:u256 }")); - BOOST_CHECK(successParse("{ let address:u256 := 1:u256 }")); - BOOST_CHECK(successParse("{ let bool:u256 := 1:u256 }")); -} - -BOOST_AUTO_TEST_CASE(lacking_types) -{ - CHECK_ERROR("{ let x := 1:u256 }", ParserError, "Expected identifier but got '='"); - CHECK_ERROR("{ let x:u256 := 1 }", ParserError, "Expected ':' but got '}'"); - CHECK_ERROR("{ function f(a) {} }", ParserError, "Expected ':' but got ')'"); - CHECK_ERROR("{ function f(a:u256) -> b {} }", ParserError, "Expected ':' but got '{'"); -} - -BOOST_AUTO_TEST_CASE(invalid_types) -{ - /// testing invalid literal - /// NOTE: these will need to change when types are compared - CHECK_ERROR("{ let x:bool := 1:invalid }", TypeError, "\"invalid\" is not a valid type (user defined types are not yet supported)."); - /// testing invalid variable declaration - CHECK_ERROR("{ let x:invalid := 1:bool }", TypeError, "\"invalid\" is not a valid type (user defined types are not yet supported)."); - CHECK_ERROR("{ function f(a:invalid) {} }", TypeError, "\"invalid\" is not a valid type (user defined types are not yet supported)."); -} - -BOOST_AUTO_TEST_CASE(number_literals) -{ - BOOST_CHECK(successParse("{ let x:u256 := 1:u256 }")); - CHECK_ERROR("{ let x:u256 := .1:u256 }", ParserError, "Invalid number literal."); - CHECK_ERROR("{ let x:u256 := 1e5:u256 }", ParserError, "Invalid number literal."); - CHECK_ERROR("{ let x:u256 := 67.235:u256 }", ParserError, "Invalid number literal."); - CHECK_ERROR("{ let x:u256 := 0x1ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff:u256 }", TypeError, "Number literal too large (> 256 bits)"); -} - -BOOST_AUTO_TEST_CASE(builtin_types) -{ - BOOST_CHECK(successParse("{ let x:bool := true:bool }")); - BOOST_CHECK(successParse("{ let x:u8 := 1:u8 }")); - BOOST_CHECK(successParse("{ let x:s8 := 1:u8 }")); - BOOST_CHECK(successParse("{ let x:u32 := 1:u32 }")); - BOOST_CHECK(successParse("{ let x:s32 := 1:s32 }")); - BOOST_CHECK(successParse("{ let x:u64 := 1:u64 }")); - BOOST_CHECK(successParse("{ let x:s64 := 1:s64 }")); - BOOST_CHECK(successParse("{ let x:u128 := 1:u128 }")); - BOOST_CHECK(successParse("{ let x:s128 := 1:s128 }")); - BOOST_CHECK(successParse("{ let x:u256 := 1:u256 }")); - BOOST_CHECK(successParse("{ let x:s256 := 1:s256 }")); -} - -BOOST_AUTO_TEST_CASE(recursion_depth) -{ - string input; - for (size_t i = 0; i < 20000; i++) - input += "{"; - input += "let x:u256 := 0:u256"; - for (size_t i = 0; i < 20000; i++) - input += "}"; - - CHECK_ERROR(input, ParserError, "recursion"); -} - -BOOST_AUTO_TEST_CASE(multiple_assignment) -{ - CHECK_ERROR("{ let x:u256 function f() -> a:u256, b:u256 {} 123:u256, x := f() }", ParserError, "Label name / variable name must precede \",\" (multiple assignment)."); - CHECK_ERROR("{ let x:u256 function f() -> a:u256, b:u256 {} x, 123:u256 := f() }", ParserError, "Variable name expected in multiple assignemnt."); - - /// NOTE: Travis hiccups if not having a variable - char const* text = R"( - { - function f(a:u256) -> r1:u256, r2:u256 { - r1 := a - r2 := 7:u256 - } - let x:u256 := 9:u256 - let y:u256 := 2:u256 - x, y := f(x) - } - )"; - BOOST_CHECK(successParse(text)); -} - -BOOST_AUTO_TEST_CASE(if_statement) -{ - BOOST_CHECK(successParse("{ if true:bool {} }")); - BOOST_CHECK(successParse("{ if false:bool { let x:u256 := 3:u256 } }")); - BOOST_CHECK(successParse("{ function f() -> x:bool {} if f() { let b:bool := f() } }")); -} - -BOOST_AUTO_TEST_CASE(if_statement_invalid) -{ - CHECK_ERROR("{ if let x:u256 {} }", ParserError, "Literal or identifier expected."); - CHECK_ERROR("{ if true:bool let x:u256 := 3:u256 }", ParserError, "Expected '{' but got reserved keyword 'let'"); - // TODO change this to an error once we check types. - BOOST_CHECK(successParse("{ if 42:u256 { } }")); -} - -BOOST_AUTO_TEST_SUITE_END() - -} -} -} // end namespaces diff --git a/test/libjulia/Rematerialiser.cpp b/test/libjulia/Rematerialiser.cpp deleted file mode 100644 index 8f928f8e..00000000 --- a/test/libjulia/Rematerialiser.cpp +++ /dev/null @@ -1,179 +0,0 @@ -/* - This file is part of solidity. - - solidity is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - solidity is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with solidity. If not, see <http://www.gnu.org/licenses/>. -*/ -/** - * @date 2017 - * Unit tests for the rematerialiser optimizer stage. - */ - -#include <test/libjulia/Common.h> - -#include <libjulia/optimiser/Rematerialiser.h> - -#include <libsolidity/inlineasm/AsmPrinter.h> - -#include <boost/test/unit_test.hpp> - -#include <boost/range/adaptors.hpp> -#include <boost/algorithm/string/join.hpp> - -using namespace std; -using namespace dev; -using namespace dev::julia; -using namespace dev::julia::test; -using namespace dev::solidity; - - -#define CHECK(_original, _expectation)\ -do\ -{\ - assembly::AsmPrinter p;\ - Block b = disambiguate(_original, false);\ - (Rematerialiser{})(b);\ - string result = p(b);\ - BOOST_CHECK_EQUAL(result, format(_expectation, false));\ -}\ -while(false) - -BOOST_AUTO_TEST_SUITE(IuliaRematerialiser) - -BOOST_AUTO_TEST_CASE(smoke_test) -{ - CHECK("{ }", "{ }"); -} - -BOOST_AUTO_TEST_CASE(trivial) -{ - CHECK( - "{ let a := 1 let b := a mstore(0, b) }", - "{ let a := 1 let b := 1 mstore(0, 1) }" - ); -} - -BOOST_AUTO_TEST_CASE(expression) -{ - CHECK( - "{ let a := add(mul(calldatasize(), 2), number()) let b := add(a, a) }", - "{ let a := add(mul(calldatasize(), 2), number()) let b := add(" - "add(mul(calldatasize(), 2), number())," - "add(mul(calldatasize(), 2), number())" - ") }" - ); -} - -BOOST_AUTO_TEST_CASE(reassign) -{ - CHECK( - "{ let a := extcodesize(0) let b := a let c := b a := 2 let d := add(b, c) pop(a) pop(b) pop(c) pop(d) }", - "{ let a := extcodesize(0) let b := a let c := a a := 2 let d := add(b, c) pop(2) pop(b) pop(c) pop(add(b, c)) }" - ); -} - -BOOST_AUTO_TEST_CASE(non_movable_instr) -{ - CHECK( - "{ let a := 1 let b := mload(a) let c := a mstore(add(a, b), c) }", - "{ let a := 1 let b := mload(1) let c := 1 mstore(add(1, b), 1) }" - ); -} - -BOOST_AUTO_TEST_CASE(non_movable_fun) -{ - CHECK( - "{ function f(x) -> y {} let a := 1 let b := f(a) let c := a mstore(add(a, b), c) }", - "{ function f(x) -> y {} let a := 1 let b := f(1) let c := 1 mstore(add(1, b), 1) }" - ); -} - -BOOST_AUTO_TEST_CASE(branches_if) -{ - CHECK( - "{ let a := 1 let b := 2 if b { pop(b) b := a } let c := b }", - "{ let a := 1 let b := 2 if 2 { pop(2) b := 1 } let c := b }" - ); -} - -BOOST_AUTO_TEST_CASE(branches_switch) -{ - CHECK( - "{ let a := 1 let b := 2 switch number() case 1 { b := a } default { let x := a let y := b b := a } pop(add(a, b)) }", - "{ let a := 1 let b := 2 switch number() case 1 { b := 1 } default { let x := 1 let y := b b := 1 } pop(add(1, b)) }" - ); -} - -BOOST_AUTO_TEST_CASE(branches_for) -{ - CHECK( - "{ let a := 1 for { pop(a) } a { pop(a) } { pop(a) } }", - "{ let a := 1 for { pop(1) } 1 { pop(1) } { pop(1) } }" - ); - CHECK( - "{ let a := 1 for { pop(a) } a { pop(a) } { a := 7 let c := a } let x := a }", - "{ let a := 1 for { pop(1) } a { pop(7) } { a := 7 let c := 7 } let x := a }" - ); -} - -BOOST_AUTO_TEST_CASE(branches_for_declared_in_init) -{ - CHECK( - "{ let b := 0 for { let a := 1 pop(a) } a { pop(a) } { b := 1 pop(a) } }", - "{ let b := 0 for { let a := 1 pop(1) } 1 { pop(1) } { b := 1 pop(1) } }" - ); - CHECK( - "{ let b := 0 for { let a := 1 pop(a) } lt(a, 0) { pop(a) a := add(a, 3) } { b := 1 pop(a) } }", - "{ let b := 0 for { let a := 1 pop(1) } lt(a, 0) { pop(a) a := add(a, 3) } { b := 1 pop(a) } }" - ); -} - -BOOST_AUTO_TEST_CASE(reassignment) -{ - CHECK( - "{ let a := 1 pop(a) if a { a := 2 } let b := mload(a) pop(b) }", - "{ let a := 1 pop(1) if 1 { a := 2 } let b := mload(a) pop(b) }" - ); -} - -BOOST_AUTO_TEST_CASE(update_assignment_remat) -{ - // We cannot substitute `a` in `let b := a` - CHECK( - "{ let a := extcodesize(0) a := mul(a, 2) let b := a }", - "{ let a := extcodesize(0) a := mul(a, 2) let b := a }" - ); -} - -BOOST_AUTO_TEST_CASE(do_not_move_out_of_scope) -{ - // Cannot replace by `let b := x` by `let b := a` since a is out of scope. - CHECK( - "{ let x { let a := sload(0) x := a } let b := x }", - "{ let x { let a := sload(0) x := a } let b := x }" - ); -} - -BOOST_AUTO_TEST_CASE(do_not_remat_large_amounts_of_code) -{ - CHECK( - "{ let x := add(mul(calldataload(2), calldataload(4)), mul(2, calldatasize())) let b := x }", - "{ let x := add(mul(calldataload(2), calldataload(4)), mul(2, calldatasize())) let b := x }" - ); - CHECK( - "{ let x := add(mul(calldataload(2), calldataload(4)), calldatasize()) let b := x }", - "{ let x := add(mul(calldataload(2), calldataload(4)), calldatasize()) let b := add(mul(calldataload(2), calldataload(4)), calldatasize()) }" - ); -} - -BOOST_AUTO_TEST_SUITE_END() diff --git a/test/libjulia/Simplifier.cpp b/test/libjulia/Simplifier.cpp deleted file mode 100644 index 4d4e8d53..00000000 --- a/test/libjulia/Simplifier.cpp +++ /dev/null @@ -1,142 +0,0 @@ -/* - This file is part of solidity. - - solidity is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - solidity is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with solidity. If not, see <http://www.gnu.org/licenses/>. -*/ -/** - * @date 2017 - * Unit tests for the expression simplifier optimizer stage. - */ - -#include <test/libjulia/Common.h> - -#include <libjulia/optimiser/ExpressionSimplifier.h> - -#include <libsolidity/inlineasm/AsmPrinter.h> - -#include <boost/test/unit_test.hpp> - -#include <boost/range/adaptors.hpp> -#include <boost/algorithm/string/join.hpp> - -using namespace std; -using namespace dev; -using namespace dev::julia; -using namespace dev::julia::test; -using namespace dev::solidity; - - -#define CHECK(_original, _expectation)\ -do\ -{\ - assembly::AsmPrinter p;\ - Block b = *(parse(_original, false).first);\ - (ExpressionSimplifier{})(b);\ - string result = p(b);\ - BOOST_CHECK_EQUAL(result, format(_expectation, false));\ -}\ -while(false) - -BOOST_AUTO_TEST_SUITE(IuliaSimplifier) - -BOOST_AUTO_TEST_CASE(smoke_test) -{ - CHECK("{ }", "{ }"); -} - -BOOST_AUTO_TEST_CASE(constants) -{ - CHECK( - "{ let a := add(1, mul(3, 4)) }", - "{ let a := 13 }" - ); -} - -BOOST_AUTO_TEST_CASE(invariant) -{ - CHECK( - "{ let a := mload(sub(7, 7)) let b := sub(a, 0) }", - "{ let a := mload(0) let b := a }" - ); -} - -BOOST_AUTO_TEST_CASE(reversed) -{ - CHECK( - "{ let a := add(0, mload(0)) }", - "{ let a := mload(0) }" - ); -} - -BOOST_AUTO_TEST_CASE(constant_propagation) -{ - CHECK( - "{ let a := add(7, sub(mload(0), 7)) }", - "{ let a := mload(0) }" - ); -} - -BOOST_AUTO_TEST_CASE(identity_rules_simple) -{ - CHECK( - "{ let a := mload(0) let b := sub(a, a) }", - "{ let a := mload(0) let b := 0 }" - ); -} - -BOOST_AUTO_TEST_CASE(identity_rules_complex) -{ - CHECK( - "{ let a := sub(calldataload(0), calldataload(0)) }", - "{ let a := 0 }" - ); -} - -BOOST_AUTO_TEST_CASE(identity_rules_negative) -{ - CHECK( - "{ let a := sub(calldataload(1), calldataload(0)) }", - "{ let a := sub(calldataload(1), calldataload(0)) }" - ); -} - -BOOST_AUTO_TEST_CASE(including_function_calls) -{ - CHECK( - "{ function f() -> a {} let b := add(7, sub(f(), 7)) }", - "{ function f() -> a {} let b := f() }" - ); -} - -BOOST_AUTO_TEST_CASE(inside_for) -{ - CHECK( - "{ for { let a := 10 } iszero(eq(a, 0)) { a := add(a, 1) } {} }", - "{ for { let a := 10 } iszero(iszero(a)) { a := add(a, 1) } {} }" - ); -} - -BOOST_AUTO_TEST_CASE(mod_and) -{ - CHECK( - "{ mstore(0, mod(calldataload(0), exp(2, 8))) }", - "{ mstore(0, and(calldataload(0), 255)) }" - ); - CHECK( - "{ mstore(0, mod(calldataload(0), exp(2, 255))) }", - "{ mstore(0, and(calldataload(0), 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff)) }" - ); -} - -BOOST_AUTO_TEST_SUITE_END() diff --git a/test/libjulia/UnusedPruner.cpp b/test/libjulia/UnusedPruner.cpp deleted file mode 100644 index b86a54b3..00000000 --- a/test/libjulia/UnusedPruner.cpp +++ /dev/null @@ -1,129 +0,0 @@ -/* - This file is part of solidity. - - solidity is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - solidity is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with solidity. If not, see <http://www.gnu.org/licenses/>. -*/ -/** - * @date 2017 - * Unit tests for the pruning of unused variables and functions. - */ - -#include <test/libjulia/Common.h> - -#include <libjulia/optimiser/UnusedPruner.h> - -#include <libsolidity/inlineasm/AsmPrinter.h> - -#include <boost/test/unit_test.hpp> - -#include <boost/range/adaptors.hpp> -#include <boost/algorithm/string/join.hpp> - -using namespace std; -using namespace dev; -using namespace dev::julia; -using namespace dev::julia::test; -using namespace dev::solidity; - - -#define CHECK(_original, _expectation)\ -do\ -{\ - assembly::AsmPrinter p;\ - Block b = disambiguate(_original, false);\ - UnusedPruner::runUntilStabilised(b);\ - string result = p(b);\ - BOOST_CHECK_EQUAL(result, format(_expectation, false));\ -}\ -while(false) - -BOOST_AUTO_TEST_SUITE(IuliaUnusedPruner) - -BOOST_AUTO_TEST_CASE(smoke_test) -{ - CHECK("{ }", "{ }"); -} - -BOOST_AUTO_TEST_CASE(trivial) -{ - CHECK( - "{ let a := 1 let b := 1 mstore(0, 1) }", - "{ mstore(0, 1) }" - ); -} - -BOOST_AUTO_TEST_CASE(multi_declarations) -{ - CHECK( - "{ let x, y }", - "{ }" - ); -} - -BOOST_AUTO_TEST_CASE(multi_assignments) -{ - CHECK( - "{ let x, y x := 1 y := 2 }", - "{ let x, y x := 1 y := 2 }" - ); -} - -BOOST_AUTO_TEST_CASE(multi_partial_assignments) -{ - CHECK( - "{ let x, y x := 1 }", - "{ let x, y x := 1 }" - ); -} - -BOOST_AUTO_TEST_CASE(functions) -{ - CHECK( - "{ function f() { let a := 1 } function g() { f() } }", - "{ }" - ); -} - -BOOST_AUTO_TEST_CASE(intermediate_assignment) -{ - CHECK( - "{ let a := 1 a := 4 let b := 1 }", - "{ let a := 1 a := 4 }" - ); -} - -BOOST_AUTO_TEST_CASE(intermediate_multi_assignment){ - CHECK( - "{ let a, b function f() -> x { } a := f() b := 1 }", - "{ let a, b function f() -> x { } a := f() b := 1 }" - ); -} - -BOOST_AUTO_TEST_CASE(multi_declare) -{ - CHECK( - "{ function f() -> x, y { } let a, b := f() }", - "{ function f() -> x, y { } let a, b := f() }" - ); -} - -BOOST_AUTO_TEST_CASE(multi_assign) -{ - CHECK( - "{ let a let b function f() -> x, y { } a, b := f() }", - "{ let a let b function f() -> x, y { } a, b := f() }" - ); -} - -BOOST_AUTO_TEST_SUITE_END() diff --git a/test/liblll/Compiler.cpp b/test/liblll/Compiler.cpp index ebdea185..27db45a5 100644 --- a/test/liblll/Compiler.cpp +++ b/test/liblll/Compiler.cpp @@ -46,7 +46,7 @@ namespace bool successCompile(string const& _sourceCode) { vector<string> errors; - bytes bytecode = eth::compileLLL(_sourceCode, dev::test::Options::get().evmVersion(), false, &errors); + bytes bytecode = lll::compileLLL(_sourceCode, dev::test::Options::get().evmVersion(), false, &errors); if (!errors.empty()) return false; if (bytecode.empty()) @@ -130,235 +130,238 @@ BOOST_AUTO_TEST_CASE(switch_inconsistent_return_count) BOOST_AUTO_TEST_CASE(disallowed_asm_instructions) { for (unsigned i = 1; i <= 32; i++) - BOOST_CHECK(!successCompile("(asm PUSH" + boost::lexical_cast<string>(i) + ")")); + BOOST_CHECK(!successCompile("(asm PUSH" + to_string(i) + ")")); } BOOST_AUTO_TEST_CASE(disallowed_functional_asm_instructions) { for (unsigned i = 1; i <= 32; i++) - BOOST_CHECK(!successCompile("(PUSH" + boost::lexical_cast<string>(i) + ")")); + BOOST_CHECK(!successCompile("(PUSH" + to_string(i) + ")")); for (unsigned i = 1; i <= 16; i++) - BOOST_CHECK(!successCompile("(DUP" + boost::lexical_cast<string>(i) + ")")); + BOOST_CHECK(!successCompile("(DUP" + to_string(i) + ")")); for (unsigned i = 1; i <= 16; i++) - BOOST_CHECK(!successCompile("(SWAP" + boost::lexical_cast<string>(i) + ")")); + BOOST_CHECK(!successCompile("(SWAP" + to_string(i) + ")")); BOOST_CHECK(!successCompile("(JUMPDEST)")); } BOOST_AUTO_TEST_CASE(valid_opcodes_functional) { vector<string> opcodes_bytecode { - "00", - "6000600001", - "6000600002", - "6000600003", - "6000600004", - "6000600005", - "6000600006", - "6000600007", - "60006000600008", - "60006000600009", - "600060000a", - "600060000b", - "6000600010", - "6000600011", - "6000600012", - "6000600013", - "6000600014", - "600015", - "6000600016", - "6000600017", - "6000600018", - "600019", - "600060001a", - "6000600020", - "30", - "600031", - "32", - "33", - "34", - "600035", - "36", - "60006000600037", - "38", - "60006000600039", - "3a", - "60003b", - "60006000600060003c", - "3d", - "6000600060003e", - "600040", - "41", - "42", - "43", - "44", - "45", - "600050", - "600051", - "6000600052", - "6000600053", - "600054", - "6000600055", - "600056", - "6000600057", - "58", - "59", - "5a", - "60ff", - "61ffff", - "62ffffff", - "63ffffffff", - "64ffffffffff", - "65ffffffffffff", - "66ffffffffffffff", - "67ffffffffffffffff", - "68ffffffffffffffffff", - "69ffffffffffffffffffff", - "6affffffffffffffffffffff", - "6bffffffffffffffffffffffff", - "6cffffffffffffffffffffffffff", - "6dffffffffffffffffffffffffffff", - "6effffffffffffffffffffffffffffff", - "6fffffffffffffffffffffffffffffffff", - "70ffffffffffffffffffffffffffffffffff", - "71ffffffffffffffffffffffffffffffffffff", - "72ffffffffffffffffffffffffffffffffffffff", - "73ffffffffffffffffffffffffffffffffffffffff", - "74ffffffffffffffffffffffffffffffffffffffffff", - "75ffffffffffffffffffffffffffffffffffffffffffff", - "76ffffffffffffffffffffffffffffffffffffffffffffff", - "77ffffffffffffffffffffffffffffffffffffffffffffffff", - "78ffffffffffffffffffffffffffffffffffffffffffffffffff", - "79ffffffffffffffffffffffffffffffffffffffffffffffffffff", - "7affffffffffffffffffffffffffffffffffffffffffffffffffffff", - "7bffffffffffffffffffffffffffffffffffffffffffffffffffffffff", - "7cffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", - "7dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", - "7effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", - "7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", - "60006000a0", - "600060006000a1", - "6000600060006000a2", - "60006000600060006000a3", - "600060006000600060006000a4", - "600060006000f0", - "6000600060006000600060006000f1", - "6000600060006000600060006000f2", - "60006000f3", - "600060006000600060006000f4", - "600060006000600060006000fa", - "60006000fd", - "fe", - "6000ff" + "0000", + "600060000100", + "600060000200", + "600060000300", + "600060000400", + "600060000500", + "600060000600", + "600060000700", + "6000600060000800", + "6000600060000900", + "600060000a00", + "600060000b00", + "600060001000", + "600060001100", + "600060001200", + "600060001300", + "600060001400", + "60001500", + "600060001600", + "600060001700", + "600060001800", + "60001900", + "600060001a00", + "600060002000", + "3000", + "60003100", + "3200", + "3300", + "3400", + "60003500", + "3600", + "6000600060003700", + "3800", + "6000600060003900", + "3a00", + "60003b00", + "60006000600060003c00", + "3d00", + "6000600060003e00", + "60003f00", + "60004000", + "4100", + "4200", + "4300", + "4400", + "4500", + "60005000", + "60005100", + "600060005200", + "600060005300", + "60005400", + "600060005500", + "60005600", + "600060005700", + "5800", + "5900", + "5a00", + "60ff00", + "61ffff00", + "62ffffff00", + "63ffffffff00", + "64ffffffffff00", + "65ffffffffffff00", + "66ffffffffffffff00", + "67ffffffffffffffff00", + "68ffffffffffffffffff00", + "69ffffffffffffffffffff00", + "6affffffffffffffffffffff00", + "6bffffffffffffffffffffffff00", + "6cffffffffffffffffffffffffff00", + "6dffffffffffffffffffffffffffff00", + "6effffffffffffffffffffffffffffff00", + "6fffffffffffffffffffffffffffffffff00", + "70ffffffffffffffffffffffffffffffffff00", + "71ffffffffffffffffffffffffffffffffffff00", + "72ffffffffffffffffffffffffffffffffffffff00", + "73ffffffffffffffffffffffffffffffffffffffff00", + "74ffffffffffffffffffffffffffffffffffffffffff00", + "75ffffffffffffffffffffffffffffffffffffffffffff00", + "76ffffffffffffffffffffffffffffffffffffffffffffff00", + "77ffffffffffffffffffffffffffffffffffffffffffffffff00", + "78ffffffffffffffffffffffffffffffffffffffffffffffffff00", + "79ffffffffffffffffffffffffffffffffffffffffffffffffffff00", + "7affffffffffffffffffffffffffffffffffffffffffffffffffffff00", + "7bffffffffffffffffffffffffffffffffffffffffffffffffffffffff00", + "7cffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00", + "7dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00", + "7effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00", + "7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00", + "60006000a000", + "600060006000a100", + "6000600060006000a200", + "60006000600060006000a300", + "600060006000600060006000a400", + "600060006000f000", + "6000600060006000600060006000f100", + "6000600060006000600060006000f200", + "60006000f300", + "600060006000600060006000f400", + "600060006000600060006000fa00", + "60006000fd00", + "fe00", + "6000ff00" }; vector<string> opcodes_lll { - "{ (STOP) }", - "{ (ADD 0 0) }", - "{ (MUL 0 0) }", - "{ (SUB 0 0) }", - "{ (DIV 0 0) }", - "{ (SDIV 0 0) }", - "{ (MOD 0 0) }", - "{ (SMOD 0 0) }", - "{ (ADDMOD 0 0 0) }", - "{ (MULMOD 0 0 0) }", - "{ (EXP 0 0) }", - "{ (SIGNEXTEND 0 0) }", - "{ (LT 0 0) }", - "{ (GT 0 0) }", - "{ (SLT 0 0) }", - "{ (SGT 0 0) }", - "{ (EQ 0 0) }", - "{ (ISZERO 0) }", - "{ (AND 0 0) }", - "{ (OR 0 0) }", - "{ (XOR 0 0) }", - "{ (NOT 0) }", - "{ (BYTE 0 0) }", - "{ (KECCAK256 0 0) }", - "{ (ADDRESS) }", - "{ (BALANCE 0) }", - "{ (ORIGIN) }", - "{ (CALLER) }", - "{ (CALLVALUE) }", - "{ (CALLDATALOAD 0) }", - "{ (CALLDATASIZE) }", - "{ (CALLDATACOPY 0 0 0) }", - "{ (CODESIZE) }", - "{ (CODECOPY 0 0 0) }", - "{ (GASPRICE) }", - "{ (EXTCODESIZE 0) }", - "{ (EXTCODECOPY 0 0 0 0) }", - "{ (RETURNDATASIZE) }", - "{ (RETURNDATACOPY 0 0 0) }", - "{ (BLOCKHASH 0) }", - "{ (COINBASE) }", - "{ (TIMESTAMP) }", - "{ (NUMBER) }", - "{ (DIFFICULTY) }", - "{ (GASLIMIT) }", - "{ (POP 0) }", - "{ (MLOAD 0) }", - "{ (MSTORE 0 0) }", - "{ (MSTORE8 0 0) }", - "{ (SLOAD 0) }", - "{ (SSTORE 0 0) }", - "{ (JUMP 0) }", - "{ (JUMPI 0 0) }", - "{ (PC) }", - "{ (MSIZE) }", - "{ (GAS) }", - "{ 0xff }", - "{ 0xffff }", - "{ 0xffffff }", - "{ 0xffffffff }", - "{ 0xffffffffff }", - "{ 0xffffffffffff }", - "{ 0xffffffffffffff }", - "{ 0xffffffffffffffff }", - "{ 0xffffffffffffffffff }", - "{ 0xffffffffffffffffffff }", - "{ 0xffffffffffffffffffffff }", - "{ 0xffffffffffffffffffffffff }", - "{ 0xffffffffffffffffffffffffff }", - "{ 0xffffffffffffffffffffffffffff }", - "{ 0xffffffffffffffffffffffffffffff }", - "{ 0xffffffffffffffffffffffffffffffff }", - "{ 0xffffffffffffffffffffffffffffffffff }", - "{ 0xffffffffffffffffffffffffffffffffffff }", - "{ 0xffffffffffffffffffffffffffffffffffffff }", - "{ 0xffffffffffffffffffffffffffffffffffffffff }", - "{ 0xffffffffffffffffffffffffffffffffffffffffff }", - "{ 0xffffffffffffffffffffffffffffffffffffffffffff }", - "{ 0xffffffffffffffffffffffffffffffffffffffffffffff }", - "{ 0xffffffffffffffffffffffffffffffffffffffffffffffff }", - "{ 0xffffffffffffffffffffffffffffffffffffffffffffffffff }", - "{ 0xffffffffffffffffffffffffffffffffffffffffffffffffffff }", - "{ 0xffffffffffffffffffffffffffffffffffffffffffffffffffffff }", - "{ 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffff }", - "{ 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffff }", - "{ 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff }", - "{ 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff }", - "{ 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff }", - "{ (LOG0 0 0) }", - "{ (LOG1 0 0 0) }", - "{ (LOG2 0 0 0 0) }", - "{ (LOG3 0 0 0 0 0) }", - "{ (LOG4 0 0 0 0 0 0) }", - "{ (CREATE 0 0 0) }", - "{ (CALL 0 0 0 0 0 0 0) }", - "{ (CALLCODE 0 0 0 0 0 0 0) }", - "{ (RETURN 0 0) }", - "{ (DELEGATECALL 0 0 0 0 0 0) }", - "{ (STATICCALL 0 0 0 0 0 0) }", - "{ (REVERT 0 0) }", - "{ (INVALID) }", - "{ (SELFDESTRUCT 0) }" + "(STOP)", + "(ADD 0 0)", + "(MUL 0 0)", + "(SUB 0 0)", + "(DIV 0 0)", + "(SDIV 0 0)", + "(MOD 0 0)", + "(SMOD 0 0)", + "(ADDMOD 0 0 0)", + "(MULMOD 0 0 0)", + "(EXP 0 0)", + "(SIGNEXTEND 0 0)", + "(LT 0 0)", + "(GT 0 0)", + "(SLT 0 0)", + "(SGT 0 0)", + "(EQ 0 0)", + "(ISZERO 0)", + "(AND 0 0)", + "(OR 0 0)", + "(XOR 0 0)", + "(NOT 0)", + "(BYTE 0 0)", + "(KECCAK256 0 0)", + "(ADDRESS)", + "(BALANCE 0)", + "(ORIGIN)", + "(CALLER)", + "(CALLVALUE)", + "(CALLDATALOAD 0)", + "(CALLDATASIZE)", + "(CALLDATACOPY 0 0 0)", + "(CODESIZE)", + "(CODECOPY 0 0 0)", + "(GASPRICE)", + "(EXTCODESIZE 0)", + "(EXTCODECOPY 0 0 0 0)", + "(RETURNDATASIZE)", + "(RETURNDATACOPY 0 0 0)", + "(EXTCODEHASH 0)", + "(BLOCKHASH 0)", + "(COINBASE)", + "(TIMESTAMP)", + "(NUMBER)", + "(DIFFICULTY)", + "(GASLIMIT)", + "(POP 0)", + "(MLOAD 0)", + "(MSTORE 0 0)", + "(MSTORE8 0 0)", + "(SLOAD 0)", + "(SSTORE 0 0)", + "(JUMP 0)", + "(JUMPI 0 0)", + "(PC)", + "(MSIZE)", + "(GAS)", + "0xff", + "0xffff", + "0xffffff", + "0xffffffff", + "0xffffffffff", + "0xffffffffffff", + "0xffffffffffffff", + "0xffffffffffffffff", + "0xffffffffffffffffff", + "0xffffffffffffffffffff", + "0xffffffffffffffffffffff", + "0xffffffffffffffffffffffff", + "0xffffffffffffffffffffffffff", + "0xffffffffffffffffffffffffffff", + "0xffffffffffffffffffffffffffffff", + "0xffffffffffffffffffffffffffffffff", + "0xffffffffffffffffffffffffffffffffff", + "0xffffffffffffffffffffffffffffffffffff", + "0xffffffffffffffffffffffffffffffffffffff", + "0xffffffffffffffffffffffffffffffffffffffff", + "0xffffffffffffffffffffffffffffffffffffffffff", + "0xffffffffffffffffffffffffffffffffffffffffffff", + "0xffffffffffffffffffffffffffffffffffffffffffffff", + "0xffffffffffffffffffffffffffffffffffffffffffffffff", + "0xffffffffffffffffffffffffffffffffffffffffffffffffff", + "0xffffffffffffffffffffffffffffffffffffffffffffffffffff", + "0xffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "(LOG0 0 0)", + "(LOG1 0 0 0)", + "(LOG2 0 0 0 0)", + "(LOG3 0 0 0 0 0)", + "(LOG4 0 0 0 0 0 0)", + "(CREATE 0 0 0)", + "(CALL 0 0 0 0 0 0 0)", + "(CALLCODE 0 0 0 0 0 0 0)", + "(RETURN 0 0)", + "(DELEGATECALL 0 0 0 0 0 0)", + "(STATICCALL 0 0 0 0 0 0)", + "(REVERT 0 0)", + "(INVALID)", + "(SELFDESTRUCT 0)" }; - for (size_t i = 0; i < opcodes_bytecode.size(); i++) { + for (size_t i = 0; i < opcodes_bytecode.size(); i++) + { vector<string> errors; - bytes code = eth::compileLLL(opcodes_lll[i], dev::test::Options::get().evmVersion(), false, &errors); + bytes code = lll::compileLLL(opcodes_lll[i], dev::test::Options::get().evmVersion(), false, &errors); BOOST_REQUIRE_MESSAGE(errors.empty(), opcodes_lll[i]); @@ -369,284 +372,287 @@ BOOST_AUTO_TEST_CASE(valid_opcodes_functional) BOOST_AUTO_TEST_CASE(valid_opcodes_asm) { vector<string> opcodes_bytecode { - "00", - "01", - "02", - "03", - "04", - "05", - "06", - "07", - "08", - "09", - "0a", - "0b", - "10", - "11", - "12", - "13", - "14", - "15", - "16", - "17", - "18", - "19", - "1a", - "20", - "30", - "31", - "32", - "33", - "34", - "35", - "36", - "37", - "38", - "39", - "3a", - "3b", - "3c", - "3d", - "3e", - "40", - "41", - "42", - "43", - "44", - "45", - "50", - "51", - "52", - "53", - "54", - "55", - "56", - "57", - "58", - "59", - "5a", - "5b", - "60ff", - "61ffff", - "62ffffff", - "63ffffffff", - "64ffffffffff", - "65ffffffffffff", - "66ffffffffffffff", - "67ffffffffffffffff", - "68ffffffffffffffffff", - "69ffffffffffffffffffff", - "6affffffffffffffffffffff", - "6bffffffffffffffffffffffff", - "6cffffffffffffffffffffffffff", - "6dffffffffffffffffffffffffffff", - "6effffffffffffffffffffffffffffff", - "6fffffffffffffffffffffffffffffffff", - "70ffffffffffffffffffffffffffffffffff", - "71ffffffffffffffffffffffffffffffffffff", - "72ffffffffffffffffffffffffffffffffffffff", - "73ffffffffffffffffffffffffffffffffffffffff", - "74ffffffffffffffffffffffffffffffffffffffffff", - "75ffffffffffffffffffffffffffffffffffffffffffff", - "76ffffffffffffffffffffffffffffffffffffffffffffff", - "77ffffffffffffffffffffffffffffffffffffffffffffffff", - "78ffffffffffffffffffffffffffffffffffffffffffffffffff", - "79ffffffffffffffffffffffffffffffffffffffffffffffffffff", - "7affffffffffffffffffffffffffffffffffffffffffffffffffffff", - "7bffffffffffffffffffffffffffffffffffffffffffffffffffffffff", - "7cffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", - "7dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", - "7effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", - "7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", - "80", - "81", - "82", - "83", - "84", - "85", - "86", - "87", - "88", - "89", - "8a", - "8b", - "8c", - "8d", - "8e", - "8f", - "90", - "91", - "92", - "93", - "94", - "95", - "96", - "97", - "98", - "99", - "9a", - "9b", - "9c", - "9d", - "9e", - "9f", - "a0", - "a1", - "a2", - "a3", - "a4", - "f0", - "f1", - "f2", - "f3", - "f4", - "fa", - "fd", - "fe", - "ff" + "0000", + "600060000100", + "600060000200", + "600060000300", + "600060000400", + "600060000500", + "600060000600", + "600060000700", + "6000600060000800", + "6000600060000900", + "600060000a00", + "600060000b00", + "600060001000", + "600060001100", + "600060001200", + "600060001300", + "600060001400", + "60001500", + "600060001600", + "600060001700", + "600060001800", + "60001900", + "600060001a00", + "600060002000", + "3000", + "60003100", + "3200", + "3300", + "3400", + "60003500", + "3600", + "6000600060003700", + "3800", + "6000600060003900", + "3a00", + "60003b00", + "60006000600060003c00", + "3d00", + "6000600060003e00", + "60003f00", + "4000", + "4100", + "4200", + "4300", + "4400", + "4500", + "60005000", + "60005100", + "600060005200", + "600060005300", + "60005400", + "600060005500", + "60005600", + "600060005700", + "5800", + "5900", + "5a00", + "5b00", + "60ff00", + "61ffff00", + "62ffffff00", + "63ffffffff00", + "64ffffffffff00", + "65ffffffffffff00", + "66ffffffffffffff00", + "67ffffffffffffffff00", + "68ffffffffffffffffff00", + "69ffffffffffffffffffff00", + "6affffffffffffffffffffff00", + "6bffffffffffffffffffffffff00", + "6cffffffffffffffffffffffffff00", + "6dffffffffffffffffffffffffffff00", + "6effffffffffffffffffffffffffffff00", + "6fffffffffffffffffffffffffffffffff00", + "70ffffffffffffffffffffffffffffffffff00", + "71ffffffffffffffffffffffffffffffffffff00", + "72ffffffffffffffffffffffffffffffffffffff00", + "73ffffffffffffffffffffffffffffffffffffffff00", + "74ffffffffffffffffffffffffffffffffffffffffff00", + "75ffffffffffffffffffffffffffffffffffffffffffff00", + "76ffffffffffffffffffffffffffffffffffffffffffffff00", + "77ffffffffffffffffffffffffffffffffffffffffffffffff00", + "78ffffffffffffffffffffffffffffffffffffffffffffffffff00", + "79ffffffffffffffffffffffffffffffffffffffffffffffffffff00", + "7affffffffffffffffffffffffffffffffffffffffffffffffffffff00", + "7bffffffffffffffffffffffffffffffffffffffffffffffffffffffff00", + "7cffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00", + "7dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00", + "7effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00", + "7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00", + "60006000600060006000600060006000600060006000600060006000600060008000", + "60006000600060006000600060006000600060006000600060006000600060008100", + "60006000600060006000600060006000600060006000600060006000600060008200", + "60006000600060006000600060006000600060006000600060006000600060008300", + "60006000600060006000600060006000600060006000600060006000600060008400", + "60006000600060006000600060006000600060006000600060006000600060008500", + "60006000600060006000600060006000600060006000600060006000600060008600", + "60006000600060006000600060006000600060006000600060006000600060008700", + "60006000600060006000600060006000600060006000600060006000600060008800", + "60006000600060006000600060006000600060006000600060006000600060008900", + "60006000600060006000600060006000600060006000600060006000600060008a00", + "60006000600060006000600060006000600060006000600060006000600060008b00", + "60006000600060006000600060006000600060006000600060006000600060008c00", + "60006000600060006000600060006000600060006000600060006000600060008d00", + "60006000600060006000600060006000600060006000600060006000600060008e00", + "60006000600060006000600060006000600060006000600060006000600060008f00", + "60006000600060006000600060006000600060006000600060006000600060009000", + "60006000600060006000600060006000600060006000600060006000600060009100", + "60006000600060006000600060006000600060006000600060006000600060009200", + "60006000600060006000600060006000600060006000600060006000600060009300", + "60006000600060006000600060006000600060006000600060006000600060009400", + "60006000600060006000600060006000600060006000600060006000600060009500", + "60006000600060006000600060006000600060006000600060006000600060009600", + "60006000600060006000600060006000600060006000600060006000600060009700", + "60006000600060006000600060006000600060006000600060006000600060009800", + "60006000600060006000600060006000600060006000600060006000600060009900", + "60006000600060006000600060006000600060006000600060006000600060009a00", + "60006000600060006000600060006000600060006000600060006000600060009b00", + "60006000600060006000600060006000600060006000600060006000600060009c00", + "60006000600060006000600060006000600060006000600060006000600060009d00", + "60006000600060006000600060006000600060006000600060006000600060009e00", + "60006000600060006000600060006000600060006000600060006000600060009f00", + "60006000a000", + "600060006000a100", + "6000600060006000a200", + "60006000600060006000a300", + "600060006000600060006000a400", + "600060006000f000", + "600060006000600060006000f100", + "600060006000600060006000f200", + "60006000f300", + "60006000600060006000f400", + "60006000600060006000fa00", + "60006000fd00", + "fe00", + "6000ff00" }; vector<string> opcodes_lll { - "{ (asm STOP) }", - "{ (asm ADD) }", - "{ (asm MUL) }", - "{ (asm SUB) }", - "{ (asm DIV) }", - "{ (asm SDIV ) }", - "{ (asm MOD) }", - "{ (asm SMOD) }", - "{ (asm ADDMOD) }", - "{ (asm MULMOD) }", - "{ (asm EXP) }", - "{ (asm SIGNEXTEND) }", - "{ (asm LT) }", - "{ (asm GT) }", - "{ (asm SLT) }", - "{ (asm SGT) }", - "{ (asm EQ) }", - "{ (asm ISZERO) }", - "{ (asm AND) }", - "{ (asm OR) }", - "{ (asm XOR) }", - "{ (asm NOT) }", - "{ (asm BYTE) }", - "{ (asm KECCAK256) }", - "{ (asm ADDRESS) }", - "{ (asm BALANCE) }", - "{ (asm ORIGIN) }", - "{ (asm CALLER) }", - "{ (asm CALLVALUE) }", - "{ (asm CALLDATALOAD) }", - "{ (asm CALLDATASIZE) }", - "{ (asm CALLDATACOPY) }", - "{ (asm CODESIZE) }", - "{ (asm CODECOPY) }", - "{ (asm GASPRICE) }", - "{ (asm EXTCODESIZE)}", - "{ (asm EXTCODECOPY) }", - "{ (asm RETURNDATASIZE) }", - "{ (asm RETURNDATACOPY) }", - "{ (asm BLOCKHASH) }", - "{ (asm COINBASE) }", - "{ (asm TIMESTAMP) }", - "{ (asm NUMBER) }", - "{ (asm DIFFICULTY) }", - "{ (asm GASLIMIT) }", - "{ (asm POP) }", - "{ (asm MLOAD) }", - "{ (asm MSTORE) }", - "{ (asm MSTORE8) }", - "{ (asm SLOAD) }", - "{ (asm SSTORE) }", - "{ (asm JUMP ) }", - "{ (asm JUMPI ) }", - "{ (asm PC) }", - "{ (asm MSIZE) }", - "{ (asm GAS) }", - "{ (asm JUMPDEST) }", - "{ (asm 0xff) }", - "{ (asm 0xffff) }", - "{ (asm 0xffffff) }", - "{ (asm 0xffffffff) }", - "{ (asm 0xffffffffff) }", - "{ (asm 0xffffffffffff) }", - "{ (asm 0xffffffffffffff) }", - "{ (asm 0xffffffffffffffff) }", - "{ (asm 0xffffffffffffffffff) }", - "{ (asm 0xffffffffffffffffffff) }", - "{ (asm 0xffffffffffffffffffffff) }", - "{ (asm 0xffffffffffffffffffffffff) }", - "{ (asm 0xffffffffffffffffffffffffff) }", - "{ (asm 0xffffffffffffffffffffffffffff) }", - "{ (asm 0xffffffffffffffffffffffffffffff) }", - "{ (asm 0xffffffffffffffffffffffffffffffff) }", - "{ (asm 0xffffffffffffffffffffffffffffffffff) }", - "{ (asm 0xffffffffffffffffffffffffffffffffffff) }", - "{ (asm 0xffffffffffffffffffffffffffffffffffffff) }", - "{ (asm 0xffffffffffffffffffffffffffffffffffffffff) }", - "{ (asm 0xffffffffffffffffffffffffffffffffffffffffff) }", - "{ (asm 0xffffffffffffffffffffffffffffffffffffffffffff) }", - "{ (asm 0xffffffffffffffffffffffffffffffffffffffffffffff) }", - "{ (asm 0xffffffffffffffffffffffffffffffffffffffffffffffff) }", - "{ (asm 0xffffffffffffffffffffffffffffffffffffffffffffffffff) }", - "{ (asm 0xffffffffffffffffffffffffffffffffffffffffffffffffffff) }", - "{ (asm 0xffffffffffffffffffffffffffffffffffffffffffffffffffffff) }", - "{ (asm 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffff) }", - "{ (asm 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) }", - "{ (asm 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) }", - "{ (asm 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) }", - "{ (asm 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) }", - "{ (asm DUP1) }", - "{ (asm DUP2) }", - "{ (asm DUP3) }", - "{ (asm DUP4) }", - "{ (asm DUP5) }", - "{ (asm DUP6) }", - "{ (asm DUP7) }", - "{ (asm DUP8) }", - "{ (asm DUP9) }", - "{ (asm DUP10) }", - "{ (asm DUP11) }", - "{ (asm DUP12) }", - "{ (asm DUP13) }", - "{ (asm DUP14) }", - "{ (asm DUP15) }", - "{ (asm DUP16) }", - "{ (asm SWAP1) }", - "{ (asm SWAP2) }", - "{ (asm SWAP3) }", - "{ (asm SWAP4) }", - "{ (asm SWAP5) }", - "{ (asm SWAP6) }", - "{ (asm SWAP7) }", - "{ (asm SWAP8) }", - "{ (asm SWAP9) }", - "{ (asm SWAP10) }", - "{ (asm SWAP11) }", - "{ (asm SWAP12) }", - "{ (asm SWAP13) }", - "{ (asm SWAP14) }", - "{ (asm SWAP15) }", - "{ (asm SWAP16) }", - "{ (asm LOG0) }", - "{ (asm LOG1) }", - "{ (asm LOG2) }", - "{ (asm LOG3) }", - "{ (asm LOG4) }", - "{ (asm CREATE) }", - "{ (asm CALL) }", - "{ (asm CALLCODE) }", - "{ (asm RETURN) }", - "{ (asm DELEGATECALL) }", - "{ (asm STATICCALL) }", - "{ (asm REVERT) }", - "{ (asm INVALID) }", - "{ (asm SELFDESTRUCT) }" + "(asm STOP)", + "(asm 0 0 ADD)", + "(asm 0 0 MUL)", + "(asm 0 0 SUB)", + "(asm 0 0 DIV)", + "(asm 0 0 SDIV)", + "(asm 0 0 MOD)", + "(asm 0 0 SMOD)", + "(asm 0 0 0 ADDMOD)", + "(asm 0 0 0 MULMOD)", + "(asm 0 0 EXP)", + "(asm 0 0 SIGNEXTEND)", + "(asm 0 0 LT)", + "(asm 0 0 GT)", + "(asm 0 0 SLT)", + "(asm 0 0 SGT)", + "(asm 0 0 EQ)", + "(asm 0 ISZERO)", + "(asm 0 0 AND)", + "(asm 0 0 OR)", + "(asm 0 0 XOR)", + "(asm 0 NOT)", + "(asm 0 0 BYTE)", + "(asm 0 0 KECCAK256)", + "(asm ADDRESS)", + "(asm 0 BALANCE)", + "(asm ORIGIN)", + "(asm CALLER)", + "(asm CALLVALUE)", + "(asm 0 CALLDATALOAD)", + "(asm CALLDATASIZE)", + "(asm 0 0 0 CALLDATACOPY)", + "(asm CODESIZE)", + "(asm 0 0 0 CODECOPY)", + "(asm GASPRICE)", + "(asm 0 EXTCODESIZE)", + "(asm 0 0 0 0 EXTCODECOPY)", + "(asm RETURNDATASIZE)", + "(asm 0 0 0 RETURNDATACOPY)", + "(asm 0 EXTCODEHASH)", + "(asm BLOCKHASH)", + "(asm COINBASE)", + "(asm TIMESTAMP)", + "(asm NUMBER)", + "(asm DIFFICULTY)", + "(asm GASLIMIT)", + "(asm 0 POP)", + "(asm 0 MLOAD)", + "(asm 0 0 MSTORE)", + "(asm 0 0 MSTORE8)", + "(asm 0 SLOAD)", + "(asm 0 0 SSTORE)", + "(asm 0 JUMP)", + "(asm 0 0 JUMPI)", + "(asm PC)", + "(asm MSIZE)", + "(asm GAS)", + "(asm JUMPDEST)", + "(asm 0xff)", + "(asm 0xffff)", + "(asm 0xffffff)", + "(asm 0xffffffff)", + "(asm 0xffffffffff)", + "(asm 0xffffffffffff)", + "(asm 0xffffffffffffff)", + "(asm 0xffffffffffffffff)", + "(asm 0xffffffffffffffffff)", + "(asm 0xffffffffffffffffffff)", + "(asm 0xffffffffffffffffffffff)", + "(asm 0xffffffffffffffffffffffff)", + "(asm 0xffffffffffffffffffffffffff)", + "(asm 0xffffffffffffffffffffffffffff)", + "(asm 0xffffffffffffffffffffffffffffff)", + "(asm 0xffffffffffffffffffffffffffffffff)", + "(asm 0xffffffffffffffffffffffffffffffffff)", + "(asm 0xffffffffffffffffffffffffffffffffffff)", + "(asm 0xffffffffffffffffffffffffffffffffffffff)", + "(asm 0xffffffffffffffffffffffffffffffffffffffff)", + "(asm 0xffffffffffffffffffffffffffffffffffffffffff)", + "(asm 0xffffffffffffffffffffffffffffffffffffffffffff)", + "(asm 0xffffffffffffffffffffffffffffffffffffffffffffff)", + "(asm 0xffffffffffffffffffffffffffffffffffffffffffffffff)", + "(asm 0xffffffffffffffffffffffffffffffffffffffffffffffffff)", + "(asm 0xffffffffffffffffffffffffffffffffffffffffffffffffffff)", + "(asm 0xffffffffffffffffffffffffffffffffffffffffffffffffffffff)", + "(asm 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffff)", + "(asm 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffff)", + "(asm 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff)", + "(asm 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff)", + "(asm 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff)", + "(asm 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 DUP1)", + "(asm 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 DUP2)", + "(asm 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 DUP3)", + "(asm 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 DUP4)", + "(asm 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 DUP5)", + "(asm 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 DUP6)", + "(asm 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 DUP7)", + "(asm 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 DUP8)", + "(asm 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 DUP9)", + "(asm 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 DUP10)", + "(asm 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 DUP11)", + "(asm 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 DUP12)", + "(asm 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 DUP13)", + "(asm 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 DUP14)", + "(asm 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 DUP15)", + "(asm 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 DUP16)", + "(asm 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 SWAP1)", + "(asm 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 SWAP2)", + "(asm 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 SWAP3)", + "(asm 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 SWAP4)", + "(asm 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 SWAP5)", + "(asm 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 SWAP6)", + "(asm 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 SWAP7)", + "(asm 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 SWAP8)", + "(asm 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 SWAP9)", + "(asm 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 SWAP10)", + "(asm 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 SWAP11)", + "(asm 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 SWAP12)", + "(asm 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 SWAP13)", + "(asm 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 SWAP14)", + "(asm 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 SWAP15)", + "(asm 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 SWAP16)", + "(asm 0 0 LOG0)", + "(asm 0 0 0 LOG1)", + "(asm 0 0 0 0 LOG2)", + "(asm 0 0 0 0 0 LOG3)", + "(asm 0 0 0 0 0 0 LOG4)", + "(asm 0 0 0 CREATE)", + "(asm 0 0 0 0 0 0 CALL)", + "(asm 0 0 0 0 0 0 CALLCODE)", + "(asm 0 0 RETURN)", + "(asm 0 0 0 0 0 DELEGATECALL)", + "(asm 0 0 0 0 0 STATICCALL)", + "(asm 0 0 REVERT)", + "(asm INVALID)", + "(asm 0 SELFDESTRUCT)" }; - for (size_t i = 0; i < opcodes_bytecode.size(); i++) { + for (size_t i = 0; i < opcodes_bytecode.size(); i++) + { vector<string> errors; - bytes code = eth::compileLLL(opcodes_lll[i], dev::test::Options::get().evmVersion(), false, &errors); + bytes code = lll::compileLLL(opcodes_lll[i], dev::test::Options::get().evmVersion(), false, &errors); BOOST_REQUIRE_MESSAGE(errors.empty(), opcodes_lll[i]); diff --git a/test/liblll/EndToEndTest.cpp b/test/liblll/EndToEndTest.cpp index fd8099f2..60aef7b0 100644 --- a/test/liblll/EndToEndTest.cpp +++ b/test/liblll/EndToEndTest.cpp @@ -50,6 +50,7 @@ BOOST_AUTO_TEST_CASE(bare_panic) { char const* sourceCode = "(panic)"; compileAndRunWithoutCheck(sourceCode); + BOOST_REQUIRE(!m_transactionSuccessful); BOOST_REQUIRE(m_output.empty()); } @@ -57,6 +58,7 @@ BOOST_AUTO_TEST_CASE(panic) { char const* sourceCode = "{ (panic) }"; compileAndRunWithoutCheck(sourceCode); + BOOST_REQUIRE(!m_transactionSuccessful); BOOST_REQUIRE(m_output.empty()); } @@ -69,6 +71,7 @@ BOOST_AUTO_TEST_CASE(macro_zeroarg) (zeroarg))) )"; compileAndRun(sourceCode); + BOOST_CHECK(m_transactionSuccessful); BOOST_CHECK(callFallback() == encodeArgs(u256(0x1234))); } @@ -983,6 +986,20 @@ BOOST_AUTO_TEST_CASE(shift_right) BOOST_CHECK(callFallback() == encodeArgs(u256(256))); } +BOOST_AUTO_TEST_CASE(sub_assemblies) +{ + char const* sourceCode = R"( + (returnlll + (return (create 0 (returnlll (sstore 1 1))))) + )"; + compileAndRun(sourceCode); + bytes ret = callFallback(); + BOOST_REQUIRE(ret.size() == 32); + u256 rVal = u256(toHex(ret, 2, HexPrefix::Add)); + BOOST_CHECK(rVal != 0); + BOOST_CHECK(rVal < u256("0x10000000000000000000000000000000000000000")); +} + BOOST_AUTO_TEST_SUITE_END() } diff --git a/test/liblll/ExecutionFramework.h b/test/liblll/ExecutionFramework.h index ae5cd988..7c1ce670 100644 --- a/test/liblll/ExecutionFramework.h +++ b/test/liblll/ExecutionFramework.h @@ -56,7 +56,7 @@ public: BOOST_REQUIRE(_libraryAddresses.empty()); std::vector<std::string> errors; - bytes bytecode = eth::compileLLL(_sourceCode, dev::test::Options::get().evmVersion(), m_optimize, &errors); + bytes bytecode = lll::compileLLL(_sourceCode, dev::test::Options::get().evmVersion(), m_optimize, &errors); if (!errors.empty()) { for (auto const& error: errors) diff --git a/test/liblll/Parser.cpp b/test/liblll/Parser.cpp index fc977b81..d343aab1 100644 --- a/test/liblll/Parser.cpp +++ b/test/liblll/Parser.cpp @@ -39,13 +39,13 @@ namespace bool successParse(std::string const& _source) { - std::string ret = eth::parseLLL(_source); + std::string ret = lll::parseLLL(_source); return ret.size() != 0; } std::string parse(std::string const& _source) { - return eth::parseLLL(_source); + return lll::parseLLL(_source); } } diff --git a/test/libsolidity/ABIDecoderTests.cpp b/test/libsolidity/ABIDecoderTests.cpp index beb7b5af..94319985 100644 --- a/test/libsolidity/ABIDecoderTests.cpp +++ b/test/libsolidity/ABIDecoderTests.cpp @@ -128,7 +128,7 @@ BOOST_AUTO_TEST_CASE(fixed_arrays) { string sourceCode = R"( contract C { - function f(uint16[3] a, uint16[2][3] b, uint i, uint j, uint k) + function f(uint16[3] memory a, uint16[2][3] memory b, uint i, uint j, uint k) public pure returns (uint, uint) { return (a[i], b[j][k]); } @@ -154,7 +154,7 @@ BOOST_AUTO_TEST_CASE(dynamic_arrays) { string sourceCode = R"( contract C { - function f(uint a, uint16[] b, uint c) + function f(uint a, uint16[] memory b, uint c) public pure returns (uint, uint, uint) { return (b.length, b[a], c); } @@ -178,11 +178,11 @@ BOOST_AUTO_TEST_CASE(dynamic_nested_arrays) { string sourceCode = R"( contract C { - function f(uint a, uint16[][] b, uint[2][][3] c, uint d) + function f(uint a, uint16[][] memory b, uint[2][][3] memory c, uint d) public pure returns (uint, uint, uint, uint, uint, uint, uint) { return (a, b.length, b[1].length, b[1][1], c[1].length, c[1][1][1], d); } - function test() view returns (uint, uint, uint, uint, uint, uint, uint) { + function test() public view returns (uint, uint, uint, uint, uint, uint, uint) { uint16[][] memory b = new uint16[][](3); b[0] = new uint16[](2); b[0][0] = 0x55; @@ -229,12 +229,12 @@ BOOST_AUTO_TEST_CASE(byte_arrays) { string sourceCode = R"( contract C { - function f(uint a, bytes b, uint c) + function f(uint a, bytes memory b, uint c) public pure returns (uint, uint, byte, uint) { return (a, b.length, b[3], c); } - function f_external(uint a, bytes b, uint c) + function f_external(uint a, bytes calldata b, uint c) external pure returns (uint, uint, byte, uint) { return (a, b.length, b[3], c); } @@ -261,12 +261,11 @@ BOOST_AUTO_TEST_CASE(calldata_arrays_too_large) { string sourceCode = R"( contract C { - function f(uint a, uint[] b, uint c) external pure returns (uint) { + function f(uint a, uint[] calldata b, uint c) external pure returns (uint) { return 7; } } )"; - bool newEncoder = false; BOTH_ENCODERS( compileAndRun(sourceCode); bytes args = encodeArgs( @@ -275,9 +274,8 @@ BOOST_AUTO_TEST_CASE(calldata_arrays_too_large) ); ABI_CHECK( callContractFunction("f(uint256,uint256[],uint256)", args), - newEncoder ? encodeArgs() : encodeArgs(7) + encodeArgs() ); - newEncoder = true; ) } @@ -287,7 +285,7 @@ BOOST_AUTO_TEST_CASE(decode_from_memory_simple) contract C { uint public _a; uint[] public _b; - function C(uint a, uint[] b) { + constructor(uint a, uint[] memory b) public { _a = a; _b = b; } @@ -312,24 +310,24 @@ BOOST_AUTO_TEST_CASE(decode_function_type) string sourceCode = R"( contract D { function () external returns (uint) public _a; - function D(function () external returns (uint) a) { + constructor(function () external returns (uint) a) public { _a = a; } } contract C { - function f() returns (uint) { + function f() public returns (uint) { return 3; } - function g(function () external returns (uint) _f) returns (uint) { + function g(function () external returns (uint) _f) public returns (uint) { return _f(); } // uses "decode from memory" - function test1() returns (uint) { + function test1() public returns (uint) { D d = new D(this.f); return d._a()(); } // uses "decode from calldata" - function test2() returns (uint) { + function test2() public returns (uint) { return this.g(this.f); } } @@ -346,13 +344,13 @@ BOOST_AUTO_TEST_CASE(decode_function_type_array) string sourceCode = R"( contract D { function () external returns (uint)[] public _a; - function D(function () external returns (uint)[] a) { + constructor(function () external returns (uint)[] memory a) public { _a = a; } } contract E { function () external returns (uint)[3] public _a; - function E(function () external returns (uint)[3] a) { + constructor(function () external returns (uint)[3] memory a) public { _a = a; } } @@ -366,15 +364,15 @@ BOOST_AUTO_TEST_CASE(decode_function_type_array) function f3() public returns (uint) { return 3; } - function g(function () external returns (uint)[] _f, uint i) public returns (uint) { + function g(function () external returns (uint)[] memory _f, uint i) public returns (uint) { return _f[i](); } - function h(function () external returns (uint)[3] _f, uint i) public returns (uint) { + function h(function () external returns (uint)[3] memory _f, uint i) public returns (uint) { return _f[i](); } // uses "decode from memory" function test1_dynamic() public returns (uint) { - var x = new function() external returns (uint)[](3); + function () external returns (uint)[] memory x = new function() external returns (uint)[](4); x[0] = this.f1; x[1] = this.f2; x[2] = this.f3; @@ -387,7 +385,7 @@ BOOST_AUTO_TEST_CASE(decode_function_type_array) } // uses "decode from calldata" function test2_dynamic() public returns (uint) { - var x = new function() external returns (uint)[](3); + function () external returns (uint)[] memory x = new function() external returns (uint)[](3); x[0] = this.f1; x[1] = this.f2; x[2] = this.f3; @@ -414,7 +412,7 @@ BOOST_AUTO_TEST_CASE(decode_from_memory_complex) uint public _a; uint[] public _b; bytes[2] public _c; - function C(uint a, uint[] b, bytes[2] c) { + constructor(uint a, uint[] memory b, bytes[2] memory c) public { _a = a; _b = b; _c = c; @@ -449,13 +447,11 @@ BOOST_AUTO_TEST_CASE(short_input_value_type) function f(uint a, uint b) public pure returns (uint) { return a; } } )"; - bool newDecoder = false; BOTH_ENCODERS( compileAndRun(sourceCode); ABI_CHECK(callContractFunction("f(uint256,uint256)", 1, 2), encodeArgs(1)); ABI_CHECK(callContractFunctionNoEncoding("f(uint256,uint256)", bytes(64, 0)), encodeArgs(0)); - ABI_CHECK(callContractFunctionNoEncoding("f(uint256,uint256)", bytes(63, 0)), newDecoder ? encodeArgs() : encodeArgs(0)); - newDecoder = true; + ABI_CHECK(callContractFunctionNoEncoding("f(uint256,uint256)", bytes(63, 0)), encodeArgs()); ) } @@ -463,18 +459,16 @@ BOOST_AUTO_TEST_CASE(short_input_array) { string sourceCode = R"( contract C { - function f(uint[] a) public pure returns (uint) { return 7; } + function f(uint[] memory a) public pure returns (uint) { return 7; } } )"; - bool newDecoder = false; BOTH_ENCODERS( compileAndRun(sourceCode); ABI_CHECK(callContractFunctionNoEncoding("f(uint256[])", encodeArgs(0x20, 0)), encodeArgs(7)); - ABI_CHECK(callContractFunctionNoEncoding("f(uint256[])", encodeArgs(0x20, 1)), newDecoder ? encodeArgs() : encodeArgs(7)); - ABI_CHECK(callContractFunctionNoEncoding("f(uint256[])", encodeArgs(0x20, 1) + bytes(31, 0)), newDecoder ? encodeArgs() : encodeArgs(7)); + ABI_CHECK(callContractFunctionNoEncoding("f(uint256[])", encodeArgs(0x20, 1)), encodeArgs()); + ABI_CHECK(callContractFunctionNoEncoding("f(uint256[])", encodeArgs(0x20, 1) + bytes(31, 0)), encodeArgs()); ABI_CHECK(callContractFunctionNoEncoding("f(uint256[])", encodeArgs(0x20, 1) + bytes(32, 0)), encodeArgs(7)); ABI_CHECK(callContractFunctionNoEncoding("f(uint256[])", encodeArgs(0x20, 2, 5, 6)), encodeArgs(7)); - newDecoder = true; ) } @@ -482,7 +476,7 @@ BOOST_AUTO_TEST_CASE(short_dynamic_input_array) { string sourceCode = R"( contract C { - function f(bytes[1] a) public pure returns (uint) { return 7; } + function f(bytes[1] memory a) public pure returns (uint) { return 7; } } )"; NEW_ENCODER( @@ -495,8 +489,8 @@ BOOST_AUTO_TEST_CASE(short_input_bytes) { string sourceCode = R"( contract C { - function e(bytes a) public pure returns (uint) { return 7; } - function f(bytes[] a) public pure returns (uint) { return 7; } + function e(bytes memory a) public pure returns (uint) { return 7; } + function f(bytes[] memory a) public pure returns (uint) { return 7; } } )"; NEW_ENCODER( @@ -517,9 +511,9 @@ BOOST_AUTO_TEST_CASE(cleanup_int_inside_arrays) string sourceCode = R"( contract C { enum E { A, B } - function f(uint16[] a) public pure returns (uint r) { assembly { r := mload(add(a, 0x20)) } } - function g(int16[] a) public pure returns (uint r) { assembly { r := mload(add(a, 0x20)) } } - function h(E[] a) public pure returns (uint r) { assembly { r := mload(add(a, 0x20)) } } + function f(uint16[] memory a) public pure returns (uint r) { assembly { r := mload(add(a, 0x20)) } } + function g(int16[] memory a) public pure returns (uint r) { assembly { r := mload(add(a, 0x20)) } } + function h(E[] memory a) public pure returns (uint r) { assembly { r := mload(add(a, 0x20)) } } } )"; NEW_ENCODER( @@ -558,7 +552,7 @@ BOOST_AUTO_TEST_CASE(storage_ptr) r[2] = 3; s.x = 11; s.y = 12; - var (a, b, c, d) = L.f(r, s); + (uint a, uint b, uint c, uint d) = L.f(r, s); return (r[2], s.x, a, b, c, d); } } @@ -575,11 +569,11 @@ BOOST_AUTO_TEST_CASE(struct_simple) string sourceCode = R"( contract C { struct S { uint a; uint8 b; uint8 c; bytes2 d; } - function f(S s) public pure returns (uint a, uint b, uint c, uint d) { + function f(S memory s) public pure returns (uint a, uint b, uint c, uint d) { a = s.a; b = s.b; c = s.c; - d = uint(s.d); + d = uint16(s.d); } } )"; @@ -594,7 +588,7 @@ BOOST_AUTO_TEST_CASE(struct_cleanup) string sourceCode = R"( contract C { struct S { int16 a; uint8 b; bytes2 c; } - function f(S s) public pure returns (uint a, uint b, uint c) { + function f(S memory s) public pure returns (uint a, uint b, uint c) { assembly { a := mload(s) b := mload(add(s, 0x20)) @@ -617,7 +611,7 @@ BOOST_AUTO_TEST_CASE(struct_short) string sourceCode = R"( contract C { struct S { int a; uint b; bytes16 c; } - function f(S s) public pure returns (S q) { + function f(S memory s) public pure returns (S memory q) { q = s; } } @@ -644,7 +638,7 @@ BOOST_AUTO_TEST_CASE(struct_function) string sourceCode = R"( contract C { struct S { function () external returns (uint) f; uint b; } - function f(S s) public returns (uint, uint) { + function f(S memory s) public returns (uint, uint) { return (s.f(), s.b); } function test() public returns (uint, uint) { @@ -659,32 +653,12 @@ BOOST_AUTO_TEST_CASE(struct_function) ) } -BOOST_AUTO_TEST_CASE(empty_struct) -{ - string sourceCode = R"( - contract C { - struct S { } - function f(uint a, S s, uint b) public pure returns (uint x, uint y) { - assembly { x := a y := b } - } - function g() public returns (uint, uint) { - return this.f(7, S(), 8); - } - } - )"; - NEW_ENCODER( - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("f(uint256,(),uint256)", 7, 8), encodeArgs(7, 8)); - ABI_CHECK(callContractFunction("g()"), encodeArgs(7, 8)); - ) -} - BOOST_AUTO_TEST_CASE(mediocre_struct) { string sourceCode = R"( contract C { struct S { C c; } - function f(uint a, S[2] s1, uint b) public returns (uint r1, C r2, uint r3) { + function f(uint a, S[2] memory s1, uint b) public returns (uint r1, C r2, uint r3) { r1 = a; r2 = s1[0].c; r3 = b; @@ -705,7 +679,7 @@ BOOST_AUTO_TEST_CASE(mediocre2_struct) string sourceCode = R"( contract C { struct S { C c; uint[] x; } - function f(uint a, S[2] s1, uint b) public returns (uint r1, C r2, uint r3) { + function f(uint a, S[2] memory s1, uint b) public returns (uint r1, C r2, uint r3) { r1 = a; r2 = s1[0].c; r3 = b; @@ -733,7 +707,7 @@ BOOST_AUTO_TEST_CASE(complex_struct) enum E {A, B, C} struct T { uint x; E e; uint8 y; } struct S { C c; T[] t;} - function f(uint a, S[2] s1, S[] s2, uint b) public returns + function f(uint a, S[2] memory s1, S[] memory s2, uint b) public returns (uint r1, C r2, uint r3, uint r4, C r5, uint r6, E r7, uint8 r8) { r1 = a; r2 = s1[0].c; @@ -793,10 +767,10 @@ BOOST_AUTO_TEST_CASE(return_dynamic_types_cross_call_simple) string sourceCode = R"( contract C { - function dyn() public returns (bytes) { + function dyn() public returns (bytes memory) { return "1234567890123456789012345678901234567890"; } - function f() public returns (bytes) { + function f() public returns (bytes memory) { return this.dyn(); } } @@ -814,15 +788,15 @@ BOOST_AUTO_TEST_CASE(return_dynamic_types_cross_call_advanced) string sourceCode = R"( contract C { - function dyn() public returns (bytes a, uint b, bytes20[] c, uint d) { + function dyn() public returns (bytes memory a, uint b, bytes20[] memory c, uint d) { a = "1234567890123456789012345678901234567890"; b = uint(-1); c = new bytes20[](4); - c[0] = bytes20(1234); - c[3] = bytes20(6789); + c[0] = bytes20(uint160(1234)); + c[3] = bytes20(uint160(6789)); d = 0x1234; } - function f() public returns (bytes, uint, bytes20[], uint) { + function f() public returns (bytes memory, uint, bytes20[] memory, uint) { return this.dyn(); } } @@ -841,7 +815,7 @@ BOOST_AUTO_TEST_CASE(return_dynamic_types_cross_call_out_of_range) { string sourceCode = R"( contract C { - function dyn(uint x) public returns (bytes a) { + function dyn(uint x) public returns (bytes memory a) { assembly { mstore(0, 0x20) mstore(0x20, 0x21) diff --git a/test/libsolidity/ABIEncoderTests.cpp b/test/libsolidity/ABIEncoderTests.cpp index 49db9ce1..d2125cc7 100644 --- a/test/libsolidity/ABIEncoderTests.cpp +++ b/test/libsolidity/ABIEncoderTests.cpp @@ -68,7 +68,7 @@ BOOST_AUTO_TEST_CASE(value_types) assembly { b := 7 } C c; assembly { c := sub(0, 5) } - E(10, uint16(uint256(-2)), uint24(0x12121212), int24(int256(-1)), bytes3(x), b, c); + emit E(10, uint16(uint256(-2)), uint24(0x12121212), int24(int256(-1)), bytes3(x), b, c); } } )"; @@ -87,7 +87,7 @@ BOOST_AUTO_TEST_CASE(string_literal) contract C { event E(string, bytes20, string); function f() public { - E("abcdef", "abcde", "abcdefabcdefgehabcabcasdfjklabcdefabcedefghabcabcasdfjklabcdefabcdefghabcabcasdfjklabcdeefabcdefghabcabcasdefjklabcdefabcdefghabcabcasdfjkl"); + emit E("abcdef", "abcde", "abcdefabcdefgehabcabcasdfjklabcdefabcedefghabcabcasdfjklabcdefabcdefghabcabcasdfjklabcdeefabcdefghabcabcasdefjklabcdefabcdefghabcabcasdfjkl"); } } )"; @@ -133,7 +133,7 @@ BOOST_AUTO_TEST_CASE(conversion) int8 c; int16 d; assembly { a := sub(0, 1) c := 0x0101ff d := 0xff01 } - E(10, x, a, uint8(b), c, int8(d)); + emit E(bytes4(uint32(10)), x, a, uint8(b), c, int8(d)); } } )"; @@ -159,7 +159,7 @@ BOOST_AUTO_TEST_CASE(memory_array_one_dim) mstore(add(x, mul(add(i, 1), 0x20)), add(0xfffffffe, i)) } } - E(10, x, 11); + emit E(10, x, 11); } } )"; @@ -188,7 +188,7 @@ BOOST_AUTO_TEST_CASE(memory_array_two_dim) x[0][2] = -1; x[1][0] = 4; x[1][1] = 5; - E(10, x, 11); + emit E(10, x, 11); } } )"; @@ -208,7 +208,7 @@ BOOST_AUTO_TEST_CASE(memory_byte_array) bytes[] memory x = new bytes[](2); x[0] = "abcabcdefghjklmnopqrsuvwabcdefgijklmnopqrstuwabcdefgijklmnoprstuvw"; x[1] = "abcdefghijklmnopqrtuvwabcfghijklmnopqstuvwabcdeghijklmopqrstuvw"; - E(10, x, 11); + emit E(10, x, 11); } } )"; @@ -234,7 +234,7 @@ BOOST_AUTO_TEST_CASE(storage_byte_array) function f() public { short = "123456789012345678901234567890a"; long = "ffff123456789012345678901234567890afffffffff123456789012345678901234567890a"; - E(short, long); + emit E(short, long); } } )"; @@ -261,7 +261,7 @@ BOOST_AUTO_TEST_CASE(storage_array) sstore(1, sub(0, 2)) sstore(2, sub(0, 3)) } - E(addr); + emit E(addr); } } )"; @@ -279,10 +279,10 @@ BOOST_AUTO_TEST_CASE(storage_array_dyn) address[] addr; event E(address[] a); function f() public { - addr.push(1); - addr.push(2); - addr.push(3); - E(addr); + addr.push(0x0000000000000000000000000000000000000001); + addr.push(0x0000000000000000000000000000000000000002); + addr.push(0x0000000000000000000000000000000000000003); + emit E(addr); } } )"; @@ -308,7 +308,7 @@ BOOST_AUTO_TEST_CASE(storage_array_compact) x.push(6); x.push(-7); x.push(8); - E(x); + emit E(x); } } )"; @@ -329,7 +329,7 @@ BOOST_AUTO_TEST_CASE(external_function) function(uint) external returns (uint) g; function f(uint) public returns (uint) { g = this.f; - E(this.f, g); + emit E(this.f, g); } } )"; @@ -351,7 +351,7 @@ BOOST_AUTO_TEST_CASE(external_function_cleanup) function f(uint) public returns (uint) { function(uint) external returns (uint)[1] memory h; assembly { sstore(0, sub(0, 1)) mstore(h, sub(0, 1)) } - E(h[0], g); + emit E(h[0], g); } } )"; @@ -367,22 +367,19 @@ BOOST_AUTO_TEST_CASE(calldata) string sourceCode = R"( contract C { event E(bytes); - function f(bytes a) external { - E(a); + function f(bytes calldata a) external { + emit E(a); } } )"; string s("abcdef"); string t("abcdefgggggggggggggggggggggggggggggggggggggggghhheeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeggg"); - bool newEncoder = false; BOTH_ENCODERS( compileAndRun(sourceCode); callContractFunction("f(bytes)", 0x20, s.size(), s); - // The old encoder did not pad to multiples of 32 bytes - REQUIRE_LOG_DATA(encodeArgs(0x20, s.size()) + (newEncoder ? encodeArgs(s) : asBytes(s))); + REQUIRE_LOG_DATA(encodeArgs(0x20, s.size(), s)); callContractFunction("f(bytes)", 0x20, t.size(), t); - REQUIRE_LOG_DATA(encodeArgs(0x20, t.size()) + (newEncoder ? encodeArgs(t) : asBytes(t))); - newEncoder = true; + REQUIRE_LOG_DATA(encodeArgs(0x20, t.size(), t)); ) } @@ -420,7 +417,7 @@ BOOST_AUTO_TEST_CASE(structs) struct T { uint64[2] x; } S s; event e(uint16, S); - function f() public returns (uint, S) { + function f() public returns (uint, S memory) { uint16 x = 7; s.a = 8; s.b = 9; @@ -429,7 +426,7 @@ BOOST_AUTO_TEST_CASE(structs) s.sub[0].x[0] = 11; s.sub[1].x[0] = 12; s.sub[2].x[1] = 13; - e(x, s); + emit e(x, s); return (x, s); } } @@ -450,28 +447,6 @@ BOOST_AUTO_TEST_CASE(structs) ) } -BOOST_AUTO_TEST_CASE(empty_struct) -{ - string sourceCode = R"( - contract C { - struct S { } - S s; - event e(uint16, S, uint16); - function f() returns (uint, S, uint) { - e(7, s, 8); - return (7, s, 8); - } - } - )"; - - NEW_ENCODER( - compileAndRun(sourceCode, 0, "C"); - bytes encoded = encodeArgs(7, 8); - BOOST_CHECK(callContractFunction("f()") == encoded); - REQUIRE_LOG_DATA(encoded); - ) -} - BOOST_AUTO_TEST_CASE(structs2) { string sourceCode = R"( @@ -479,7 +454,7 @@ BOOST_AUTO_TEST_CASE(structs2) enum E {A, B, C} struct T { uint x; E e; uint8 y; } struct S { C c; T[] t;} - function f() public returns (uint a, S[2] s1, S[] s2, uint b) { + function f() public returns (uint a, S[2] memory s1, S[] memory s2, uint b) { a = 7; b = 8; s1[0].c = this; diff --git a/test/libsolidity/ASTJSON.cpp b/test/libsolidity/ASTJSON.cpp deleted file mode 100644 index 5d5b14e8..00000000 --- a/test/libsolidity/ASTJSON.cpp +++ /dev/null @@ -1,145 +0,0 @@ -/* - This file is part of solidity. - - solidity is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - solidity is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with solidity. If not, see <http://www.gnu.org/licenses/>. -*/ -/** - * @author Christian <c@ethdev.com> - * @date 2016 - * Tests for the json ast output. - */ - -#include <test/Options.h> - -#include <libsolidity/interface/Exceptions.h> -#include <libsolidity/interface/CompilerStack.h> -#include <libsolidity/ast/ASTJsonConverter.h> - -#include <boost/test/unit_test.hpp> - -#include <string> - -using namespace std; - -namespace dev -{ -namespace solidity -{ -namespace test -{ - -BOOST_AUTO_TEST_SUITE(SolidityASTJSON) - -BOOST_AUTO_TEST_CASE(short_type_name) -{ - CompilerStack c; - c.addSource("a", "contract c { function f() { uint[] memory x; } }"); - c.setEVMVersion(dev::test::Options::get().evmVersion()); - c.parseAndAnalyze(); - map<string, unsigned> sourceIndices; - sourceIndices["a"] = 1; - Json::Value astJson = ASTJsonConverter(false, sourceIndices).toJson(c.ast("a")); - Json::Value varDecl = astJson["nodes"][0]["nodes"][0]["body"]["statements"][0]["declarations"][0]; - BOOST_CHECK_EQUAL(varDecl["storageLocation"], "memory"); - BOOST_CHECK_EQUAL(varDecl["typeDescriptions"]["typeIdentifier"], "t_array$_t_uint256_$dyn_memory_ptr"); - BOOST_CHECK_EQUAL(varDecl["typeDescriptions"]["typeString"], "uint256[]"); -} - -BOOST_AUTO_TEST_CASE(short_type_name_ref) -{ - CompilerStack c; - c.addSource("a", "contract c { function f() { uint[][] memory rows; } }"); - c.setEVMVersion(dev::test::Options::get().evmVersion()); - c.parseAndAnalyze(); - map<string, unsigned> sourceIndices; - sourceIndices["a"] = 1; - Json::Value astJson = ASTJsonConverter(false, sourceIndices).toJson(c.ast("a")); - Json::Value varDecl = astJson["nodes"][0]["nodes"][0]["body"]["statements"][0]["declarations"][0]; - BOOST_CHECK_EQUAL(varDecl["storageLocation"], "memory"); - BOOST_CHECK_EQUAL(varDecl["typeName"]["typeDescriptions"]["typeIdentifier"], "t_array$_t_array$_t_uint256_$dyn_storage_$dyn_storage_ptr"); - BOOST_CHECK_EQUAL(varDecl["typeName"]["typeDescriptions"]["typeString"], "uint256[][]"); -} - -BOOST_AUTO_TEST_CASE(long_type_name_binary_operation) -{ - CompilerStack c; - c.addSource("a", "contract c { function f() public { uint a = 2 + 3; } }"); - c.setEVMVersion(dev::test::Options::get().evmVersion()); - c.parseAndAnalyze(); - map<string, unsigned> sourceIndices; - sourceIndices["a"] = 1; - Json::Value astJson = ASTJsonConverter(false, sourceIndices).toJson(c.ast("a")); - Json::Value varDecl = astJson["nodes"][0]["nodes"][0]["body"]["statements"][0]["initialValue"]["commonType"]; - BOOST_CHECK_EQUAL(varDecl["typeIdentifier"], "t_rational_5_by_1"); - BOOST_CHECK_EQUAL(varDecl["typeString"], "int_const 5"); -} - -BOOST_AUTO_TEST_CASE(long_type_name_identifier) -{ - CompilerStack c; - c.addSource("a", "contract c { uint[] a; function f() public { uint[] b = a; } }"); - c.setEVMVersion(dev::test::Options::get().evmVersion()); - c.parseAndAnalyze(); - map<string, unsigned> sourceIndices; - sourceIndices["a"] = 1; - Json::Value astJson = ASTJsonConverter(false, sourceIndices).toJson(c.ast("a")); - Json::Value varDecl = astJson["nodes"][0]["nodes"][1]["body"]["statements"][0]["initialValue"]; - BOOST_CHECK_EQUAL(varDecl["typeDescriptions"]["typeIdentifier"], "t_array$_t_uint256_$dyn_storage"); - BOOST_CHECK_EQUAL(varDecl["typeDescriptions"]["typeString"], "uint256[] storage ref"); -} - -BOOST_AUTO_TEST_CASE(documentation) -{ - CompilerStack c; - c.addSource("a", "/**This contract is empty*/ contract C {}"); - c.addSource("b", - "/**This contract is empty" - " and has a line-breaking comment.*/" - "contract C {}" - ); - c.addSource("c", - "contract C {" - " /** Some comment on Evt.*/ event Evt();" - " /** Some comment on mod.*/ modifier mod() { _; }" - " /** Some comment on fn.*/ function fn() public {}" - "}" - ); - c.setEVMVersion(dev::test::Options::get().evmVersion()); - c.parseAndAnalyze(); - map<string, unsigned> sourceIndices; - sourceIndices["a"] = 0; - sourceIndices["b"] = 1; - sourceIndices["c"] = 2; - //same tests for non-legacy mode - Json::Value astJsonA = ASTJsonConverter(false, sourceIndices).toJson(c.ast("a")); - Json::Value documentationA = astJsonA["nodes"][0]["documentation"]; - BOOST_CHECK_EQUAL(documentationA, "This contract is empty"); - Json::Value astJsonB = ASTJsonConverter(false, sourceIndices).toJson(c.ast("b")); - Json::Value documentationB = astJsonB["nodes"][0]["documentation"]; - BOOST_CHECK_EQUAL(documentationB, "This contract is empty and has a line-breaking comment."); - Json::Value astJsonC = ASTJsonConverter(false, sourceIndices).toJson(c.ast("c")); - Json::Value documentationC0 = astJsonC["nodes"][0]["nodes"][0]["documentation"]; - Json::Value documentationC1 = astJsonC["nodes"][0]["nodes"][1]["documentation"]; - Json::Value documentationC2 = astJsonC["nodes"][0]["nodes"][2]["documentation"]; - BOOST_CHECK_EQUAL(documentationC0, "Some comment on Evt."); - BOOST_CHECK_EQUAL(documentationC1, "Some comment on mod."); - BOOST_CHECK_EQUAL(documentationC2, "Some comment on fn."); -} - - -BOOST_AUTO_TEST_SUITE_END() - -} -} -} // end namespaces diff --git a/test/libsolidity/ASTJSON/address_payable.json b/test/libsolidity/ASTJSON/address_payable.json new file mode 100644 index 00000000..0f30e8e8 --- /dev/null +++ b/test/libsolidity/ASTJSON/address_payable.json @@ -0,0 +1,560 @@ +{ + "absolutePath" : "a", + "exportedSymbols" : + { + "C" : + [ + 37 + ] + }, + "id" : 38, + "nodeType" : "SourceUnit", + "nodes" : + [ + { + "baseContracts" : [], + "contractDependencies" : [], + "contractKind" : "contract", + "documentation" : null, + "fullyImplemented" : true, + "id" : 37, + "linearizedBaseContracts" : + [ + 37 + ], + "name" : "C", + "nodeType" : "ContractDefinition", + "nodes" : + [ + { + "constant" : false, + "id" : 4, + "name" : "m", + "nodeType" : "VariableDeclaration", + "scope" : 37, + "src" : "17:44:1", + "stateVariable" : true, + "storageLocation" : "default", + "typeDescriptions" : + { + "typeIdentifier" : "t_mapping$_t_address_$_t_address_payable_$", + "typeString" : "mapping(address => address payable)" + }, + "typeName" : + { + "id" : 3, + "keyType" : + { + "id" : 1, + "name" : "address", + "nodeType" : "ElementaryTypeName", + "src" : "25:7:1", + "typeDescriptions" : + { + "typeIdentifier" : "t_address", + "typeString" : "address" + } + }, + "nodeType" : "Mapping", + "src" : "17:35:1", + "typeDescriptions" : + { + "typeIdentifier" : "t_mapping$_t_address_$_t_address_payable_$", + "typeString" : "mapping(address => address payable)" + }, + "valueType" : + { + "id" : 2, + "name" : "address", + "nodeType" : "ElementaryTypeName", + "src" : "36:15:1", + "stateMutability" : "payable", + "typeDescriptions" : + { + "typeIdentifier" : "t_address_payable", + "typeString" : "address payable" + } + } + }, + "value" : null, + "visibility" : "public" + }, + { + "body" : + { + "id" : 35, + "nodeType" : "Block", + "src" : "134:122:1", + "statements" : + [ + { + "assignments" : + [ + 12 + ], + "declarations" : + [ + { + "constant" : false, + "id" : 12, + "name" : "a", + "nodeType" : "VariableDeclaration", + "scope" : 35, + "src" : "144:17:1", + "stateVariable" : false, + "storageLocation" : "default", + "typeDescriptions" : + { + "typeIdentifier" : "t_address_payable", + "typeString" : "address payable" + }, + "typeName" : + { + "id" : 11, + "name" : "address", + "nodeType" : "ElementaryTypeName", + "src" : "144:15:1", + "stateMutability" : "payable", + "typeDescriptions" : + { + "typeIdentifier" : "t_address_payable", + "typeString" : "address payable" + } + }, + "value" : null, + "visibility" : "internal" + } + ], + "id" : 16, + "initialValue" : + { + "argumentTypes" : null, + "baseExpression" : + { + "argumentTypes" : null, + "id" : 13, + "name" : "m", + "nodeType" : "Identifier", + "overloadedDeclarations" : [], + "referencedDeclaration" : 4, + "src" : "164:1:1", + "typeDescriptions" : + { + "typeIdentifier" : "t_mapping$_t_address_$_t_address_payable_$", + "typeString" : "mapping(address => address payable)" + } + }, + "id" : 15, + "indexExpression" : + { + "argumentTypes" : null, + "id" : 14, + "name" : "arg", + "nodeType" : "Identifier", + "overloadedDeclarations" : [], + "referencedDeclaration" : 6, + "src" : "166:3:1", + "typeDescriptions" : + { + "typeIdentifier" : "t_address_payable", + "typeString" : "address payable" + } + }, + "isConstant" : false, + "isLValue" : true, + "isPure" : false, + "lValueRequested" : false, + "nodeType" : "IndexAccess", + "src" : "164:6:1", + "typeDescriptions" : + { + "typeIdentifier" : "t_address_payable", + "typeString" : "address payable" + } + }, + "nodeType" : "VariableDeclarationStatement", + "src" : "144:26:1" + }, + { + "expression" : + { + "argumentTypes" : null, + "id" : 19, + "isConstant" : false, + "isLValue" : false, + "isPure" : false, + "lValueRequested" : false, + "leftHandSide" : + { + "argumentTypes" : null, + "id" : 17, + "name" : "r", + "nodeType" : "Identifier", + "overloadedDeclarations" : [], + "referencedDeclaration" : 9, + "src" : "180:1:1", + "typeDescriptions" : + { + "typeIdentifier" : "t_address_payable", + "typeString" : "address payable" + } + }, + "nodeType" : "Assignment", + "operator" : "=", + "rightHandSide" : + { + "argumentTypes" : null, + "id" : 18, + "name" : "arg", + "nodeType" : "Identifier", + "overloadedDeclarations" : [], + "referencedDeclaration" : 6, + "src" : "184:3:1", + "typeDescriptions" : + { + "typeIdentifier" : "t_address_payable", + "typeString" : "address payable" + } + }, + "src" : "180:7:1", + "typeDescriptions" : + { + "typeIdentifier" : "t_address_payable", + "typeString" : "address payable" + } + }, + "id" : 20, + "nodeType" : "ExpressionStatement", + "src" : "180:7:1" + }, + { + "assignments" : + [ + 22 + ], + "declarations" : + [ + { + "constant" : false, + "id" : 22, + "name" : "c", + "nodeType" : "VariableDeclaration", + "scope" : 35, + "src" : "197:9:1", + "stateVariable" : false, + "storageLocation" : "default", + "typeDescriptions" : + { + "typeIdentifier" : "t_address", + "typeString" : "address" + }, + "typeName" : + { + "id" : 21, + "name" : "address", + "nodeType" : "ElementaryTypeName", + "src" : "197:7:1", + "stateMutability" : "nonpayable", + "typeDescriptions" : + { + "typeIdentifier" : "t_address", + "typeString" : "address" + } + }, + "value" : null, + "visibility" : "internal" + } + ], + "id" : 26, + "initialValue" : + { + "argumentTypes" : null, + "arguments" : + [ + { + "argumentTypes" : null, + "id" : 24, + "name" : "this", + "nodeType" : "Identifier", + "overloadedDeclarations" : [], + "referencedDeclaration" : 65, + "src" : "217:4:1", + "typeDescriptions" : + { + "typeIdentifier" : "t_contract$_C_$37", + "typeString" : "contract C" + } + } + ], + "expression" : + { + "argumentTypes" : + [ + { + "typeIdentifier" : "t_contract$_C_$37", + "typeString" : "contract C" + } + ], + "id" : 23, + "isConstant" : false, + "isLValue" : false, + "isPure" : true, + "lValueRequested" : false, + "nodeType" : "ElementaryTypeNameExpression", + "src" : "209:7:1", + "typeDescriptions" : + { + "typeIdentifier" : "t_type$_t_address_$", + "typeString" : "type(address)" + }, + "typeName" : "address" + }, + "id" : 25, + "isConstant" : false, + "isLValue" : false, + "isPure" : false, + "kind" : "typeConversion", + "lValueRequested" : false, + "names" : [], + "nodeType" : "FunctionCall", + "src" : "209:13:1", + "typeDescriptions" : + { + "typeIdentifier" : "t_address", + "typeString" : "address" + } + }, + "nodeType" : "VariableDeclarationStatement", + "src" : "197:25:1" + }, + { + "expression" : + { + "argumentTypes" : null, + "id" : 33, + "isConstant" : false, + "isLValue" : false, + "isPure" : false, + "lValueRequested" : false, + "leftHandSide" : + { + "argumentTypes" : null, + "baseExpression" : + { + "argumentTypes" : null, + "id" : 27, + "name" : "m", + "nodeType" : "Identifier", + "overloadedDeclarations" : [], + "referencedDeclaration" : 4, + "src" : "232:1:1", + "typeDescriptions" : + { + "typeIdentifier" : "t_mapping$_t_address_$_t_address_payable_$", + "typeString" : "mapping(address => address payable)" + } + }, + "id" : 29, + "indexExpression" : + { + "argumentTypes" : null, + "id" : 28, + "name" : "c", + "nodeType" : "Identifier", + "overloadedDeclarations" : [], + "referencedDeclaration" : 22, + "src" : "234:1:1", + "typeDescriptions" : + { + "typeIdentifier" : "t_address", + "typeString" : "address" + } + }, + "isConstant" : false, + "isLValue" : true, + "isPure" : false, + "lValueRequested" : true, + "nodeType" : "IndexAccess", + "src" : "232:4:1", + "typeDescriptions" : + { + "typeIdentifier" : "t_address_payable", + "typeString" : "address payable" + } + }, + "nodeType" : "Assignment", + "operator" : "=", + "rightHandSide" : + { + "argumentTypes" : null, + "arguments" : + [ + { + "argumentTypes" : null, + "hexValue" : "30", + "id" : 31, + "isConstant" : false, + "isLValue" : false, + "isPure" : true, + "kind" : "number", + "lValueRequested" : false, + "nodeType" : "Literal", + "src" : "247:1:1", + "subdenomination" : null, + "typeDescriptions" : + { + "typeIdentifier" : "t_rational_0_by_1", + "typeString" : "int_const 0" + }, + "value" : "0" + } + ], + "expression" : + { + "argumentTypes" : + [ + { + "typeIdentifier" : "t_rational_0_by_1", + "typeString" : "int_const 0" + } + ], + "id" : 30, + "isConstant" : false, + "isLValue" : false, + "isPure" : true, + "lValueRequested" : false, + "nodeType" : "ElementaryTypeNameExpression", + "src" : "239:7:1", + "typeDescriptions" : + { + "typeIdentifier" : "t_type$_t_address_$", + "typeString" : "type(address)" + }, + "typeName" : "address" + }, + "id" : 32, + "isConstant" : false, + "isLValue" : false, + "isPure" : true, + "kind" : "typeConversion", + "lValueRequested" : false, + "names" : [], + "nodeType" : "FunctionCall", + "src" : "239:10:1", + "typeDescriptions" : + { + "typeIdentifier" : "t_address_payable", + "typeString" : "address payable" + } + }, + "src" : "232:17:1", + "typeDescriptions" : + { + "typeIdentifier" : "t_address_payable", + "typeString" : "address payable" + } + }, + "id" : 34, + "nodeType" : "ExpressionStatement", + "src" : "232:17:1" + } + ] + }, + "documentation" : null, + "id" : 36, + "implemented" : true, + "kind" : "function", + "modifiers" : [], + "name" : "f", + "nodeType" : "FunctionDefinition", + "parameters" : + { + "id" : 7, + "nodeType" : "ParameterList", + "parameters" : + [ + { + "constant" : false, + "id" : 6, + "name" : "arg", + "nodeType" : "VariableDeclaration", + "scope" : 36, + "src" : "78:19:1", + "stateVariable" : false, + "storageLocation" : "default", + "typeDescriptions" : + { + "typeIdentifier" : "t_address_payable", + "typeString" : "address payable" + }, + "typeName" : + { + "id" : 5, + "name" : "address", + "nodeType" : "ElementaryTypeName", + "src" : "78:15:1", + "stateMutability" : "payable", + "typeDescriptions" : + { + "typeIdentifier" : "t_address_payable", + "typeString" : "address payable" + } + }, + "value" : null, + "visibility" : "internal" + } + ], + "src" : "77:21:1" + }, + "returnParameters" : + { + "id" : 10, + "nodeType" : "ParameterList", + "parameters" : + [ + { + "constant" : false, + "id" : 9, + "name" : "r", + "nodeType" : "VariableDeclaration", + "scope" : 36, + "src" : "115:17:1", + "stateVariable" : false, + "storageLocation" : "default", + "typeDescriptions" : + { + "typeIdentifier" : "t_address_payable", + "typeString" : "address payable" + }, + "typeName" : + { + "id" : 8, + "name" : "address", + "nodeType" : "ElementaryTypeName", + "src" : "115:15:1", + "stateMutability" : "payable", + "typeDescriptions" : + { + "typeIdentifier" : "t_address_payable", + "typeString" : "address payable" + } + }, + "value" : null, + "visibility" : "internal" + } + ], + "src" : "114:19:1" + }, + "scope" : 37, + "src" : "67:189:1", + "stateMutability" : "nonpayable", + "superFunction" : null, + "visibility" : "public" + } + ], + "scope" : 38, + "src" : "0:258:1" + } + ], + "src" : "0:259:1" +} diff --git a/test/libsolidity/ASTJSON/address_payable.sol b/test/libsolidity/ASTJSON/address_payable.sol new file mode 100644 index 00000000..f7cc66cb --- /dev/null +++ b/test/libsolidity/ASTJSON/address_payable.sol @@ -0,0 +1,11 @@ +contract C { + mapping(address => address payable) public m; + function f(address payable arg) public returns (address payable r) { + address payable a = m[arg]; + r = arg; + address c = address(this); + m[c] = address(0); + } +} + +// ---- diff --git a/test/libsolidity/ASTJSON/address_payable_legacy.json b/test/libsolidity/ASTJSON/address_payable_legacy.json new file mode 100644 index 00000000..dd8a5582 --- /dev/null +++ b/test/libsolidity/ASTJSON/address_payable_legacy.json @@ -0,0 +1,601 @@ +{ + "attributes" : + { + "absolutePath" : "a", + "exportedSymbols" : + { + "C" : + [ + 37 + ] + } + }, + "children" : + [ + { + "attributes" : + { + "baseContracts" : + [ + null + ], + "contractDependencies" : + [ + null + ], + "contractKind" : "contract", + "documentation" : null, + "fullyImplemented" : true, + "linearizedBaseContracts" : + [ + 37 + ], + "name" : "C", + "scope" : 38 + }, + "children" : + [ + { + "attributes" : + { + "constant" : false, + "name" : "m", + "scope" : 37, + "stateVariable" : true, + "storageLocation" : "default", + "type" : "mapping(address => address payable)", + "value" : null, + "visibility" : "public" + }, + "children" : + [ + { + "attributes" : + { + "type" : "mapping(address => address payable)" + }, + "children" : + [ + { + "attributes" : + { + "name" : "address", + "type" : "address" + }, + "id" : 1, + "name" : "ElementaryTypeName", + "src" : "25:7:1" + }, + { + "attributes" : + { + "name" : "address", + "stateMutability" : "payable", + "type" : "address payable" + }, + "id" : 2, + "name" : "ElementaryTypeName", + "src" : "36:15:1" + } + ], + "id" : 3, + "name" : "Mapping", + "src" : "17:35:1" + } + ], + "id" : 4, + "name" : "VariableDeclaration", + "src" : "17:44:1" + }, + { + "attributes" : + { + "documentation" : null, + "implemented" : true, + "isConstructor" : false, + "kind" : "function", + "modifiers" : + [ + null + ], + "name" : "f", + "scope" : 37, + "stateMutability" : "nonpayable", + "superFunction" : null, + "visibility" : "public" + }, + "children" : + [ + { + "children" : + [ + { + "attributes" : + { + "constant" : false, + "name" : "arg", + "scope" : 36, + "stateVariable" : false, + "storageLocation" : "default", + "type" : "address payable", + "value" : null, + "visibility" : "internal" + }, + "children" : + [ + { + "attributes" : + { + "name" : "address", + "stateMutability" : "payable", + "type" : "address payable" + }, + "id" : 5, + "name" : "ElementaryTypeName", + "src" : "78:15:1" + } + ], + "id" : 6, + "name" : "VariableDeclaration", + "src" : "78:19:1" + } + ], + "id" : 7, + "name" : "ParameterList", + "src" : "77:21:1" + }, + { + "children" : + [ + { + "attributes" : + { + "constant" : false, + "name" : "r", + "scope" : 36, + "stateVariable" : false, + "storageLocation" : "default", + "type" : "address payable", + "value" : null, + "visibility" : "internal" + }, + "children" : + [ + { + "attributes" : + { + "name" : "address", + "stateMutability" : "payable", + "type" : "address payable" + }, + "id" : 8, + "name" : "ElementaryTypeName", + "src" : "115:15:1" + } + ], + "id" : 9, + "name" : "VariableDeclaration", + "src" : "115:17:1" + } + ], + "id" : 10, + "name" : "ParameterList", + "src" : "114:19:1" + }, + { + "children" : + [ + { + "attributes" : + { + "assignments" : + [ + 12 + ] + }, + "children" : + [ + { + "attributes" : + { + "constant" : false, + "name" : "a", + "scope" : 35, + "stateVariable" : false, + "storageLocation" : "default", + "type" : "address payable", + "value" : null, + "visibility" : "internal" + }, + "children" : + [ + { + "attributes" : + { + "name" : "address", + "stateMutability" : "payable", + "type" : "address payable" + }, + "id" : 11, + "name" : "ElementaryTypeName", + "src" : "144:15:1" + } + ], + "id" : 12, + "name" : "VariableDeclaration", + "src" : "144:17:1" + }, + { + "attributes" : + { + "argumentTypes" : null, + "isConstant" : false, + "isLValue" : true, + "isPure" : false, + "lValueRequested" : false, + "type" : "address payable" + }, + "children" : + [ + { + "attributes" : + { + "argumentTypes" : null, + "overloadedDeclarations" : + [ + null + ], + "referencedDeclaration" : 4, + "type" : "mapping(address => address payable)", + "value" : "m" + }, + "id" : 13, + "name" : "Identifier", + "src" : "164:1:1" + }, + { + "attributes" : + { + "argumentTypes" : null, + "overloadedDeclarations" : + [ + null + ], + "referencedDeclaration" : 6, + "type" : "address payable", + "value" : "arg" + }, + "id" : 14, + "name" : "Identifier", + "src" : "166:3:1" + } + ], + "id" : 15, + "name" : "IndexAccess", + "src" : "164:6:1" + } + ], + "id" : 16, + "name" : "VariableDeclarationStatement", + "src" : "144:26:1" + }, + { + "children" : + [ + { + "attributes" : + { + "argumentTypes" : null, + "isConstant" : false, + "isLValue" : false, + "isPure" : false, + "lValueRequested" : false, + "operator" : "=", + "type" : "address payable" + }, + "children" : + [ + { + "attributes" : + { + "argumentTypes" : null, + "overloadedDeclarations" : + [ + null + ], + "referencedDeclaration" : 9, + "type" : "address payable", + "value" : "r" + }, + "id" : 17, + "name" : "Identifier", + "src" : "180:1:1" + }, + { + "attributes" : + { + "argumentTypes" : null, + "overloadedDeclarations" : + [ + null + ], + "referencedDeclaration" : 6, + "type" : "address payable", + "value" : "arg" + }, + "id" : 18, + "name" : "Identifier", + "src" : "184:3:1" + } + ], + "id" : 19, + "name" : "Assignment", + "src" : "180:7:1" + } + ], + "id" : 20, + "name" : "ExpressionStatement", + "src" : "180:7:1" + }, + { + "attributes" : + { + "assignments" : + [ + 22 + ] + }, + "children" : + [ + { + "attributes" : + { + "constant" : false, + "name" : "c", + "scope" : 35, + "stateVariable" : false, + "storageLocation" : "default", + "type" : "address", + "value" : null, + "visibility" : "internal" + }, + "children" : + [ + { + "attributes" : + { + "name" : "address", + "stateMutability" : "nonpayable", + "type" : "address" + }, + "id" : 21, + "name" : "ElementaryTypeName", + "src" : "197:7:1" + } + ], + "id" : 22, + "name" : "VariableDeclaration", + "src" : "197:9:1" + }, + { + "attributes" : + { + "argumentTypes" : null, + "isConstant" : false, + "isLValue" : false, + "isPure" : false, + "isStructConstructorCall" : false, + "lValueRequested" : false, + "names" : + [ + null + ], + "type" : "address", + "type_conversion" : true + }, + "children" : + [ + { + "attributes" : + { + "argumentTypes" : + [ + { + "typeIdentifier" : "t_contract$_C_$37", + "typeString" : "contract C" + } + ], + "isConstant" : false, + "isLValue" : false, + "isPure" : true, + "lValueRequested" : false, + "type" : "type(address)", + "value" : "address" + }, + "id" : 23, + "name" : "ElementaryTypeNameExpression", + "src" : "209:7:1" + }, + { + "attributes" : + { + "argumentTypes" : null, + "overloadedDeclarations" : + [ + null + ], + "referencedDeclaration" : 65, + "type" : "contract C", + "value" : "this" + }, + "id" : 24, + "name" : "Identifier", + "src" : "217:4:1" + } + ], + "id" : 25, + "name" : "FunctionCall", + "src" : "209:13:1" + } + ], + "id" : 26, + "name" : "VariableDeclarationStatement", + "src" : "197:25:1" + }, + { + "children" : + [ + { + "attributes" : + { + "argumentTypes" : null, + "isConstant" : false, + "isLValue" : false, + "isPure" : false, + "lValueRequested" : false, + "operator" : "=", + "type" : "address payable" + }, + "children" : + [ + { + "attributes" : + { + "argumentTypes" : null, + "isConstant" : false, + "isLValue" : true, + "isPure" : false, + "lValueRequested" : true, + "type" : "address payable" + }, + "children" : + [ + { + "attributes" : + { + "argumentTypes" : null, + "overloadedDeclarations" : + [ + null + ], + "referencedDeclaration" : 4, + "type" : "mapping(address => address payable)", + "value" : "m" + }, + "id" : 27, + "name" : "Identifier", + "src" : "232:1:1" + }, + { + "attributes" : + { + "argumentTypes" : null, + "overloadedDeclarations" : + [ + null + ], + "referencedDeclaration" : 22, + "type" : "address", + "value" : "c" + }, + "id" : 28, + "name" : "Identifier", + "src" : "234:1:1" + } + ], + "id" : 29, + "name" : "IndexAccess", + "src" : "232:4:1" + }, + { + "attributes" : + { + "argumentTypes" : null, + "isConstant" : false, + "isLValue" : false, + "isPure" : true, + "isStructConstructorCall" : false, + "lValueRequested" : false, + "names" : + [ + null + ], + "type" : "address payable", + "type_conversion" : true + }, + "children" : + [ + { + "attributes" : + { + "argumentTypes" : + [ + { + "typeIdentifier" : "t_rational_0_by_1", + "typeString" : "int_const 0" + } + ], + "isConstant" : false, + "isLValue" : false, + "isPure" : true, + "lValueRequested" : false, + "type" : "type(address)", + "value" : "address" + }, + "id" : 30, + "name" : "ElementaryTypeNameExpression", + "src" : "239:7:1" + }, + { + "attributes" : + { + "argumentTypes" : null, + "hexvalue" : "30", + "isConstant" : false, + "isLValue" : false, + "isPure" : true, + "lValueRequested" : false, + "subdenomination" : null, + "token" : "number", + "type" : "int_const 0", + "value" : "0" + }, + "id" : 31, + "name" : "Literal", + "src" : "247:1:1" + } + ], + "id" : 32, + "name" : "FunctionCall", + "src" : "239:10:1" + } + ], + "id" : 33, + "name" : "Assignment", + "src" : "232:17:1" + } + ], + "id" : 34, + "name" : "ExpressionStatement", + "src" : "232:17:1" + } + ], + "id" : 35, + "name" : "Block", + "src" : "134:122:1" + } + ], + "id" : 36, + "name" : "FunctionDefinition", + "src" : "67:189:1" + } + ], + "id" : 37, + "name" : "ContractDefinition", + "src" : "0:258:1" + } + ], + "id" : 38, + "name" : "SourceUnit", + "src" : "0:259:1" +} diff --git a/test/libsolidity/ASTJSON/array_type_name.json b/test/libsolidity/ASTJSON/array_type_name.json new file mode 100644 index 00000000..e3a3bea9 --- /dev/null +++ b/test/libsolidity/ASTJSON/array_type_name.json @@ -0,0 +1,76 @@ +{ + "absolutePath" : "a", + "exportedSymbols" : + { + "C" : + [ + 4 + ] + }, + "id" : 5, + "nodeType" : "SourceUnit", + "nodes" : + [ + { + "baseContracts" : [], + "contractDependencies" : [], + "contractKind" : "contract", + "documentation" : null, + "fullyImplemented" : true, + "id" : 4, + "linearizedBaseContracts" : + [ + 4 + ], + "name" : "C", + "nodeType" : "ContractDefinition", + "nodes" : + [ + { + "constant" : false, + "id" : 3, + "name" : "i", + "nodeType" : "VariableDeclaration", + "scope" : 4, + "src" : "13:8:1", + "stateVariable" : true, + "storageLocation" : "default", + "typeDescriptions" : + { + "typeIdentifier" : "t_array$_t_uint256_$dyn_storage", + "typeString" : "uint256[]" + }, + "typeName" : + { + "baseType" : + { + "id" : 1, + "name" : "uint", + "nodeType" : "ElementaryTypeName", + "src" : "13:4:1", + "typeDescriptions" : + { + "typeIdentifier" : "t_uint256", + "typeString" : "uint256" + } + }, + "id" : 2, + "length" : null, + "nodeType" : "ArrayTypeName", + "src" : "13:6:1", + "typeDescriptions" : + { + "typeIdentifier" : "t_array$_t_uint256_$dyn_storage_ptr", + "typeString" : "uint256[]" + } + }, + "value" : null, + "visibility" : "internal" + } + ], + "scope" : 5, + "src" : "0:24:1" + } + ], + "src" : "0:25:1" +} diff --git a/test/libsolidity/ASTJSON/array_type_name.sol b/test/libsolidity/ASTJSON/array_type_name.sol new file mode 100644 index 00000000..202ecf02 --- /dev/null +++ b/test/libsolidity/ASTJSON/array_type_name.sol @@ -0,0 +1 @@ +contract C { uint[] i; } diff --git a/test/libsolidity/ASTJSON/array_type_name_legacy.json b/test/libsolidity/ASTJSON/array_type_name_legacy.json new file mode 100644 index 00000000..80feb344 --- /dev/null +++ b/test/libsolidity/ASTJSON/array_type_name_legacy.json @@ -0,0 +1,89 @@ +{ + "attributes" : + { + "absolutePath" : "a", + "exportedSymbols" : + { + "C" : + [ + 4 + ] + } + }, + "children" : + [ + { + "attributes" : + { + "baseContracts" : + [ + null + ], + "contractDependencies" : + [ + null + ], + "contractKind" : "contract", + "documentation" : null, + "fullyImplemented" : true, + "linearizedBaseContracts" : + [ + 4 + ], + "name" : "C", + "scope" : 5 + }, + "children" : + [ + { + "attributes" : + { + "constant" : false, + "name" : "i", + "scope" : 4, + "stateVariable" : true, + "storageLocation" : "default", + "type" : "uint256[]", + "value" : null, + "visibility" : "internal" + }, + "children" : + [ + { + "attributes" : + { + "length" : null, + "type" : "uint256[]" + }, + "children" : + [ + { + "attributes" : + { + "name" : "uint", + "type" : "uint256" + }, + "id" : 1, + "name" : "ElementaryTypeName", + "src" : "13:4:1" + } + ], + "id" : 2, + "name" : "ArrayTypeName", + "src" : "13:6:1" + } + ], + "id" : 3, + "name" : "VariableDeclaration", + "src" : "13:8:1" + } + ], + "id" : 4, + "name" : "ContractDefinition", + "src" : "0:24:1" + } + ], + "id" : 5, + "name" : "SourceUnit", + "src" : "0:25:1" +} diff --git a/test/libsolidity/ASTJSON/constructor.json b/test/libsolidity/ASTJSON/constructor.json new file mode 100644 index 00000000..b0bc4201 --- /dev/null +++ b/test/libsolidity/ASTJSON/constructor.json @@ -0,0 +1,70 @@ +{ + "absolutePath" : "a", + "exportedSymbols" : + { + "C" : + [ + 5 + ] + }, + "id" : 6, + "nodeType" : "SourceUnit", + "nodes" : + [ + { + "baseContracts" : [], + "contractDependencies" : [], + "contractKind" : "contract", + "documentation" : null, + "fullyImplemented" : true, + "id" : 5, + "linearizedBaseContracts" : + [ + 5 + ], + "name" : "C", + "nodeType" : "ContractDefinition", + "nodes" : + [ + { + "body" : + { + "id" : 3, + "nodeType" : "Block", + "src" : "35:4:1", + "statements" : [] + }, + "documentation" : null, + "id" : 4, + "implemented" : true, + "kind" : "constructor", + "modifiers" : [], + "name" : "", + "nodeType" : "FunctionDefinition", + "parameters" : + { + "id" : 1, + "nodeType" : "ParameterList", + "parameters" : [], + "src" : "25:2:1" + }, + "returnParameters" : + { + "id" : 2, + "nodeType" : "ParameterList", + "parameters" : [], + "src" : "35:0:1" + }, + "scope" : 5, + "src" : "14:25:1", + "stateMutability" : "nonpayable", + "superFunction" : null, + "visibility" : "public" + } + ], + "scope" : 6, + "src" : "0:41:1" + } + ], + "src" : "0:42:1" +} diff --git a/test/libsolidity/ASTJSON/constructor.sol b/test/libsolidity/ASTJSON/constructor.sol new file mode 100644 index 00000000..79d04eb5 --- /dev/null +++ b/test/libsolidity/ASTJSON/constructor.sol @@ -0,0 +1,4 @@ +contract C { + constructor() public { + } +} diff --git a/test/libsolidity/ASTJSON/constructor_legacy.json b/test/libsolidity/ASTJSON/constructor_legacy.json new file mode 100644 index 00000000..0617073e --- /dev/null +++ b/test/libsolidity/ASTJSON/constructor_legacy.json @@ -0,0 +1,110 @@ +{ + "attributes" : + { + "absolutePath" : "a", + "exportedSymbols" : + { + "C" : + [ + 5 + ] + } + }, + "children" : + [ + { + "attributes" : + { + "baseContracts" : + [ + null + ], + "contractDependencies" : + [ + null + ], + "contractKind" : "contract", + "documentation" : null, + "fullyImplemented" : true, + "linearizedBaseContracts" : + [ + 5 + ], + "name" : "C", + "scope" : 6 + }, + "children" : + [ + { + "attributes" : + { + "documentation" : null, + "implemented" : true, + "isConstructor" : true, + "kind" : "constructor", + "modifiers" : + [ + null + ], + "name" : "", + "scope" : 5, + "stateMutability" : "nonpayable", + "superFunction" : null, + "visibility" : "public" + }, + "children" : + [ + { + "attributes" : + { + "parameters" : + [ + null + ] + }, + "children" : [], + "id" : 1, + "name" : "ParameterList", + "src" : "25:2:1" + }, + { + "attributes" : + { + "parameters" : + [ + null + ] + }, + "children" : [], + "id" : 2, + "name" : "ParameterList", + "src" : "35:0:1" + }, + { + "attributes" : + { + "statements" : + [ + null + ] + }, + "children" : [], + "id" : 3, + "name" : "Block", + "src" : "35:4:1" + } + ], + "id" : 4, + "name" : "FunctionDefinition", + "src" : "14:25:1" + } + ], + "id" : 5, + "name" : "ContractDefinition", + "src" : "0:41:1" + } + ], + "id" : 6, + "name" : "SourceUnit", + "src" : "0:42:1" +} diff --git a/test/libsolidity/ASTJSON/documentation.json b/test/libsolidity/ASTJSON/documentation.json new file mode 100644 index 00000000..ce1e0b57 --- /dev/null +++ b/test/libsolidity/ASTJSON/documentation.json @@ -0,0 +1,180 @@ +{ + "absolutePath" : "a", + "exportedSymbols" : + { + "C" : + [ + 1 + ] + }, + "id" : 2, + "nodeType" : "SourceUnit", + "nodes" : + [ + { + "baseContracts" : [], + "contractDependencies" : [], + "contractKind" : "contract", + "documentation" : "This contract is empty", + "fullyImplemented" : true, + "id" : 1, + "linearizedBaseContracts" : + [ + 1 + ], + "name" : "C", + "nodeType" : "ContractDefinition", + "nodes" : [], + "scope" : 2, + "src" : "28:13:1" + } + ], + "src" : "28:14:1" +}, +{ + "absolutePath" : "b", + "exportedSymbols" : + { + "C" : + [ + 3 + ] + }, + "id" : 4, + "nodeType" : "SourceUnit", + "nodes" : + [ + { + "baseContracts" : [], + "contractDependencies" : [], + "contractKind" : "contract", + "documentation" : "This contract is empty\nand has a line-breaking comment.", + "fullyImplemented" : true, + "id" : 3, + "linearizedBaseContracts" : + [ + 3 + ], + "name" : "C", + "nodeType" : "ContractDefinition", + "nodes" : [], + "scope" : 4, + "src" : "62:13:2" + } + ], + "src" : "62:14:2" +}, +{ + "absolutePath" : "c", + "exportedSymbols" : + { + "C" : + [ + 15 + ] + }, + "id" : 16, + "nodeType" : "SourceUnit", + "nodes" : + [ + { + "baseContracts" : [], + "contractDependencies" : [], + "contractKind" : "contract", + "documentation" : null, + "fullyImplemented" : true, + "id" : 15, + "linearizedBaseContracts" : + [ + 15 + ], + "name" : "C", + "nodeType" : "ContractDefinition", + "nodes" : + [ + { + "anonymous" : false, + "documentation" : "Some comment on Evt.", + "id" : 6, + "name" : "Evt", + "nodeType" : "EventDefinition", + "parameters" : + { + "id" : 5, + "nodeType" : "ParameterList", + "parameters" : [], + "src" : "51:2:3" + }, + "src" : "42:12:3" + }, + { + "body" : + { + "id" : 9, + "nodeType" : "Block", + "src" : "99:6:3", + "statements" : + [ + { + "id" : 8, + "nodeType" : "PlaceholderStatement", + "src" : "101:1:3" + } + ] + }, + "documentation" : "Some comment on mod.", + "id" : 10, + "name" : "mod", + "nodeType" : "ModifierDefinition", + "parameters" : + { + "id" : 7, + "nodeType" : "ParameterList", + "parameters" : [], + "src" : "96:2:3" + }, + "src" : "84:21:3", + "visibility" : "internal" + }, + { + "body" : + { + "id" : 13, + "nodeType" : "Block", + "src" : "155:2:3", + "statements" : [] + }, + "documentation" : "Some comment on fn.", + "id" : 14, + "implemented" : true, + "kind" : "function", + "modifiers" : [], + "name" : "fn", + "nodeType" : "FunctionDefinition", + "parameters" : + { + "id" : 11, + "nodeType" : "ParameterList", + "parameters" : [], + "src" : "145:2:3" + }, + "returnParameters" : + { + "id" : 12, + "nodeType" : "ParameterList", + "parameters" : [], + "src" : "155:0:3" + }, + "scope" : 15, + "src" : "134:23:3", + "stateMutability" : "nonpayable", + "superFunction" : null, + "visibility" : "public" + } + ], + "scope" : 16, + "src" : "0:159:3" + } + ], + "src" : "0:160:3" +} diff --git a/test/libsolidity/ASTJSON/documentation.sol b/test/libsolidity/ASTJSON/documentation.sol new file mode 100644 index 00000000..e65af9b4 --- /dev/null +++ b/test/libsolidity/ASTJSON/documentation.sol @@ -0,0 +1,17 @@ +// ---- SOURCE: a + +/**This contract is empty*/ contract C {} + +// ---- SOURCE: b + +/**This contract is empty + and has a line-breaking comment.*/ +contract C {} + +// ---- SOURCE: c + +contract C { + /** Some comment on Evt.*/ event Evt(); + /** Some comment on mod.*/ modifier mod() { _; } + /** Some comment on fn.*/ function fn() public {} +} diff --git a/test/libsolidity/ASTJSON/documentation_legacy.json b/test/libsolidity/ASTJSON/documentation_legacy.json new file mode 100644 index 00000000..0277902f --- /dev/null +++ b/test/libsolidity/ASTJSON/documentation_legacy.json @@ -0,0 +1,177 @@ +{ + "attributes" : + { + "absolutePath" : "c", + "exportedSymbols" : + { + "C" : + [ + 15 + ] + } + }, + "children" : + [ + { + "attributes" : + { + "baseContracts" : + [ + null + ], + "contractDependencies" : + [ + null + ], + "contractKind" : "contract", + "documentation" : null, + "fullyImplemented" : true, + "linearizedBaseContracts" : + [ + 15 + ], + "name" : "C", + "scope" : 16 + }, + "children" : + [ + { + "attributes" : + { + "anonymous" : false, + "documentation" : "Some comment on Evt.", + "name" : "Evt" + }, + "children" : + [ + { + "attributes" : + { + "parameters" : + [ + null + ] + }, + "children" : [], + "id" : 5, + "name" : "ParameterList", + "src" : "51:2:3" + } + ], + "id" : 6, + "name" : "EventDefinition", + "src" : "42:12:3" + }, + { + "attributes" : + { + "documentation" : "Some comment on mod.", + "name" : "mod", + "visibility" : "internal" + }, + "children" : + [ + { + "attributes" : + { + "parameters" : + [ + null + ] + }, + "children" : [], + "id" : 7, + "name" : "ParameterList", + "src" : "96:2:3" + }, + { + "children" : + [ + { + "id" : 8, + "name" : "PlaceholderStatement", + "src" : "101:1:3" + } + ], + "id" : 9, + "name" : "Block", + "src" : "99:6:3" + } + ], + "id" : 10, + "name" : "ModifierDefinition", + "src" : "84:21:3" + }, + { + "attributes" : + { + "documentation" : "Some comment on fn.", + "implemented" : true, + "isConstructor" : false, + "kind" : "function", + "modifiers" : + [ + null + ], + "name" : "fn", + "scope" : 15, + "stateMutability" : "nonpayable", + "superFunction" : null, + "visibility" : "public" + }, + "children" : + [ + { + "attributes" : + { + "parameters" : + [ + null + ] + }, + "children" : [], + "id" : 11, + "name" : "ParameterList", + "src" : "145:2:3" + }, + { + "attributes" : + { + "parameters" : + [ + null + ] + }, + "children" : [], + "id" : 12, + "name" : "ParameterList", + "src" : "155:0:3" + }, + { + "attributes" : + { + "statements" : + [ + null + ] + }, + "children" : [], + "id" : 13, + "name" : "Block", + "src" : "155:2:3" + } + ], + "id" : 14, + "name" : "FunctionDefinition", + "src" : "134:23:3" + } + ], + "id" : 15, + "name" : "ContractDefinition", + "src" : "0:159:3" + } + ], + "id" : 16, + "name" : "SourceUnit", + "src" : "0:160:3" +} diff --git a/test/libsolidity/ASTJSON/enum_value.json b/test/libsolidity/ASTJSON/enum_value.json new file mode 100644 index 00000000..21afd9a7 --- /dev/null +++ b/test/libsolidity/ASTJSON/enum_value.json @@ -0,0 +1,57 @@ +{ + "absolutePath" : "a", + "exportedSymbols" : + { + "C" : + [ + 4 + ] + }, + "id" : 5, + "nodeType" : "SourceUnit", + "nodes" : + [ + { + "baseContracts" : [], + "contractDependencies" : [], + "contractKind" : "contract", + "documentation" : null, + "fullyImplemented" : true, + "id" : 4, + "linearizedBaseContracts" : + [ + 4 + ], + "name" : "C", + "nodeType" : "ContractDefinition", + "nodes" : + [ + { + "canonicalName" : "C.E", + "id" : 3, + "members" : + [ + { + "id" : 1, + "name" : "A", + "nodeType" : "EnumValue", + "src" : "22:1:1" + }, + { + "id" : 2, + "name" : "B", + "nodeType" : "EnumValue", + "src" : "25:1:1" + } + ], + "name" : "E", + "nodeType" : "EnumDefinition", + "src" : "13:15:1" + } + ], + "scope" : 5, + "src" : "0:30:1" + } + ], + "src" : "0:31:1" +} diff --git a/test/libsolidity/ASTJSON/enum_value.sol b/test/libsolidity/ASTJSON/enum_value.sol new file mode 100644 index 00000000..ef0875fb --- /dev/null +++ b/test/libsolidity/ASTJSON/enum_value.sol @@ -0,0 +1 @@ +contract C { enum E { A, B } } diff --git a/test/libsolidity/ASTJSON/enum_value_legacy.json b/test/libsolidity/ASTJSON/enum_value_legacy.json new file mode 100644 index 00000000..d7782969 --- /dev/null +++ b/test/libsolidity/ASTJSON/enum_value_legacy.json @@ -0,0 +1,78 @@ +{ + "attributes" : + { + "absolutePath" : "a", + "exportedSymbols" : + { + "C" : + [ + 4 + ] + } + }, + "children" : + [ + { + "attributes" : + { + "baseContracts" : + [ + null + ], + "contractDependencies" : + [ + null + ], + "contractKind" : "contract", + "documentation" : null, + "fullyImplemented" : true, + "linearizedBaseContracts" : + [ + 4 + ], + "name" : "C", + "scope" : 5 + }, + "children" : + [ + { + "attributes" : + { + "canonicalName" : "C.E", + "name" : "E" + }, + "children" : + [ + { + "attributes" : + { + "name" : "A" + }, + "id" : 1, + "name" : "EnumValue", + "src" : "22:1:1" + }, + { + "attributes" : + { + "name" : "B" + }, + "id" : 2, + "name" : "EnumValue", + "src" : "25:1:1" + } + ], + "id" : 3, + "name" : "EnumDefinition", + "src" : "13:15:1" + } + ], + "id" : 4, + "name" : "ContractDefinition", + "src" : "0:30:1" + } + ], + "id" : 5, + "name" : "SourceUnit", + "src" : "0:31:1" +} diff --git a/test/libsolidity/ASTJSON/event_definition.json b/test/libsolidity/ASTJSON/event_definition.json new file mode 100644 index 00000000..029062c3 --- /dev/null +++ b/test/libsolidity/ASTJSON/event_definition.json @@ -0,0 +1,50 @@ +{ + "absolutePath" : "a", + "exportedSymbols" : + { + "C" : + [ + 3 + ] + }, + "id" : 4, + "nodeType" : "SourceUnit", + "nodes" : + [ + { + "baseContracts" : [], + "contractDependencies" : [], + "contractKind" : "contract", + "documentation" : null, + "fullyImplemented" : true, + "id" : 3, + "linearizedBaseContracts" : + [ + 3 + ], + "name" : "C", + "nodeType" : "ContractDefinition", + "nodes" : + [ + { + "anonymous" : false, + "documentation" : null, + "id" : 2, + "name" : "E", + "nodeType" : "EventDefinition", + "parameters" : + { + "id" : 1, + "nodeType" : "ParameterList", + "parameters" : [], + "src" : "20:2:1" + }, + "src" : "13:10:1" + } + ], + "scope" : 4, + "src" : "0:25:1" + } + ], + "src" : "0:26:1" +} diff --git a/test/libsolidity/ASTJSON/event_definition.sol b/test/libsolidity/ASTJSON/event_definition.sol new file mode 100644 index 00000000..81b43c67 --- /dev/null +++ b/test/libsolidity/ASTJSON/event_definition.sol @@ -0,0 +1 @@ +contract C { event E(); } diff --git a/test/libsolidity/ASTJSON/event_definition_legacy.json b/test/libsolidity/ASTJSON/event_definition_legacy.json new file mode 100644 index 00000000..f5967bf4 --- /dev/null +++ b/test/libsolidity/ASTJSON/event_definition_legacy.json @@ -0,0 +1,74 @@ +{ + "attributes" : + { + "absolutePath" : "a", + "exportedSymbols" : + { + "C" : + [ + 3 + ] + } + }, + "children" : + [ + { + "attributes" : + { + "baseContracts" : + [ + null + ], + "contractDependencies" : + [ + null + ], + "contractKind" : "contract", + "documentation" : null, + "fullyImplemented" : true, + "linearizedBaseContracts" : + [ + 3 + ], + "name" : "C", + "scope" : 4 + }, + "children" : + [ + { + "attributes" : + { + "anonymous" : false, + "documentation" : null, + "name" : "E" + }, + "children" : + [ + { + "attributes" : + { + "parameters" : + [ + null + ] + }, + "children" : [], + "id" : 1, + "name" : "ParameterList", + "src" : "20:2:1" + } + ], + "id" : 2, + "name" : "EventDefinition", + "src" : "13:10:1" + } + ], + "id" : 3, + "name" : "ContractDefinition", + "src" : "0:25:1" + } + ], + "id" : 4, + "name" : "SourceUnit", + "src" : "0:26:1" +} diff --git a/test/libsolidity/ASTJSON/fallback.json b/test/libsolidity/ASTJSON/fallback.json new file mode 100644 index 00000000..a9c85b2f --- /dev/null +++ b/test/libsolidity/ASTJSON/fallback.json @@ -0,0 +1,70 @@ +{ + "absolutePath" : "a", + "exportedSymbols" : + { + "C" : + [ + 5 + ] + }, + "id" : 6, + "nodeType" : "SourceUnit", + "nodes" : + [ + { + "baseContracts" : [], + "contractDependencies" : [], + "contractKind" : "contract", + "documentation" : null, + "fullyImplemented" : true, + "id" : 5, + "linearizedBaseContracts" : + [ + 5 + ], + "name" : "C", + "nodeType" : "ContractDefinition", + "nodes" : + [ + { + "body" : + { + "id" : 3, + "nodeType" : "Block", + "src" : "43:5:1", + "statements" : [] + }, + "documentation" : null, + "id" : 4, + "implemented" : true, + "kind" : "fallback", + "modifiers" : [], + "name" : "", + "nodeType" : "FunctionDefinition", + "parameters" : + { + "id" : 1, + "nodeType" : "ParameterList", + "parameters" : [], + "src" : "23:2:1" + }, + "returnParameters" : + { + "id" : 2, + "nodeType" : "ParameterList", + "parameters" : [], + "src" : "43:0:1" + }, + "scope" : 5, + "src" : "15:33:1", + "stateMutability" : "payable", + "superFunction" : null, + "visibility" : "external" + } + ], + "scope" : 6, + "src" : "0:50:1" + } + ], + "src" : "0:51:1" +} diff --git a/test/libsolidity/ASTJSON/fallback.sol b/test/libsolidity/ASTJSON/fallback.sol new file mode 100644 index 00000000..4e318892 --- /dev/null +++ b/test/libsolidity/ASTJSON/fallback.sol @@ -0,0 +1,4 @@ +contract C { + function() external payable { + } +} diff --git a/test/libsolidity/ASTJSON/fallback_legacy.json b/test/libsolidity/ASTJSON/fallback_legacy.json new file mode 100644 index 00000000..0aca3128 --- /dev/null +++ b/test/libsolidity/ASTJSON/fallback_legacy.json @@ -0,0 +1,110 @@ +{ + "attributes" : + { + "absolutePath" : "a", + "exportedSymbols" : + { + "C" : + [ + 5 + ] + } + }, + "children" : + [ + { + "attributes" : + { + "baseContracts" : + [ + null + ], + "contractDependencies" : + [ + null + ], + "contractKind" : "contract", + "documentation" : null, + "fullyImplemented" : true, + "linearizedBaseContracts" : + [ + 5 + ], + "name" : "C", + "scope" : 6 + }, + "children" : + [ + { + "attributes" : + { + "documentation" : null, + "implemented" : true, + "isConstructor" : false, + "kind" : "fallback", + "modifiers" : + [ + null + ], + "name" : "", + "scope" : 5, + "stateMutability" : "payable", + "superFunction" : null, + "visibility" : "external" + }, + "children" : + [ + { + "attributes" : + { + "parameters" : + [ + null + ] + }, + "children" : [], + "id" : 1, + "name" : "ParameterList", + "src" : "23:2:1" + }, + { + "attributes" : + { + "parameters" : + [ + null + ] + }, + "children" : [], + "id" : 2, + "name" : "ParameterList", + "src" : "43:0:1" + }, + { + "attributes" : + { + "statements" : + [ + null + ] + }, + "children" : [], + "id" : 3, + "name" : "Block", + "src" : "43:5:1" + } + ], + "id" : 4, + "name" : "FunctionDefinition", + "src" : "15:33:1" + } + ], + "id" : 5, + "name" : "ContractDefinition", + "src" : "0:50:1" + } + ], + "id" : 6, + "name" : "SourceUnit", + "src" : "0:51:1" +} diff --git a/test/libsolidity/ASTJSON/fallback_payable.json b/test/libsolidity/ASTJSON/fallback_payable.json new file mode 100644 index 00000000..9d56f74b --- /dev/null +++ b/test/libsolidity/ASTJSON/fallback_payable.json @@ -0,0 +1,70 @@ +{ + "absolutePath" : "a", + "exportedSymbols" : + { + "C" : + [ + 5 + ] + }, + "id" : 6, + "nodeType" : "SourceUnit", + "nodes" : + [ + { + "baseContracts" : [], + "contractDependencies" : [], + "contractKind" : "contract", + "documentation" : null, + "fullyImplemented" : true, + "id" : 5, + "linearizedBaseContracts" : + [ + 5 + ], + "name" : "C", + "nodeType" : "ContractDefinition", + "nodes" : + [ + { + "body" : + { + "id" : 3, + "nodeType" : "Block", + "src" : "34:2:1", + "statements" : [] + }, + "documentation" : null, + "id" : 4, + "implemented" : true, + "kind" : "fallback", + "modifiers" : [], + "name" : "", + "nodeType" : "FunctionDefinition", + "parameters" : + { + "id" : 1, + "nodeType" : "ParameterList", + "parameters" : [], + "src" : "22:2:1" + }, + "returnParameters" : + { + "id" : 2, + "nodeType" : "ParameterList", + "parameters" : [], + "src" : "34:0:1" + }, + "scope" : 5, + "src" : "14:22:1", + "stateMutability" : "nonpayable", + "superFunction" : null, + "visibility" : "external" + } + ], + "scope" : 6, + "src" : "0:38:1" + } + ], + "src" : "0:39:1" +} diff --git a/test/libsolidity/ASTJSON/fallback_payable.sol b/test/libsolidity/ASTJSON/fallback_payable.sol new file mode 100644 index 00000000..21db99ec --- /dev/null +++ b/test/libsolidity/ASTJSON/fallback_payable.sol @@ -0,0 +1,3 @@ +contract C { + function() external {} +} diff --git a/test/libsolidity/ASTJSON/fallback_payable_legacy.json b/test/libsolidity/ASTJSON/fallback_payable_legacy.json new file mode 100644 index 00000000..7320f574 --- /dev/null +++ b/test/libsolidity/ASTJSON/fallback_payable_legacy.json @@ -0,0 +1,110 @@ +{ + "attributes" : + { + "absolutePath" : "a", + "exportedSymbols" : + { + "C" : + [ + 5 + ] + } + }, + "children" : + [ + { + "attributes" : + { + "baseContracts" : + [ + null + ], + "contractDependencies" : + [ + null + ], + "contractKind" : "contract", + "documentation" : null, + "fullyImplemented" : true, + "linearizedBaseContracts" : + [ + 5 + ], + "name" : "C", + "scope" : 6 + }, + "children" : + [ + { + "attributes" : + { + "documentation" : null, + "implemented" : true, + "isConstructor" : false, + "kind" : "fallback", + "modifiers" : + [ + null + ], + "name" : "", + "scope" : 5, + "stateMutability" : "nonpayable", + "superFunction" : null, + "visibility" : "external" + }, + "children" : + [ + { + "attributes" : + { + "parameters" : + [ + null + ] + }, + "children" : [], + "id" : 1, + "name" : "ParameterList", + "src" : "22:2:1" + }, + { + "attributes" : + { + "parameters" : + [ + null + ] + }, + "children" : [], + "id" : 2, + "name" : "ParameterList", + "src" : "34:0:1" + }, + { + "attributes" : + { + "statements" : + [ + null + ] + }, + "children" : [], + "id" : 3, + "name" : "Block", + "src" : "34:2:1" + } + ], + "id" : 4, + "name" : "FunctionDefinition", + "src" : "14:22:1" + } + ], + "id" : 5, + "name" : "ContractDefinition", + "src" : "0:38:1" + } + ], + "id" : 6, + "name" : "SourceUnit", + "src" : "0:39:1" +} diff --git a/test/libsolidity/ASTJSON/function_type.json b/test/libsolidity/ASTJSON/function_type.json new file mode 100644 index 00000000..b78d8446 --- /dev/null +++ b/test/libsolidity/ASTJSON/function_type.json @@ -0,0 +1,224 @@ +{ + "absolutePath" : "a", + "exportedSymbols" : + { + "C" : + [ + 17 + ] + }, + "id" : 18, + "nodeType" : "SourceUnit", + "nodes" : + [ + { + "baseContracts" : [], + "contractDependencies" : [], + "contractKind" : "contract", + "documentation" : null, + "fullyImplemented" : true, + "id" : 17, + "linearizedBaseContracts" : + [ + 17 + ], + "name" : "C", + "nodeType" : "ContractDefinition", + "nodes" : + [ + { + "body" : + { + "id" : 15, + "nodeType" : "Block", + "src" : "120:2:1", + "statements" : [] + }, + "documentation" : null, + "id" : 16, + "implemented" : true, + "kind" : "function", + "modifiers" : [], + "name" : "f", + "nodeType" : "FunctionDefinition", + "parameters" : + { + "id" : 7, + "nodeType" : "ParameterList", + "parameters" : + [ + { + "constant" : false, + "id" : 6, + "name" : "x", + "nodeType" : "VariableDeclaration", + "scope" : 16, + "src" : "24:44:1", + "stateVariable" : false, + "storageLocation" : "default", + "typeDescriptions" : + { + "typeIdentifier" : "t_function_external_payable$__$returns$_t_uint256_$", + "typeString" : "function () payable external returns (uint256)" + }, + "typeName" : + { + "id" : 5, + "nodeType" : "FunctionTypeName", + "parameterTypes" : + { + "id" : 1, + "nodeType" : "ParameterList", + "parameters" : [], + "src" : "32:2:1" + }, + "returnParameterTypes" : + { + "id" : 4, + "nodeType" : "ParameterList", + "parameters" : + [ + { + "constant" : false, + "id" : 3, + "name" : "", + "nodeType" : "VariableDeclaration", + "scope" : 5, + "src" : "61:4:1", + "stateVariable" : false, + "storageLocation" : "default", + "typeDescriptions" : + { + "typeIdentifier" : "t_uint256", + "typeString" : "uint256" + }, + "typeName" : + { + "id" : 2, + "name" : "uint", + "nodeType" : "ElementaryTypeName", + "src" : "61:4:1", + "typeDescriptions" : + { + "typeIdentifier" : "t_uint256", + "typeString" : "uint256" + } + }, + "value" : null, + "visibility" : "internal" + } + ], + "src" : "60:6:1" + }, + "src" : "24:44:1", + "stateMutability" : "payable", + "typeDescriptions" : + { + "typeIdentifier" : "t_function_external_payable$__$returns$_t_uint256_$", + "typeString" : "function () payable external returns (uint256)" + }, + "visibility" : "external" + }, + "value" : null, + "visibility" : "internal" + } + ], + "src" : "23:46:1" + }, + "returnParameters" : + { + "id" : 14, + "nodeType" : "ParameterList", + "parameters" : + [ + { + "constant" : false, + "id" : 13, + "name" : "", + "nodeType" : "VariableDeclaration", + "scope" : 16, + "src" : "79:40:1", + "stateVariable" : false, + "storageLocation" : "default", + "typeDescriptions" : + { + "typeIdentifier" : "t_function_external_view$__$returns$_t_uint256_$", + "typeString" : "function () view external returns (uint256)" + }, + "typeName" : + { + "id" : 12, + "nodeType" : "FunctionTypeName", + "parameterTypes" : + { + "id" : 8, + "nodeType" : "ParameterList", + "parameters" : [], + "src" : "87:2:1" + }, + "returnParameterTypes" : + { + "id" : 11, + "nodeType" : "ParameterList", + "parameters" : + [ + { + "constant" : false, + "id" : 10, + "name" : "", + "nodeType" : "VariableDeclaration", + "scope" : 12, + "src" : "113:4:1", + "stateVariable" : false, + "storageLocation" : "default", + "typeDescriptions" : + { + "typeIdentifier" : "t_uint256", + "typeString" : "uint256" + }, + "typeName" : + { + "id" : 9, + "name" : "uint", + "nodeType" : "ElementaryTypeName", + "src" : "113:4:1", + "typeDescriptions" : + { + "typeIdentifier" : "t_uint256", + "typeString" : "uint256" + } + }, + "value" : null, + "visibility" : "internal" + } + ], + "src" : "112:6:1" + }, + "src" : "79:40:1", + "stateMutability" : "view", + "typeDescriptions" : + { + "typeIdentifier" : "t_function_external_view$__$returns$_t_uint256_$", + "typeString" : "function () view external returns (uint256)" + }, + "visibility" : "external" + }, + "value" : null, + "visibility" : "internal" + } + ], + "src" : "78:41:1" + }, + "scope" : 17, + "src" : "13:109:1", + "stateMutability" : "nonpayable", + "superFunction" : null, + "visibility" : "public" + } + ], + "scope" : 18, + "src" : "0:124:1" + } + ], + "src" : "0:125:1" +} diff --git a/test/libsolidity/ASTJSON/function_type.sol b/test/libsolidity/ASTJSON/function_type.sol new file mode 100644 index 00000000..bed2742b --- /dev/null +++ b/test/libsolidity/ASTJSON/function_type.sol @@ -0,0 +1,3 @@ +contract C { function f(function() external payable returns (uint) x) returns (function() external view returns (uint)) {} } + +// ---- diff --git a/test/libsolidity/ASTJSON/function_type_legacy.json b/test/libsolidity/ASTJSON/function_type_legacy.json new file mode 100644 index 00000000..72ceec81 --- /dev/null +++ b/test/libsolidity/ASTJSON/function_type_legacy.json @@ -0,0 +1,266 @@ +{ + "attributes" : + { + "absolutePath" : "a", + "exportedSymbols" : + { + "C" : + [ + 17 + ] + } + }, + "children" : + [ + { + "attributes" : + { + "baseContracts" : + [ + null + ], + "contractDependencies" : + [ + null + ], + "contractKind" : "contract", + "documentation" : null, + "fullyImplemented" : true, + "linearizedBaseContracts" : + [ + 17 + ], + "name" : "C", + "scope" : 18 + }, + "children" : + [ + { + "attributes" : + { + "documentation" : null, + "implemented" : true, + "isConstructor" : false, + "kind" : "function", + "modifiers" : + [ + null + ], + "name" : "f", + "scope" : 17, + "stateMutability" : "nonpayable", + "superFunction" : null, + "visibility" : "public" + }, + "children" : + [ + { + "children" : + [ + { + "attributes" : + { + "constant" : false, + "name" : "x", + "scope" : 16, + "stateVariable" : false, + "storageLocation" : "default", + "type" : "function () payable external returns (uint256)", + "value" : null, + "visibility" : "internal" + }, + "children" : + [ + { + "attributes" : + { + "stateMutability" : "payable", + "type" : "function () payable external returns (uint256)", + "visibility" : "external" + }, + "children" : + [ + { + "attributes" : + { + "parameters" : + [ + null + ] + }, + "children" : [], + "id" : 1, + "name" : "ParameterList", + "src" : "32:2:1" + }, + { + "children" : + [ + { + "attributes" : + { + "constant" : false, + "name" : "", + "scope" : 5, + "stateVariable" : false, + "storageLocation" : "default", + "type" : "uint256", + "value" : null, + "visibility" : "internal" + }, + "children" : + [ + { + "attributes" : + { + "name" : "uint", + "type" : "uint256" + }, + "id" : 2, + "name" : "ElementaryTypeName", + "src" : "61:4:1" + } + ], + "id" : 3, + "name" : "VariableDeclaration", + "src" : "61:4:1" + } + ], + "id" : 4, + "name" : "ParameterList", + "src" : "60:6:1" + } + ], + "id" : 5, + "name" : "FunctionTypeName", + "src" : "24:44:1" + } + ], + "id" : 6, + "name" : "VariableDeclaration", + "src" : "24:44:1" + } + ], + "id" : 7, + "name" : "ParameterList", + "src" : "23:46:1" + }, + { + "children" : + [ + { + "attributes" : + { + "constant" : false, + "name" : "", + "scope" : 16, + "stateVariable" : false, + "storageLocation" : "default", + "type" : "function () view external returns (uint256)", + "value" : null, + "visibility" : "internal" + }, + "children" : + [ + { + "attributes" : + { + "stateMutability" : "view", + "type" : "function () view external returns (uint256)", + "visibility" : "external" + }, + "children" : + [ + { + "attributes" : + { + "parameters" : + [ + null + ] + }, + "children" : [], + "id" : 8, + "name" : "ParameterList", + "src" : "87:2:1" + }, + { + "children" : + [ + { + "attributes" : + { + "constant" : false, + "name" : "", + "scope" : 12, + "stateVariable" : false, + "storageLocation" : "default", + "type" : "uint256", + "value" : null, + "visibility" : "internal" + }, + "children" : + [ + { + "attributes" : + { + "name" : "uint", + "type" : "uint256" + }, + "id" : 9, + "name" : "ElementaryTypeName", + "src" : "113:4:1" + } + ], + "id" : 10, + "name" : "VariableDeclaration", + "src" : "113:4:1" + } + ], + "id" : 11, + "name" : "ParameterList", + "src" : "112:6:1" + } + ], + "id" : 12, + "name" : "FunctionTypeName", + "src" : "79:40:1" + } + ], + "id" : 13, + "name" : "VariableDeclaration", + "src" : "79:40:1" + } + ], + "id" : 14, + "name" : "ParameterList", + "src" : "78:41:1" + }, + { + "attributes" : + { + "statements" : + [ + null + ] + }, + "children" : [], + "id" : 15, + "name" : "Block", + "src" : "120:2:1" + } + ], + "id" : 16, + "name" : "FunctionDefinition", + "src" : "13:109:1" + } + ], + "id" : 17, + "name" : "ContractDefinition", + "src" : "0:124:1" + } + ], + "id" : 18, + "name" : "SourceUnit", + "src" : "0:125:1" +} diff --git a/test/libsolidity/ASTJSON/inheritance_specifier.json b/test/libsolidity/ASTJSON/inheritance_specifier.json new file mode 100644 index 00000000..edef8677 --- /dev/null +++ b/test/libsolidity/ASTJSON/inheritance_specifier.json @@ -0,0 +1,80 @@ +{ + "absolutePath" : "a", + "exportedSymbols" : + { + "C1" : + [ + 1 + ], + "C2" : + [ + 4 + ] + }, + "id" : 5, + "nodeType" : "SourceUnit", + "nodes" : + [ + { + "baseContracts" : [], + "contractDependencies" : [], + "contractKind" : "contract", + "documentation" : null, + "fullyImplemented" : true, + "id" : 1, + "linearizedBaseContracts" : + [ + 1 + ], + "name" : "C1", + "nodeType" : "ContractDefinition", + "nodes" : [], + "scope" : 5, + "src" : "0:14:1" + }, + { + "baseContracts" : + [ + { + "arguments" : null, + "baseName" : + { + "contractScope" : null, + "id" : 2, + "name" : "C1", + "nodeType" : "UserDefinedTypeName", + "referencedDeclaration" : 1, + "src" : "30:2:1", + "typeDescriptions" : + { + "typeIdentifier" : "t_contract$_C1_$1", + "typeString" : "contract C1" + } + }, + "id" : 3, + "nodeType" : "InheritanceSpecifier", + "src" : "30:2:1" + } + ], + "contractDependencies" : + [ + 1 + ], + "contractKind" : "contract", + "documentation" : null, + "fullyImplemented" : true, + "id" : 4, + "linearizedBaseContracts" : + [ + 4, + 1 + ], + "name" : "C2", + "nodeType" : "ContractDefinition", + "nodes" : [], + "scope" : 5, + "src" : "15:20:1" + } + ], + "src" : "0:36:1" +} diff --git a/test/libsolidity/ASTJSON/inheritance_specifier.sol b/test/libsolidity/ASTJSON/inheritance_specifier.sol new file mode 100644 index 00000000..02dbf0c5 --- /dev/null +++ b/test/libsolidity/ASTJSON/inheritance_specifier.sol @@ -0,0 +1 @@ +contract C1 {} contract C2 is C1 {} diff --git a/test/libsolidity/ASTJSON/inheritance_specifier_legacy.json b/test/libsolidity/ASTJSON/inheritance_specifier_legacy.json new file mode 100644 index 00000000..0fcf2939 --- /dev/null +++ b/test/libsolidity/ASTJSON/inheritance_specifier_legacy.json @@ -0,0 +1,105 @@ +{ + "attributes" : + { + "absolutePath" : "a", + "exportedSymbols" : + { + "C1" : + [ + 1 + ], + "C2" : + [ + 4 + ] + } + }, + "children" : + [ + { + "attributes" : + { + "baseContracts" : + [ + null + ], + "contractDependencies" : + [ + null + ], + "contractKind" : "contract", + "documentation" : null, + "fullyImplemented" : true, + "linearizedBaseContracts" : + [ + 1 + ], + "name" : "C1", + "nodes" : + [ + null + ], + "scope" : 5 + }, + "id" : 1, + "name" : "ContractDefinition", + "src" : "0:14:1" + }, + { + "attributes" : + { + "contractDependencies" : + [ + 1 + ], + "contractKind" : "contract", + "documentation" : null, + "fullyImplemented" : true, + "linearizedBaseContracts" : + [ + 4, + 1 + ], + "name" : "C2", + "nodes" : + [ + null + ], + "scope" : 5 + }, + "children" : + [ + { + "attributes" : + { + "arguments" : null + }, + "children" : + [ + { + "attributes" : + { + "contractScope" : null, + "name" : "C1", + "referencedDeclaration" : 1, + "type" : "contract C1" + }, + "id" : 2, + "name" : "UserDefinedTypeName", + "src" : "30:2:1" + } + ], + "id" : 3, + "name" : "InheritanceSpecifier", + "src" : "30:2:1" + } + ], + "id" : 4, + "name" : "ContractDefinition", + "src" : "15:20:1" + } + ], + "id" : 5, + "name" : "SourceUnit", + "src" : "0:36:1" +} diff --git a/test/libsolidity/ASTJSON/long_type_name_binary_operation.json b/test/libsolidity/ASTJSON/long_type_name_binary_operation.json new file mode 100644 index 00000000..c6d40af2 --- /dev/null +++ b/test/libsolidity/ASTJSON/long_type_name_binary_operation.json @@ -0,0 +1,175 @@ +{ + "absolutePath" : "a", + "exportedSymbols" : + { + "c" : + [ + 11 + ] + }, + "id" : 12, + "nodeType" : "SourceUnit", + "nodes" : + [ + { + "baseContracts" : [], + "contractDependencies" : [], + "contractKind" : "contract", + "documentation" : null, + "fullyImplemented" : true, + "id" : 11, + "linearizedBaseContracts" : + [ + 11 + ], + "name" : "c", + "nodeType" : "ContractDefinition", + "nodes" : + [ + { + "body" : + { + "id" : 9, + "nodeType" : "Block", + "src" : "33:19:1", + "statements" : + [ + { + "assignments" : + [ + 4 + ], + "declarations" : + [ + { + "constant" : false, + "id" : 4, + "name" : "a", + "nodeType" : "VariableDeclaration", + "scope" : 9, + "src" : "35:6:1", + "stateVariable" : false, + "storageLocation" : "default", + "typeDescriptions" : + { + "typeIdentifier" : "t_uint256", + "typeString" : "uint256" + }, + "typeName" : + { + "id" : 3, + "name" : "uint", + "nodeType" : "ElementaryTypeName", + "src" : "35:4:1", + "typeDescriptions" : + { + "typeIdentifier" : "t_uint256", + "typeString" : "uint256" + } + }, + "value" : null, + "visibility" : "internal" + } + ], + "id" : 8, + "initialValue" : + { + "argumentTypes" : null, + "commonType" : + { + "typeIdentifier" : "t_rational_5_by_1", + "typeString" : "int_const 5" + }, + "id" : 7, + "isConstant" : false, + "isLValue" : false, + "isPure" : true, + "lValueRequested" : false, + "leftExpression" : + { + "argumentTypes" : null, + "hexValue" : "32", + "id" : 5, + "isConstant" : false, + "isLValue" : false, + "isPure" : true, + "kind" : "number", + "lValueRequested" : false, + "nodeType" : "Literal", + "src" : "44:1:1", + "subdenomination" : null, + "typeDescriptions" : + { + "typeIdentifier" : "t_rational_2_by_1", + "typeString" : "int_const 2" + }, + "value" : "2" + }, + "nodeType" : "BinaryOperation", + "operator" : "+", + "rightExpression" : + { + "argumentTypes" : null, + "hexValue" : "33", + "id" : 6, + "isConstant" : false, + "isLValue" : false, + "isPure" : true, + "kind" : "number", + "lValueRequested" : false, + "nodeType" : "Literal", + "src" : "48:1:1", + "subdenomination" : null, + "typeDescriptions" : + { + "typeIdentifier" : "t_rational_3_by_1", + "typeString" : "int_const 3" + }, + "value" : "3" + }, + "src" : "44:5:1", + "typeDescriptions" : + { + "typeIdentifier" : "t_rational_5_by_1", + "typeString" : "int_const 5" + } + }, + "nodeType" : "VariableDeclarationStatement", + "src" : "35:14:1" + } + ] + }, + "documentation" : null, + "id" : 10, + "implemented" : true, + "kind" : "function", + "modifiers" : [], + "name" : "f", + "nodeType" : "FunctionDefinition", + "parameters" : + { + "id" : 1, + "nodeType" : "ParameterList", + "parameters" : [], + "src" : "23:2:1" + }, + "returnParameters" : + { + "id" : 2, + "nodeType" : "ParameterList", + "parameters" : [], + "src" : "33:0:1" + }, + "scope" : 11, + "src" : "13:39:1", + "stateMutability" : "nonpayable", + "superFunction" : null, + "visibility" : "public" + } + ], + "scope" : 12, + "src" : "0:54:1" + } + ], + "src" : "0:55:1" +} diff --git a/test/libsolidity/ASTJSON/long_type_name_binary_operation.sol b/test/libsolidity/ASTJSON/long_type_name_binary_operation.sol new file mode 100644 index 00000000..f07029d7 --- /dev/null +++ b/test/libsolidity/ASTJSON/long_type_name_binary_operation.sol @@ -0,0 +1 @@ +contract c { function f() public { uint a = 2 + 3; } } diff --git a/test/libsolidity/ASTJSON/long_type_name_binary_operation_legacy.json b/test/libsolidity/ASTJSON/long_type_name_binary_operation_legacy.json new file mode 100644 index 00000000..b5333286 --- /dev/null +++ b/test/libsolidity/ASTJSON/long_type_name_binary_operation_legacy.json @@ -0,0 +1,208 @@ +{ + "attributes" : + { + "absolutePath" : "a", + "exportedSymbols" : + { + "c" : + [ + 11 + ] + } + }, + "children" : + [ + { + "attributes" : + { + "baseContracts" : + [ + null + ], + "contractDependencies" : + [ + null + ], + "contractKind" : "contract", + "documentation" : null, + "fullyImplemented" : true, + "linearizedBaseContracts" : + [ + 11 + ], + "name" : "c", + "scope" : 12 + }, + "children" : + [ + { + "attributes" : + { + "documentation" : null, + "implemented" : true, + "isConstructor" : false, + "kind" : "function", + "modifiers" : + [ + null + ], + "name" : "f", + "scope" : 11, + "stateMutability" : "nonpayable", + "superFunction" : null, + "visibility" : "public" + }, + "children" : + [ + { + "attributes" : + { + "parameters" : + [ + null + ] + }, + "children" : [], + "id" : 1, + "name" : "ParameterList", + "src" : "23:2:1" + }, + { + "attributes" : + { + "parameters" : + [ + null + ] + }, + "children" : [], + "id" : 2, + "name" : "ParameterList", + "src" : "33:0:1" + }, + { + "children" : + [ + { + "attributes" : + { + "assignments" : + [ + 4 + ] + }, + "children" : + [ + { + "attributes" : + { + "constant" : false, + "name" : "a", + "scope" : 9, + "stateVariable" : false, + "storageLocation" : "default", + "type" : "uint256", + "value" : null, + "visibility" : "internal" + }, + "children" : + [ + { + "attributes" : + { + "name" : "uint", + "type" : "uint256" + }, + "id" : 3, + "name" : "ElementaryTypeName", + "src" : "35:4:1" + } + ], + "id" : 4, + "name" : "VariableDeclaration", + "src" : "35:6:1" + }, + { + "attributes" : + { + "argumentTypes" : null, + "commonType" : + { + "typeIdentifier" : "t_rational_5_by_1", + "typeString" : "int_const 5" + }, + "isConstant" : false, + "isLValue" : false, + "isPure" : true, + "lValueRequested" : false, + "operator" : "+", + "type" : "int_const 5" + }, + "children" : + [ + { + "attributes" : + { + "argumentTypes" : null, + "hexvalue" : "32", + "isConstant" : false, + "isLValue" : false, + "isPure" : true, + "lValueRequested" : false, + "subdenomination" : null, + "token" : "number", + "type" : "int_const 2", + "value" : "2" + }, + "id" : 5, + "name" : "Literal", + "src" : "44:1:1" + }, + { + "attributes" : + { + "argumentTypes" : null, + "hexvalue" : "33", + "isConstant" : false, + "isLValue" : false, + "isPure" : true, + "lValueRequested" : false, + "subdenomination" : null, + "token" : "number", + "type" : "int_const 3", + "value" : "3" + }, + "id" : 6, + "name" : "Literal", + "src" : "48:1:1" + } + ], + "id" : 7, + "name" : "BinaryOperation", + "src" : "44:5:1" + } + ], + "id" : 8, + "name" : "VariableDeclarationStatement", + "src" : "35:14:1" + } + ], + "id" : 9, + "name" : "Block", + "src" : "33:19:1" + } + ], + "id" : 10, + "name" : "FunctionDefinition", + "src" : "13:39:1" + } + ], + "id" : 11, + "name" : "ContractDefinition", + "src" : "0:54:1" + } + ], + "id" : 12, + "name" : "SourceUnit", + "src" : "0:55:1" +} diff --git a/test/libsolidity/ASTJSON/long_type_name_identifier.json b/test/libsolidity/ASTJSON/long_type_name_identifier.json new file mode 100644 index 00000000..505d260c --- /dev/null +++ b/test/libsolidity/ASTJSON/long_type_name_identifier.json @@ -0,0 +1,181 @@ +{ + "absolutePath" : "a", + "exportedSymbols" : + { + "c" : + [ + 14 + ] + }, + "id" : 15, + "nodeType" : "SourceUnit", + "nodes" : + [ + { + "baseContracts" : [], + "contractDependencies" : [], + "contractKind" : "contract", + "documentation" : null, + "fullyImplemented" : true, + "id" : 14, + "linearizedBaseContracts" : + [ + 14 + ], + "name" : "c", + "nodeType" : "ContractDefinition", + "nodes" : + [ + { + "constant" : false, + "id" : 3, + "name" : "a", + "nodeType" : "VariableDeclaration", + "scope" : 14, + "src" : "13:8:1", + "stateVariable" : true, + "storageLocation" : "default", + "typeDescriptions" : + { + "typeIdentifier" : "t_array$_t_uint256_$dyn_storage", + "typeString" : "uint256[]" + }, + "typeName" : + { + "baseType" : + { + "id" : 1, + "name" : "uint", + "nodeType" : "ElementaryTypeName", + "src" : "13:4:1", + "typeDescriptions" : + { + "typeIdentifier" : "t_uint256", + "typeString" : "uint256" + } + }, + "id" : 2, + "length" : null, + "nodeType" : "ArrayTypeName", + "src" : "13:6:1", + "typeDescriptions" : + { + "typeIdentifier" : "t_array$_t_uint256_$dyn_storage_ptr", + "typeString" : "uint256[]" + } + }, + "value" : null, + "visibility" : "internal" + }, + { + "body" : + { + "id" : 12, + "nodeType" : "Block", + "src" : "43:25:1", + "statements" : + [ + { + "assignments" : + [ + 9 + ], + "declarations" : + [ + { + "constant" : false, + "id" : 9, + "name" : "b", + "nodeType" : "VariableDeclaration", + "scope" : 12, + "src" : "45:16:1", + "stateVariable" : false, + "storageLocation" : "storage", + "typeDescriptions" : + { + "typeIdentifier" : "t_array$_t_uint256_$dyn_storage_ptr", + "typeString" : "uint256[]" + }, + "typeName" : + { + "baseType" : + { + "id" : 7, + "name" : "uint", + "nodeType" : "ElementaryTypeName", + "src" : "45:4:1", + "typeDescriptions" : + { + "typeIdentifier" : "t_uint256", + "typeString" : "uint256" + } + }, + "id" : 8, + "length" : null, + "nodeType" : "ArrayTypeName", + "src" : "45:6:1", + "typeDescriptions" : + { + "typeIdentifier" : "t_array$_t_uint256_$dyn_storage_ptr", + "typeString" : "uint256[]" + } + }, + "value" : null, + "visibility" : "internal" + } + ], + "id" : 11, + "initialValue" : + { + "argumentTypes" : null, + "id" : 10, + "name" : "a", + "nodeType" : "Identifier", + "overloadedDeclarations" : [], + "referencedDeclaration" : 3, + "src" : "64:1:1", + "typeDescriptions" : + { + "typeIdentifier" : "t_array$_t_uint256_$dyn_storage", + "typeString" : "uint256[] storage ref" + } + }, + "nodeType" : "VariableDeclarationStatement", + "src" : "45:20:1" + } + ] + }, + "documentation" : null, + "id" : 13, + "implemented" : true, + "kind" : "function", + "modifiers" : [], + "name" : "f", + "nodeType" : "FunctionDefinition", + "parameters" : + { + "id" : 4, + "nodeType" : "ParameterList", + "parameters" : [], + "src" : "33:2:1" + }, + "returnParameters" : + { + "id" : 5, + "nodeType" : "ParameterList", + "parameters" : [], + "src" : "43:0:1" + }, + "scope" : 14, + "src" : "23:45:1", + "stateMutability" : "nonpayable", + "superFunction" : null, + "visibility" : "public" + } + ], + "scope" : 15, + "src" : "0:70:1" + } + ], + "src" : "0:71:1" +} diff --git a/test/libsolidity/ASTJSON/long_type_name_identifier.sol b/test/libsolidity/ASTJSON/long_type_name_identifier.sol new file mode 100644 index 00000000..f03f7a84 --- /dev/null +++ b/test/libsolidity/ASTJSON/long_type_name_identifier.sol @@ -0,0 +1 @@ +contract c { uint[] a; function f() public { uint[] storage b = a; } } diff --git a/test/libsolidity/ASTJSON/long_type_name_identifier_legacy.json b/test/libsolidity/ASTJSON/long_type_name_identifier_legacy.json new file mode 100644 index 00000000..d3bcda56 --- /dev/null +++ b/test/libsolidity/ASTJSON/long_type_name_identifier_legacy.json @@ -0,0 +1,220 @@ +{ + "attributes" : + { + "absolutePath" : "a", + "exportedSymbols" : + { + "c" : + [ + 14 + ] + } + }, + "children" : + [ + { + "attributes" : + { + "baseContracts" : + [ + null + ], + "contractDependencies" : + [ + null + ], + "contractKind" : "contract", + "documentation" : null, + "fullyImplemented" : true, + "linearizedBaseContracts" : + [ + 14 + ], + "name" : "c", + "scope" : 15 + }, + "children" : + [ + { + "attributes" : + { + "constant" : false, + "name" : "a", + "scope" : 14, + "stateVariable" : true, + "storageLocation" : "default", + "type" : "uint256[]", + "value" : null, + "visibility" : "internal" + }, + "children" : + [ + { + "attributes" : + { + "length" : null, + "type" : "uint256[]" + }, + "children" : + [ + { + "attributes" : + { + "name" : "uint", + "type" : "uint256" + }, + "id" : 1, + "name" : "ElementaryTypeName", + "src" : "13:4:1" + } + ], + "id" : 2, + "name" : "ArrayTypeName", + "src" : "13:6:1" + } + ], + "id" : 3, + "name" : "VariableDeclaration", + "src" : "13:8:1" + }, + { + "attributes" : + { + "documentation" : null, + "implemented" : true, + "isConstructor" : false, + "kind" : "function", + "modifiers" : + [ + null + ], + "name" : "f", + "scope" : 14, + "stateMutability" : "nonpayable", + "superFunction" : null, + "visibility" : "public" + }, + "children" : + [ + { + "attributes" : + { + "parameters" : + [ + null + ] + }, + "children" : [], + "id" : 4, + "name" : "ParameterList", + "src" : "33:2:1" + }, + { + "attributes" : + { + "parameters" : + [ + null + ] + }, + "children" : [], + "id" : 5, + "name" : "ParameterList", + "src" : "43:0:1" + }, + { + "children" : + [ + { + "attributes" : + { + "assignments" : + [ + 9 + ] + }, + "children" : + [ + { + "attributes" : + { + "constant" : false, + "name" : "b", + "scope" : 12, + "stateVariable" : false, + "storageLocation" : "storage", + "type" : "uint256[]", + "value" : null, + "visibility" : "internal" + }, + "children" : + [ + { + "attributes" : + { + "length" : null, + "type" : "uint256[]" + }, + "children" : + [ + { + "attributes" : + { + "name" : "uint", + "type" : "uint256" + }, + "id" : 7, + "name" : "ElementaryTypeName", + "src" : "45:4:1" + } + ], + "id" : 8, + "name" : "ArrayTypeName", + "src" : "45:6:1" + } + ], + "id" : 9, + "name" : "VariableDeclaration", + "src" : "45:16:1" + }, + { + "attributes" : + { + "argumentTypes" : null, + "overloadedDeclarations" : + [ + null + ], + "referencedDeclaration" : 3, + "type" : "uint256[] storage ref", + "value" : "a" + }, + "id" : 10, + "name" : "Identifier", + "src" : "64:1:1" + } + ], + "id" : 11, + "name" : "VariableDeclarationStatement", + "src" : "45:20:1" + } + ], + "id" : 12, + "name" : "Block", + "src" : "43:25:1" + } + ], + "id" : 13, + "name" : "FunctionDefinition", + "src" : "23:45:1" + } + ], + "id" : 14, + "name" : "ContractDefinition", + "src" : "0:70:1" + } + ], + "id" : 15, + "name" : "SourceUnit", + "src" : "0:71:1" +} diff --git a/test/libsolidity/ASTJSON/modifier_definition.json b/test/libsolidity/ASTJSON/modifier_definition.json new file mode 100644 index 00000000..66359453 --- /dev/null +++ b/test/libsolidity/ASTJSON/modifier_definition.json @@ -0,0 +1,174 @@ +{ + "absolutePath" : "a", + "exportedSymbols" : + { + "C" : + [ + 14 + ] + }, + "id" : 15, + "nodeType" : "SourceUnit", + "nodes" : + [ + { + "baseContracts" : [], + "contractDependencies" : [], + "contractKind" : "contract", + "documentation" : null, + "fullyImplemented" : true, + "id" : 14, + "linearizedBaseContracts" : + [ + 14 + ], + "name" : "C", + "nodeType" : "ContractDefinition", + "nodes" : + [ + { + "body" : + { + "id" : 5, + "nodeType" : "Block", + "src" : "32:6:1", + "statements" : + [ + { + "id" : 4, + "nodeType" : "PlaceholderStatement", + "src" : "34:1:1" + } + ] + }, + "documentation" : null, + "id" : 6, + "name" : "M", + "nodeType" : "ModifierDefinition", + "parameters" : + { + "id" : 3, + "nodeType" : "ParameterList", + "parameters" : + [ + { + "constant" : false, + "id" : 2, + "name" : "i", + "nodeType" : "VariableDeclaration", + "scope" : 6, + "src" : "24:6:1", + "stateVariable" : false, + "storageLocation" : "default", + "typeDescriptions" : + { + "typeIdentifier" : "t_uint256", + "typeString" : "uint256" + }, + "typeName" : + { + "id" : 1, + "name" : "uint", + "nodeType" : "ElementaryTypeName", + "src" : "24:4:1", + "typeDescriptions" : + { + "typeIdentifier" : "t_uint256", + "typeString" : "uint256" + } + }, + "value" : null, + "visibility" : "internal" + } + ], + "src" : "23:8:1" + }, + "src" : "13:25:1", + "visibility" : "internal" + }, + { + "body" : + { + "id" : 12, + "nodeType" : "Block", + "src" : "64:2:1", + "statements" : [] + }, + "documentation" : null, + "id" : 13, + "implemented" : true, + "kind" : "function", + "modifiers" : + [ + { + "arguments" : + [ + { + "argumentTypes" : null, + "hexValue" : "31", + "id" : 9, + "isConstant" : false, + "isLValue" : false, + "isPure" : true, + "kind" : "number", + "lValueRequested" : false, + "nodeType" : "Literal", + "src" : "54:1:1", + "subdenomination" : null, + "typeDescriptions" : + { + "typeIdentifier" : "t_rational_1_by_1", + "typeString" : "int_const 1" + }, + "value" : "1" + } + ], + "id" : 10, + "modifierName" : + { + "argumentTypes" : null, + "id" : 8, + "name" : "M", + "nodeType" : "Identifier", + "overloadedDeclarations" : [], + "referencedDeclaration" : 6, + "src" : "52:1:1", + "typeDescriptions" : + { + "typeIdentifier" : "t_modifier$_t_uint256_$", + "typeString" : "modifier (uint256)" + } + }, + "nodeType" : "ModifierInvocation", + "src" : "52:4:1" + } + ], + "name" : "F", + "nodeType" : "FunctionDefinition", + "parameters" : + { + "id" : 7, + "nodeType" : "ParameterList", + "parameters" : [], + "src" : "49:2:1" + }, + "returnParameters" : + { + "id" : 11, + "nodeType" : "ParameterList", + "parameters" : [], + "src" : "64:0:1" + }, + "scope" : 14, + "src" : "39:27:1", + "stateMutability" : "nonpayable", + "superFunction" : null, + "visibility" : "public" + } + ], + "scope" : 15, + "src" : "0:68:1" + } + ], + "src" : "0:69:1" +} diff --git a/test/libsolidity/ASTJSON/modifier_definition.sol b/test/libsolidity/ASTJSON/modifier_definition.sol new file mode 100644 index 00000000..96474e0f --- /dev/null +++ b/test/libsolidity/ASTJSON/modifier_definition.sol @@ -0,0 +1 @@ +contract C { modifier M(uint i) { _; } function F() M(1) public {} } diff --git a/test/libsolidity/ASTJSON/modifier_definition_legacy.json b/test/libsolidity/ASTJSON/modifier_definition_legacy.json new file mode 100644 index 00000000..5186912c --- /dev/null +++ b/test/libsolidity/ASTJSON/modifier_definition_legacy.json @@ -0,0 +1,212 @@ +{ + "attributes" : + { + "absolutePath" : "a", + "exportedSymbols" : + { + "C" : + [ + 14 + ] + } + }, + "children" : + [ + { + "attributes" : + { + "baseContracts" : + [ + null + ], + "contractDependencies" : + [ + null + ], + "contractKind" : "contract", + "documentation" : null, + "fullyImplemented" : true, + "linearizedBaseContracts" : + [ + 14 + ], + "name" : "C", + "scope" : 15 + }, + "children" : + [ + { + "attributes" : + { + "documentation" : null, + "name" : "M", + "visibility" : "internal" + }, + "children" : + [ + { + "children" : + [ + { + "attributes" : + { + "constant" : false, + "name" : "i", + "scope" : 6, + "stateVariable" : false, + "storageLocation" : "default", + "type" : "uint256", + "value" : null, + "visibility" : "internal" + }, + "children" : + [ + { + "attributes" : + { + "name" : "uint", + "type" : "uint256" + }, + "id" : 1, + "name" : "ElementaryTypeName", + "src" : "24:4:1" + } + ], + "id" : 2, + "name" : "VariableDeclaration", + "src" : "24:6:1" + } + ], + "id" : 3, + "name" : "ParameterList", + "src" : "23:8:1" + }, + { + "children" : + [ + { + "id" : 4, + "name" : "PlaceholderStatement", + "src" : "34:1:1" + } + ], + "id" : 5, + "name" : "Block", + "src" : "32:6:1" + } + ], + "id" : 6, + "name" : "ModifierDefinition", + "src" : "13:25:1" + }, + { + "attributes" : + { + "documentation" : null, + "implemented" : true, + "isConstructor" : false, + "kind" : "function", + "name" : "F", + "scope" : 14, + "stateMutability" : "nonpayable", + "superFunction" : null, + "visibility" : "public" + }, + "children" : + [ + { + "attributes" : + { + "parameters" : + [ + null + ] + }, + "children" : [], + "id" : 7, + "name" : "ParameterList", + "src" : "49:2:1" + }, + { + "attributes" : + { + "parameters" : + [ + null + ] + }, + "children" : [], + "id" : 11, + "name" : "ParameterList", + "src" : "64:0:1" + }, + { + "children" : + [ + { + "attributes" : + { + "argumentTypes" : null, + "overloadedDeclarations" : + [ + null + ], + "referencedDeclaration" : 6, + "type" : "modifier (uint256)", + "value" : "M" + }, + "id" : 8, + "name" : "Identifier", + "src" : "52:1:1" + }, + { + "attributes" : + { + "argumentTypes" : null, + "hexvalue" : "31", + "isConstant" : false, + "isLValue" : false, + "isPure" : true, + "lValueRequested" : false, + "subdenomination" : null, + "token" : "number", + "type" : "int_const 1", + "value" : "1" + }, + "id" : 9, + "name" : "Literal", + "src" : "54:1:1" + } + ], + "id" : 10, + "name" : "ModifierInvocation", + "src" : "52:4:1" + }, + { + "attributes" : + { + "statements" : + [ + null + ] + }, + "children" : [], + "id" : 12, + "name" : "Block", + "src" : "64:2:1" + } + ], + "id" : 13, + "name" : "FunctionDefinition", + "src" : "39:27:1" + } + ], + "id" : 14, + "name" : "ContractDefinition", + "src" : "0:68:1" + } + ], + "id" : 15, + "name" : "SourceUnit", + "src" : "0:69:1" +} diff --git a/test/libsolidity/ASTJSON/modifier_invocation.json b/test/libsolidity/ASTJSON/modifier_invocation.json new file mode 100644 index 00000000..66359453 --- /dev/null +++ b/test/libsolidity/ASTJSON/modifier_invocation.json @@ -0,0 +1,174 @@ +{ + "absolutePath" : "a", + "exportedSymbols" : + { + "C" : + [ + 14 + ] + }, + "id" : 15, + "nodeType" : "SourceUnit", + "nodes" : + [ + { + "baseContracts" : [], + "contractDependencies" : [], + "contractKind" : "contract", + "documentation" : null, + "fullyImplemented" : true, + "id" : 14, + "linearizedBaseContracts" : + [ + 14 + ], + "name" : "C", + "nodeType" : "ContractDefinition", + "nodes" : + [ + { + "body" : + { + "id" : 5, + "nodeType" : "Block", + "src" : "32:6:1", + "statements" : + [ + { + "id" : 4, + "nodeType" : "PlaceholderStatement", + "src" : "34:1:1" + } + ] + }, + "documentation" : null, + "id" : 6, + "name" : "M", + "nodeType" : "ModifierDefinition", + "parameters" : + { + "id" : 3, + "nodeType" : "ParameterList", + "parameters" : + [ + { + "constant" : false, + "id" : 2, + "name" : "i", + "nodeType" : "VariableDeclaration", + "scope" : 6, + "src" : "24:6:1", + "stateVariable" : false, + "storageLocation" : "default", + "typeDescriptions" : + { + "typeIdentifier" : "t_uint256", + "typeString" : "uint256" + }, + "typeName" : + { + "id" : 1, + "name" : "uint", + "nodeType" : "ElementaryTypeName", + "src" : "24:4:1", + "typeDescriptions" : + { + "typeIdentifier" : "t_uint256", + "typeString" : "uint256" + } + }, + "value" : null, + "visibility" : "internal" + } + ], + "src" : "23:8:1" + }, + "src" : "13:25:1", + "visibility" : "internal" + }, + { + "body" : + { + "id" : 12, + "nodeType" : "Block", + "src" : "64:2:1", + "statements" : [] + }, + "documentation" : null, + "id" : 13, + "implemented" : true, + "kind" : "function", + "modifiers" : + [ + { + "arguments" : + [ + { + "argumentTypes" : null, + "hexValue" : "31", + "id" : 9, + "isConstant" : false, + "isLValue" : false, + "isPure" : true, + "kind" : "number", + "lValueRequested" : false, + "nodeType" : "Literal", + "src" : "54:1:1", + "subdenomination" : null, + "typeDescriptions" : + { + "typeIdentifier" : "t_rational_1_by_1", + "typeString" : "int_const 1" + }, + "value" : "1" + } + ], + "id" : 10, + "modifierName" : + { + "argumentTypes" : null, + "id" : 8, + "name" : "M", + "nodeType" : "Identifier", + "overloadedDeclarations" : [], + "referencedDeclaration" : 6, + "src" : "52:1:1", + "typeDescriptions" : + { + "typeIdentifier" : "t_modifier$_t_uint256_$", + "typeString" : "modifier (uint256)" + } + }, + "nodeType" : "ModifierInvocation", + "src" : "52:4:1" + } + ], + "name" : "F", + "nodeType" : "FunctionDefinition", + "parameters" : + { + "id" : 7, + "nodeType" : "ParameterList", + "parameters" : [], + "src" : "49:2:1" + }, + "returnParameters" : + { + "id" : 11, + "nodeType" : "ParameterList", + "parameters" : [], + "src" : "64:0:1" + }, + "scope" : 14, + "src" : "39:27:1", + "stateMutability" : "nonpayable", + "superFunction" : null, + "visibility" : "public" + } + ], + "scope" : 15, + "src" : "0:68:1" + } + ], + "src" : "0:69:1" +} diff --git a/test/libsolidity/ASTJSON/modifier_invocation.sol b/test/libsolidity/ASTJSON/modifier_invocation.sol new file mode 100644 index 00000000..96474e0f --- /dev/null +++ b/test/libsolidity/ASTJSON/modifier_invocation.sol @@ -0,0 +1 @@ +contract C { modifier M(uint i) { _; } function F() M(1) public {} } diff --git a/test/libsolidity/ASTJSON/modifier_invocation_legacy.json b/test/libsolidity/ASTJSON/modifier_invocation_legacy.json new file mode 100644 index 00000000..5186912c --- /dev/null +++ b/test/libsolidity/ASTJSON/modifier_invocation_legacy.json @@ -0,0 +1,212 @@ +{ + "attributes" : + { + "absolutePath" : "a", + "exportedSymbols" : + { + "C" : + [ + 14 + ] + } + }, + "children" : + [ + { + "attributes" : + { + "baseContracts" : + [ + null + ], + "contractDependencies" : + [ + null + ], + "contractKind" : "contract", + "documentation" : null, + "fullyImplemented" : true, + "linearizedBaseContracts" : + [ + 14 + ], + "name" : "C", + "scope" : 15 + }, + "children" : + [ + { + "attributes" : + { + "documentation" : null, + "name" : "M", + "visibility" : "internal" + }, + "children" : + [ + { + "children" : + [ + { + "attributes" : + { + "constant" : false, + "name" : "i", + "scope" : 6, + "stateVariable" : false, + "storageLocation" : "default", + "type" : "uint256", + "value" : null, + "visibility" : "internal" + }, + "children" : + [ + { + "attributes" : + { + "name" : "uint", + "type" : "uint256" + }, + "id" : 1, + "name" : "ElementaryTypeName", + "src" : "24:4:1" + } + ], + "id" : 2, + "name" : "VariableDeclaration", + "src" : "24:6:1" + } + ], + "id" : 3, + "name" : "ParameterList", + "src" : "23:8:1" + }, + { + "children" : + [ + { + "id" : 4, + "name" : "PlaceholderStatement", + "src" : "34:1:1" + } + ], + "id" : 5, + "name" : "Block", + "src" : "32:6:1" + } + ], + "id" : 6, + "name" : "ModifierDefinition", + "src" : "13:25:1" + }, + { + "attributes" : + { + "documentation" : null, + "implemented" : true, + "isConstructor" : false, + "kind" : "function", + "name" : "F", + "scope" : 14, + "stateMutability" : "nonpayable", + "superFunction" : null, + "visibility" : "public" + }, + "children" : + [ + { + "attributes" : + { + "parameters" : + [ + null + ] + }, + "children" : [], + "id" : 7, + "name" : "ParameterList", + "src" : "49:2:1" + }, + { + "attributes" : + { + "parameters" : + [ + null + ] + }, + "children" : [], + "id" : 11, + "name" : "ParameterList", + "src" : "64:0:1" + }, + { + "children" : + [ + { + "attributes" : + { + "argumentTypes" : null, + "overloadedDeclarations" : + [ + null + ], + "referencedDeclaration" : 6, + "type" : "modifier (uint256)", + "value" : "M" + }, + "id" : 8, + "name" : "Identifier", + "src" : "52:1:1" + }, + { + "attributes" : + { + "argumentTypes" : null, + "hexvalue" : "31", + "isConstant" : false, + "isLValue" : false, + "isPure" : true, + "lValueRequested" : false, + "subdenomination" : null, + "token" : "number", + "type" : "int_const 1", + "value" : "1" + }, + "id" : 9, + "name" : "Literal", + "src" : "54:1:1" + } + ], + "id" : 10, + "name" : "ModifierInvocation", + "src" : "52:4:1" + }, + { + "attributes" : + { + "statements" : + [ + null + ] + }, + "children" : [], + "id" : 12, + "name" : "Block", + "src" : "64:2:1" + } + ], + "id" : 13, + "name" : "FunctionDefinition", + "src" : "39:27:1" + } + ], + "id" : 14, + "name" : "ContractDefinition", + "src" : "0:68:1" + } + ], + "id" : 15, + "name" : "SourceUnit", + "src" : "0:69:1" +} diff --git a/test/libsolidity/ASTJSON/non_utf8.json b/test/libsolidity/ASTJSON/non_utf8.json new file mode 100644 index 00000000..1852bd38 --- /dev/null +++ b/test/libsolidity/ASTJSON/non_utf8.json @@ -0,0 +1,122 @@ +{ + "absolutePath" : "a", + "exportedSymbols" : + { + "C" : + [ + 8 + ] + }, + "id" : 9, + "nodeType" : "SourceUnit", + "nodes" : + [ + { + "baseContracts" : [], + "contractDependencies" : [], + "contractKind" : "contract", + "documentation" : null, + "fullyImplemented" : true, + "id" : 8, + "linearizedBaseContracts" : + [ + 8 + ], + "name" : "C", + "nodeType" : "ContractDefinition", + "nodes" : + [ + { + "body" : + { + "id" : 6, + "nodeType" : "Block", + "src" : "33:20:1", + "statements" : + [ + { + "assignments" : + [ + 3 + ], + "declarations" : + [ + { + "constant" : false, + "id" : 3, + "name" : "x", + "nodeType" : "VariableDeclaration", + "scope" : 6, + "src" : "35:5:1", + "stateVariable" : false, + "storageLocation" : "default", + "typeDescriptions" : + { + "typeIdentifier" : "t_string_memory_ptr", + "typeString" : "string" + }, + "typeName" : null, + "value" : null, + "visibility" : "internal" + } + ], + "id" : 5, + "initialValue" : + { + "argumentTypes" : null, + "hexValue" : "ff", + "id" : 4, + "isConstant" : false, + "isLValue" : false, + "isPure" : true, + "kind" : "string", + "lValueRequested" : false, + "nodeType" : "Literal", + "src" : "43:7:1", + "subdenomination" : null, + "typeDescriptions" : + { + "typeIdentifier" : "t_stringliteral_8b1a944cf13a9a1c08facb2c9e98623ef3254d2ddb48113885c3e8e97fec8db9", + "typeString" : "literal_string (contains invalid UTF-8 sequence at position 0)" + }, + "value" : null + }, + "nodeType" : "VariableDeclarationStatement", + "src" : "35:15:1" + } + ] + }, + "documentation" : null, + "id" : 7, + "implemented" : true, + "kind" : "function", + "modifiers" : [], + "name" : "f", + "nodeType" : "FunctionDefinition", + "parameters" : + { + "id" : 1, + "nodeType" : "ParameterList", + "parameters" : [], + "src" : "23:2:1" + }, + "returnParameters" : + { + "id" : 2, + "nodeType" : "ParameterList", + "parameters" : [], + "src" : "33:0:1" + }, + "scope" : 8, + "src" : "13:40:1", + "stateMutability" : "nonpayable", + "superFunction" : null, + "visibility" : "public" + } + ], + "scope" : 9, + "src" : "0:55:1" + } + ], + "src" : "0:56:1" +} diff --git a/test/libsolidity/ASTJSON/non_utf8.sol b/test/libsolidity/ASTJSON/non_utf8.sol new file mode 100644 index 00000000..b83f3d70 --- /dev/null +++ b/test/libsolidity/ASTJSON/non_utf8.sol @@ -0,0 +1 @@ +contract C { function f() public { var x = hex"ff"; } } diff --git a/test/libsolidity/ASTJSON/non_utf8_legacy.json b/test/libsolidity/ASTJSON/non_utf8_legacy.json new file mode 100644 index 00000000..df105096 --- /dev/null +++ b/test/libsolidity/ASTJSON/non_utf8_legacy.json @@ -0,0 +1,156 @@ +{ + "attributes" : + { + "absolutePath" : "a", + "exportedSymbols" : + { + "C" : + [ + 8 + ] + } + }, + "children" : + [ + { + "attributes" : + { + "baseContracts" : + [ + null + ], + "contractDependencies" : + [ + null + ], + "contractKind" : "contract", + "documentation" : null, + "fullyImplemented" : true, + "linearizedBaseContracts" : + [ + 8 + ], + "name" : "C", + "scope" : 9 + }, + "children" : + [ + { + "attributes" : + { + "documentation" : null, + "implemented" : true, + "isConstructor" : false, + "kind" : "function", + "modifiers" : + [ + null + ], + "name" : "f", + "scope" : 8, + "stateMutability" : "nonpayable", + "superFunction" : null, + "visibility" : "public" + }, + "children" : + [ + { + "attributes" : + { + "parameters" : + [ + null + ] + }, + "children" : [], + "id" : 1, + "name" : "ParameterList", + "src" : "23:2:1" + }, + { + "attributes" : + { + "parameters" : + [ + null + ] + }, + "children" : [], + "id" : 2, + "name" : "ParameterList", + "src" : "33:0:1" + }, + { + "children" : + [ + { + "attributes" : + { + "assignments" : + [ + 3 + ] + }, + "children" : + [ + { + "attributes" : + { + "constant" : false, + "name" : "x", + "scope" : 6, + "stateVariable" : false, + "storageLocation" : "default", + "type" : "string", + "typeName" : null, + "value" : null, + "visibility" : "internal" + }, + "children" : [], + "id" : 3, + "name" : "VariableDeclaration", + "src" : "35:5:1" + }, + { + "attributes" : + { + "argumentTypes" : null, + "hexvalue" : "ff", + "isConstant" : false, + "isLValue" : false, + "isPure" : true, + "lValueRequested" : false, + "subdenomination" : null, + "token" : "string", + "type" : "literal_string (contains invalid UTF-8 sequence at position 0)", + "value" : null + }, + "id" : 4, + "name" : "Literal", + "src" : "43:7:1" + } + ], + "id" : 5, + "name" : "VariableDeclarationStatement", + "src" : "35:15:1" + } + ], + "id" : 6, + "name" : "Block", + "src" : "33:20:1" + } + ], + "id" : 7, + "name" : "FunctionDefinition", + "src" : "13:40:1" + } + ], + "id" : 8, + "name" : "ContractDefinition", + "src" : "0:55:1" + } + ], + "id" : 9, + "name" : "SourceUnit", + "src" : "0:56:1" +} diff --git a/test/libsolidity/ASTJSON/placeholder_statement.json b/test/libsolidity/ASTJSON/placeholder_statement.json new file mode 100644 index 00000000..496e1500 --- /dev/null +++ b/test/libsolidity/ASTJSON/placeholder_statement.json @@ -0,0 +1,64 @@ +{ + "absolutePath" : "a", + "exportedSymbols" : + { + "C" : + [ + 5 + ] + }, + "id" : 6, + "nodeType" : "SourceUnit", + "nodes" : + [ + { + "baseContracts" : [], + "contractDependencies" : [], + "contractKind" : "contract", + "documentation" : null, + "fullyImplemented" : true, + "id" : 5, + "linearizedBaseContracts" : + [ + 5 + ], + "name" : "C", + "nodeType" : "ContractDefinition", + "nodes" : + [ + { + "body" : + { + "id" : 3, + "nodeType" : "Block", + "src" : "24:6:1", + "statements" : + [ + { + "id" : 2, + "nodeType" : "PlaceholderStatement", + "src" : "26:1:1" + } + ] + }, + "documentation" : null, + "id" : 4, + "name" : "M", + "nodeType" : "ModifierDefinition", + "parameters" : + { + "id" : 1, + "nodeType" : "ParameterList", + "parameters" : [], + "src" : "24:0:1" + }, + "src" : "13:17:1", + "visibility" : "internal" + } + ], + "scope" : 6, + "src" : "0:32:1" + } + ], + "src" : "0:33:1" +} diff --git a/test/libsolidity/ASTJSON/placeholder_statement.sol b/test/libsolidity/ASTJSON/placeholder_statement.sol new file mode 100644 index 00000000..cb2c0990 --- /dev/null +++ b/test/libsolidity/ASTJSON/placeholder_statement.sol @@ -0,0 +1 @@ +contract C { modifier M { _; } } diff --git a/test/libsolidity/ASTJSON/placeholder_statement_legacy.json b/test/libsolidity/ASTJSON/placeholder_statement_legacy.json new file mode 100644 index 00000000..a5582976 --- /dev/null +++ b/test/libsolidity/ASTJSON/placeholder_statement_legacy.json @@ -0,0 +1,87 @@ +{ + "attributes" : + { + "absolutePath" : "a", + "exportedSymbols" : + { + "C" : + [ + 5 + ] + } + }, + "children" : + [ + { + "attributes" : + { + "baseContracts" : + [ + null + ], + "contractDependencies" : + [ + null + ], + "contractKind" : "contract", + "documentation" : null, + "fullyImplemented" : true, + "linearizedBaseContracts" : + [ + 5 + ], + "name" : "C", + "scope" : 6 + }, + "children" : + [ + { + "attributes" : + { + "documentation" : null, + "name" : "M", + "visibility" : "internal" + }, + "children" : + [ + { + "attributes" : + { + "parameters" : + [ + null + ] + }, + "children" : [], + "id" : 1, + "name" : "ParameterList", + "src" : "24:0:1" + }, + { + "children" : + [ + { + "id" : 2, + "name" : "PlaceholderStatement", + "src" : "26:1:1" + } + ], + "id" : 3, + "name" : "Block", + "src" : "24:6:1" + } + ], + "id" : 4, + "name" : "ModifierDefinition", + "src" : "13:17:1" + } + ], + "id" : 5, + "name" : "ContractDefinition", + "src" : "0:32:1" + } + ], + "id" : 6, + "name" : "SourceUnit", + "src" : "0:33:1" +} diff --git a/test/libsolidity/ASTJSON/short_type_name.json b/test/libsolidity/ASTJSON/short_type_name.json new file mode 100644 index 00000000..acb46157 --- /dev/null +++ b/test/libsolidity/ASTJSON/short_type_name.json @@ -0,0 +1,126 @@ +{ + "absolutePath" : "a", + "exportedSymbols" : + { + "c" : + [ + 10 + ] + }, + "id" : 11, + "nodeType" : "SourceUnit", + "nodes" : + [ + { + "baseContracts" : [], + "contractDependencies" : [], + "contractKind" : "contract", + "documentation" : null, + "fullyImplemented" : true, + "id" : 10, + "linearizedBaseContracts" : + [ + 10 + ], + "name" : "c", + "nodeType" : "ContractDefinition", + "nodes" : + [ + { + "body" : + { + "id" : 8, + "nodeType" : "Block", + "src" : "33:20:1", + "statements" : + [ + { + "assignments" : + [ + 6 + ], + "declarations" : + [ + { + "constant" : false, + "id" : 6, + "name" : "x", + "nodeType" : "VariableDeclaration", + "scope" : 8, + "src" : "35:15:1", + "stateVariable" : false, + "storageLocation" : "memory", + "typeDescriptions" : + { + "typeIdentifier" : "t_array$_t_uint256_$dyn_memory_ptr", + "typeString" : "uint256[]" + }, + "typeName" : + { + "baseType" : + { + "id" : 4, + "name" : "uint", + "nodeType" : "ElementaryTypeName", + "src" : "35:4:1", + "typeDescriptions" : + { + "typeIdentifier" : "t_uint256", + "typeString" : "uint256" + } + }, + "id" : 5, + "length" : null, + "nodeType" : "ArrayTypeName", + "src" : "35:6:1", + "typeDescriptions" : + { + "typeIdentifier" : "t_array$_t_uint256_$dyn_storage_ptr", + "typeString" : "uint256[]" + } + }, + "value" : null, + "visibility" : "internal" + } + ], + "id" : 7, + "initialValue" : null, + "nodeType" : "VariableDeclarationStatement", + "src" : "35:15:1" + } + ] + }, + "documentation" : null, + "id" : 9, + "implemented" : true, + "kind" : "function", + "modifiers" : [], + "name" : "f", + "nodeType" : "FunctionDefinition", + "parameters" : + { + "id" : 1, + "nodeType" : "ParameterList", + "parameters" : [], + "src" : "23:2:1" + }, + "returnParameters" : + { + "id" : 2, + "nodeType" : "ParameterList", + "parameters" : [], + "src" : "33:0:1" + }, + "scope" : 10, + "src" : "13:40:1", + "stateMutability" : "nonpayable", + "superFunction" : null, + "visibility" : "public" + } + ], + "scope" : 11, + "src" : "0:55:1" + } + ], + "src" : "0:56:1" +} diff --git a/test/libsolidity/ASTJSON/short_type_name.sol b/test/libsolidity/ASTJSON/short_type_name.sol new file mode 100644 index 00000000..533874ae --- /dev/null +++ b/test/libsolidity/ASTJSON/short_type_name.sol @@ -0,0 +1 @@ +contract c { function f() public { uint[] memory x; } } diff --git a/test/libsolidity/ASTJSON/short_type_name_legacy.json b/test/libsolidity/ASTJSON/short_type_name_legacy.json new file mode 100644 index 00000000..1f9b1968 --- /dev/null +++ b/test/libsolidity/ASTJSON/short_type_name_legacy.json @@ -0,0 +1,163 @@ +{ + "attributes" : + { + "absolutePath" : "a", + "exportedSymbols" : + { + "c" : + [ + 10 + ] + } + }, + "children" : + [ + { + "attributes" : + { + "baseContracts" : + [ + null + ], + "contractDependencies" : + [ + null + ], + "contractKind" : "contract", + "documentation" : null, + "fullyImplemented" : true, + "linearizedBaseContracts" : + [ + 10 + ], + "name" : "c", + "scope" : 11 + }, + "children" : + [ + { + "attributes" : + { + "documentation" : null, + "implemented" : true, + "isConstructor" : false, + "kind" : "function", + "modifiers" : + [ + null + ], + "name" : "f", + "scope" : 10, + "stateMutability" : "nonpayable", + "superFunction" : null, + "visibility" : "public" + }, + "children" : + [ + { + "attributes" : + { + "parameters" : + [ + null + ] + }, + "children" : [], + "id" : 1, + "name" : "ParameterList", + "src" : "23:2:1" + }, + { + "attributes" : + { + "parameters" : + [ + null + ] + }, + "children" : [], + "id" : 2, + "name" : "ParameterList", + "src" : "33:0:1" + }, + { + "children" : + [ + { + "attributes" : + { + "assignments" : + [ + 6 + ], + "initialValue" : null + }, + "children" : + [ + { + "attributes" : + { + "constant" : false, + "name" : "x", + "scope" : 8, + "stateVariable" : false, + "storageLocation" : "memory", + "type" : "uint256[]", + "value" : null, + "visibility" : "internal" + }, + "children" : + [ + { + "attributes" : + { + "length" : null, + "type" : "uint256[]" + }, + "children" : + [ + { + "attributes" : + { + "name" : "uint", + "type" : "uint256" + }, + "id" : 4, + "name" : "ElementaryTypeName", + "src" : "35:4:1" + } + ], + "id" : 5, + "name" : "ArrayTypeName", + "src" : "35:6:1" + } + ], + "id" : 6, + "name" : "VariableDeclaration", + "src" : "35:15:1" + } + ], + "id" : 7, + "name" : "VariableDeclarationStatement", + "src" : "35:15:1" + } + ], + "id" : 8, + "name" : "Block", + "src" : "33:20:1" + } + ], + "id" : 9, + "name" : "FunctionDefinition", + "src" : "13:40:1" + } + ], + "id" : 10, + "name" : "ContractDefinition", + "src" : "0:55:1" + } + ], + "id" : 11, + "name" : "SourceUnit", + "src" : "0:56:1" +} diff --git a/test/libsolidity/ASTJSON/short_type_name_ref.json b/test/libsolidity/ASTJSON/short_type_name_ref.json new file mode 100644 index 00000000..b6b7bca5 --- /dev/null +++ b/test/libsolidity/ASTJSON/short_type_name_ref.json @@ -0,0 +1,138 @@ +{ + "absolutePath" : "a", + "exportedSymbols" : + { + "c" : + [ + 11 + ] + }, + "id" : 12, + "nodeType" : "SourceUnit", + "nodes" : + [ + { + "baseContracts" : [], + "contractDependencies" : [], + "contractKind" : "contract", + "documentation" : null, + "fullyImplemented" : true, + "id" : 11, + "linearizedBaseContracts" : + [ + 11 + ], + "name" : "c", + "nodeType" : "ContractDefinition", + "nodes" : + [ + { + "body" : + { + "id" : 9, + "nodeType" : "Block", + "src" : "33:25:1", + "statements" : + [ + { + "assignments" : + [ + 7 + ], + "declarations" : + [ + { + "constant" : false, + "id" : 7, + "name" : "rows", + "nodeType" : "VariableDeclaration", + "scope" : 9, + "src" : "35:20:1", + "stateVariable" : false, + "storageLocation" : "memory", + "typeDescriptions" : + { + "typeIdentifier" : "t_array$_t_array$_t_uint256_$dyn_memory_$dyn_memory_ptr", + "typeString" : "uint256[][]" + }, + "typeName" : + { + "baseType" : + { + "baseType" : + { + "id" : 4, + "name" : "uint", + "nodeType" : "ElementaryTypeName", + "src" : "35:4:1", + "typeDescriptions" : + { + "typeIdentifier" : "t_uint256", + "typeString" : "uint256" + } + }, + "id" : 5, + "length" : null, + "nodeType" : "ArrayTypeName", + "src" : "35:6:1", + "typeDescriptions" : + { + "typeIdentifier" : "t_array$_t_uint256_$dyn_storage_ptr", + "typeString" : "uint256[]" + } + }, + "id" : 6, + "length" : null, + "nodeType" : "ArrayTypeName", + "src" : "35:8:1", + "typeDescriptions" : + { + "typeIdentifier" : "t_array$_t_array$_t_uint256_$dyn_storage_$dyn_storage_ptr", + "typeString" : "uint256[][]" + } + }, + "value" : null, + "visibility" : "internal" + } + ], + "id" : 8, + "initialValue" : null, + "nodeType" : "VariableDeclarationStatement", + "src" : "35:20:1" + } + ] + }, + "documentation" : null, + "id" : 10, + "implemented" : true, + "kind" : "function", + "modifiers" : [], + "name" : "f", + "nodeType" : "FunctionDefinition", + "parameters" : + { + "id" : 1, + "nodeType" : "ParameterList", + "parameters" : [], + "src" : "23:2:1" + }, + "returnParameters" : + { + "id" : 2, + "nodeType" : "ParameterList", + "parameters" : [], + "src" : "33:0:1" + }, + "scope" : 11, + "src" : "13:45:1", + "stateMutability" : "nonpayable", + "superFunction" : null, + "visibility" : "public" + } + ], + "scope" : 12, + "src" : "0:60:1" + } + ], + "src" : "0:61:1" +} diff --git a/test/libsolidity/ASTJSON/short_type_name_ref.sol b/test/libsolidity/ASTJSON/short_type_name_ref.sol new file mode 100644 index 00000000..a808b982 --- /dev/null +++ b/test/libsolidity/ASTJSON/short_type_name_ref.sol @@ -0,0 +1 @@ +contract c { function f() public { uint[][] memory rows; } } diff --git a/test/libsolidity/ASTJSON/short_type_name_ref_legacy.json b/test/libsolidity/ASTJSON/short_type_name_ref_legacy.json new file mode 100644 index 00000000..420b0f60 --- /dev/null +++ b/test/libsolidity/ASTJSON/short_type_name_ref_legacy.json @@ -0,0 +1,176 @@ +{ + "attributes" : + { + "absolutePath" : "a", + "exportedSymbols" : + { + "c" : + [ + 11 + ] + } + }, + "children" : + [ + { + "attributes" : + { + "baseContracts" : + [ + null + ], + "contractDependencies" : + [ + null + ], + "contractKind" : "contract", + "documentation" : null, + "fullyImplemented" : true, + "linearizedBaseContracts" : + [ + 11 + ], + "name" : "c", + "scope" : 12 + }, + "children" : + [ + { + "attributes" : + { + "documentation" : null, + "implemented" : true, + "isConstructor" : false, + "kind" : "function", + "modifiers" : + [ + null + ], + "name" : "f", + "scope" : 11, + "stateMutability" : "nonpayable", + "superFunction" : null, + "visibility" : "public" + }, + "children" : + [ + { + "attributes" : + { + "parameters" : + [ + null + ] + }, + "children" : [], + "id" : 1, + "name" : "ParameterList", + "src" : "23:2:1" + }, + { + "attributes" : + { + "parameters" : + [ + null + ] + }, + "children" : [], + "id" : 2, + "name" : "ParameterList", + "src" : "33:0:1" + }, + { + "children" : + [ + { + "attributes" : + { + "assignments" : + [ + 7 + ], + "initialValue" : null + }, + "children" : + [ + { + "attributes" : + { + "constant" : false, + "name" : "rows", + "scope" : 9, + "stateVariable" : false, + "storageLocation" : "memory", + "type" : "uint256[][]", + "value" : null, + "visibility" : "internal" + }, + "children" : + [ + { + "attributes" : + { + "length" : null, + "type" : "uint256[][]" + }, + "children" : + [ + { + "attributes" : + { + "length" : null, + "type" : "uint256[]" + }, + "children" : + [ + { + "attributes" : + { + "name" : "uint", + "type" : "uint256" + }, + "id" : 4, + "name" : "ElementaryTypeName", + "src" : "35:4:1" + } + ], + "id" : 5, + "name" : "ArrayTypeName", + "src" : "35:6:1" + } + ], + "id" : 6, + "name" : "ArrayTypeName", + "src" : "35:8:1" + } + ], + "id" : 7, + "name" : "VariableDeclaration", + "src" : "35:20:1" + } + ], + "id" : 8, + "name" : "VariableDeclarationStatement", + "src" : "35:20:1" + } + ], + "id" : 9, + "name" : "Block", + "src" : "33:25:1" + } + ], + "id" : 10, + "name" : "FunctionDefinition", + "src" : "13:45:1" + } + ], + "id" : 11, + "name" : "ContractDefinition", + "src" : "0:60:1" + } + ], + "id" : 12, + "name" : "SourceUnit", + "src" : "0:61:1" +} diff --git a/test/libsolidity/ASTJSON/smoke.json b/test/libsolidity/ASTJSON/smoke.json new file mode 100644 index 00000000..f5369bfc --- /dev/null +++ b/test/libsolidity/ASTJSON/smoke.json @@ -0,0 +1,33 @@ +{ + "absolutePath" : "a", + "exportedSymbols" : + { + "C" : + [ + 1 + ] + }, + "id" : 2, + "nodeType" : "SourceUnit", + "nodes" : + [ + { + "baseContracts" : [], + "contractDependencies" : [], + "contractKind" : "contract", + "documentation" : null, + "fullyImplemented" : true, + "id" : 1, + "linearizedBaseContracts" : + [ + 1 + ], + "name" : "C", + "nodeType" : "ContractDefinition", + "nodes" : [], + "scope" : 2, + "src" : "0:13:1" + } + ], + "src" : "0:14:1" +} diff --git a/test/libsolidity/ASTJSON/smoke.sol b/test/libsolidity/ASTJSON/smoke.sol new file mode 100644 index 00000000..2dde0d20 --- /dev/null +++ b/test/libsolidity/ASTJSON/smoke.sol @@ -0,0 +1 @@ +contract C {} diff --git a/test/libsolidity/ASTJSON/smoke_legacy.json b/test/libsolidity/ASTJSON/smoke_legacy.json new file mode 100644 index 00000000..e01a3c9b --- /dev/null +++ b/test/libsolidity/ASTJSON/smoke_legacy.json @@ -0,0 +1,48 @@ +{ + "attributes" : + { + "absolutePath" : "a", + "exportedSymbols" : + { + "C" : + [ + 1 + ] + } + }, + "children" : + [ + { + "attributes" : + { + "baseContracts" : + [ + null + ], + "contractDependencies" : + [ + null + ], + "contractKind" : "contract", + "documentation" : null, + "fullyImplemented" : true, + "linearizedBaseContracts" : + [ + 1 + ], + "name" : "C", + "nodes" : + [ + null + ], + "scope" : 2 + }, + "id" : 1, + "name" : "ContractDefinition", + "src" : "0:13:1" + } + ], + "id" : 2, + "name" : "SourceUnit", + "src" : "0:14:1" +} diff --git a/test/libsolidity/ASTJSON/source_location.json b/test/libsolidity/ASTJSON/source_location.json new file mode 100644 index 00000000..f0ed216d --- /dev/null +++ b/test/libsolidity/ASTJSON/source_location.json @@ -0,0 +1,160 @@ +{ + "absolutePath" : "a", + "exportedSymbols" : + { + "C" : + [ + 11 + ] + }, + "id" : 12, + "nodeType" : "SourceUnit", + "nodes" : + [ + { + "baseContracts" : [], + "contractDependencies" : [], + "contractKind" : "contract", + "documentation" : null, + "fullyImplemented" : true, + "id" : 11, + "linearizedBaseContracts" : + [ + 11 + ], + "name" : "C", + "nodeType" : "ContractDefinition", + "nodes" : + [ + { + "body" : + { + "id" : 9, + "nodeType" : "Block", + "src" : "26:19:1", + "statements" : + [ + { + "assignments" : + [ + 3 + ], + "declarations" : + [ + { + "constant" : false, + "id" : 3, + "name" : "x", + "nodeType" : "VariableDeclaration", + "scope" : 9, + "src" : "28:5:1", + "stateVariable" : false, + "storageLocation" : "default", + "typeDescriptions" : + { + "typeIdentifier" : "t_uint8", + "typeString" : "uint8" + }, + "typeName" : null, + "value" : null, + "visibility" : "internal" + } + ], + "id" : 5, + "initialValue" : + { + "argumentTypes" : null, + "hexValue" : "32", + "id" : 4, + "isConstant" : false, + "isLValue" : false, + "isPure" : true, + "kind" : "number", + "lValueRequested" : false, + "nodeType" : "Literal", + "src" : "36:1:1", + "subdenomination" : null, + "typeDescriptions" : + { + "typeIdentifier" : "t_rational_2_by_1", + "typeString" : "int_const 2" + }, + "value" : "2" + }, + "nodeType" : "VariableDeclarationStatement", + "src" : "28:9:1" + }, + { + "expression" : + { + "argumentTypes" : null, + "id" : 7, + "isConstant" : false, + "isLValue" : false, + "isPure" : false, + "lValueRequested" : false, + "nodeType" : "UnaryOperation", + "operator" : "++", + "prefix" : false, + "src" : "39:3:1", + "subExpression" : + { + "argumentTypes" : null, + "id" : 6, + "name" : "x", + "nodeType" : "Identifier", + "overloadedDeclarations" : [], + "referencedDeclaration" : 3, + "src" : "39:1:1", + "typeDescriptions" : + { + "typeIdentifier" : "t_uint8", + "typeString" : "uint8" + } + }, + "typeDescriptions" : + { + "typeIdentifier" : "t_uint8", + "typeString" : "uint8" + } + }, + "id" : 8, + "nodeType" : "ExpressionStatement", + "src" : "39:3:1" + } + ] + }, + "documentation" : null, + "id" : 10, + "implemented" : true, + "kind" : "function", + "modifiers" : [], + "name" : "f", + "nodeType" : "FunctionDefinition", + "parameters" : + { + "id" : 1, + "nodeType" : "ParameterList", + "parameters" : [], + "src" : "23:2:1" + }, + "returnParameters" : + { + "id" : 2, + "nodeType" : "ParameterList", + "parameters" : [], + "src" : "26:0:1" + }, + "scope" : 11, + "src" : "13:32:1", + "stateMutability" : "nonpayable", + "superFunction" : null, + "visibility" : "public" + } + ], + "scope" : 12, + "src" : "0:47:1" + } + ], + "src" : "0:48:1" +} diff --git a/test/libsolidity/ASTJSON/source_location.sol b/test/libsolidity/ASTJSON/source_location.sol new file mode 100644 index 00000000..1fcec2e6 --- /dev/null +++ b/test/libsolidity/ASTJSON/source_location.sol @@ -0,0 +1 @@ +contract C { function f() { var x = 2; x++; } } diff --git a/test/libsolidity/ASTJSON/source_location_legacy.json b/test/libsolidity/ASTJSON/source_location_legacy.json new file mode 100644 index 00000000..a65979d6 --- /dev/null +++ b/test/libsolidity/ASTJSON/source_location_legacy.json @@ -0,0 +1,199 @@ +{ + "attributes" : + { + "absolutePath" : "a", + "exportedSymbols" : + { + "C" : + [ + 11 + ] + } + }, + "children" : + [ + { + "attributes" : + { + "baseContracts" : + [ + null + ], + "contractDependencies" : + [ + null + ], + "contractKind" : "contract", + "documentation" : null, + "fullyImplemented" : true, + "linearizedBaseContracts" : + [ + 11 + ], + "name" : "C", + "scope" : 12 + }, + "children" : + [ + { + "attributes" : + { + "documentation" : null, + "implemented" : true, + "isConstructor" : false, + "kind" : "function", + "modifiers" : + [ + null + ], + "name" : "f", + "scope" : 11, + "stateMutability" : "nonpayable", + "superFunction" : null, + "visibility" : "public" + }, + "children" : + [ + { + "attributes" : + { + "parameters" : + [ + null + ] + }, + "children" : [], + "id" : 1, + "name" : "ParameterList", + "src" : "23:2:1" + }, + { + "attributes" : + { + "parameters" : + [ + null + ] + }, + "children" : [], + "id" : 2, + "name" : "ParameterList", + "src" : "26:0:1" + }, + { + "children" : + [ + { + "attributes" : + { + "assignments" : + [ + 3 + ] + }, + "children" : + [ + { + "attributes" : + { + "constant" : false, + "name" : "x", + "scope" : 9, + "stateVariable" : false, + "storageLocation" : "default", + "type" : "uint8", + "typeName" : null, + "value" : null, + "visibility" : "internal" + }, + "children" : [], + "id" : 3, + "name" : "VariableDeclaration", + "src" : "28:5:1" + }, + { + "attributes" : + { + "argumentTypes" : null, + "hexvalue" : "32", + "isConstant" : false, + "isLValue" : false, + "isPure" : true, + "lValueRequested" : false, + "subdenomination" : null, + "token" : "number", + "type" : "int_const 2", + "value" : "2" + }, + "id" : 4, + "name" : "Literal", + "src" : "36:1:1" + } + ], + "id" : 5, + "name" : "VariableDeclarationStatement", + "src" : "28:9:1" + }, + { + "children" : + [ + { + "attributes" : + { + "argumentTypes" : null, + "isConstant" : false, + "isLValue" : false, + "isPure" : false, + "lValueRequested" : false, + "operator" : "++", + "prefix" : false, + "type" : "uint8" + }, + "children" : + [ + { + "attributes" : + { + "argumentTypes" : null, + "overloadedDeclarations" : + [ + null + ], + "referencedDeclaration" : 3, + "type" : "uint8", + "value" : "x" + }, + "id" : 6, + "name" : "Identifier", + "src" : "39:1:1" + } + ], + "id" : 7, + "name" : "UnaryOperation", + "src" : "39:3:1" + } + ], + "id" : 8, + "name" : "ExpressionStatement", + "src" : "39:3:1" + } + ], + "id" : 9, + "name" : "Block", + "src" : "26:19:1" + } + ], + "id" : 10, + "name" : "FunctionDefinition", + "src" : "13:32:1" + } + ], + "id" : 11, + "name" : "ContractDefinition", + "src" : "0:47:1" + } + ], + "id" : 12, + "name" : "SourceUnit", + "src" : "0:48:1" +} diff --git a/test/libsolidity/ASTJSON/using_for_directive.json b/test/libsolidity/ASTJSON/using_for_directive.json new file mode 100644 index 00000000..33caabb4 --- /dev/null +++ b/test/libsolidity/ASTJSON/using_for_directive.json @@ -0,0 +1,87 @@ +{ + "absolutePath" : "a", + "exportedSymbols" : + { + "C" : + [ + 5 + ], + "L" : + [ + 1 + ] + }, + "id" : 6, + "nodeType" : "SourceUnit", + "nodes" : + [ + { + "baseContracts" : [], + "contractDependencies" : [], + "contractKind" : "library", + "documentation" : null, + "fullyImplemented" : true, + "id" : 1, + "linearizedBaseContracts" : + [ + 1 + ], + "name" : "L", + "nodeType" : "ContractDefinition", + "nodes" : [], + "scope" : 6, + "src" : "0:12:1" + }, + { + "baseContracts" : [], + "contractDependencies" : [], + "contractKind" : "contract", + "documentation" : null, + "fullyImplemented" : true, + "id" : 5, + "linearizedBaseContracts" : + [ + 5 + ], + "name" : "C", + "nodeType" : "ContractDefinition", + "nodes" : + [ + { + "id" : 4, + "libraryName" : + { + "contractScope" : null, + "id" : 2, + "name" : "L", + "nodeType" : "UserDefinedTypeName", + "referencedDeclaration" : 1, + "src" : "32:1:1", + "typeDescriptions" : + { + "typeIdentifier" : "t_contract$_L_$1", + "typeString" : "library L" + } + }, + "nodeType" : "UsingForDirective", + "src" : "26:17:1", + "typeName" : + { + "id" : 3, + "name" : "uint", + "nodeType" : "ElementaryTypeName", + "src" : "38:4:1", + "typeDescriptions" : + { + "typeIdentifier" : "t_uint256", + "typeString" : "uint256" + } + } + } + ], + "scope" : 6, + "src" : "13:32:1" + } + ], + "src" : "0:46:1" +} diff --git a/test/libsolidity/ASTJSON/using_for_directive.sol b/test/libsolidity/ASTJSON/using_for_directive.sol new file mode 100644 index 00000000..a882ad88 --- /dev/null +++ b/test/libsolidity/ASTJSON/using_for_directive.sol @@ -0,0 +1 @@ +library L {} contract C { using L for uint; } diff --git a/test/libsolidity/ASTJSON/using_for_directive_legacy.json b/test/libsolidity/ASTJSON/using_for_directive_legacy.json new file mode 100644 index 00000000..0827ef90 --- /dev/null +++ b/test/libsolidity/ASTJSON/using_for_directive_legacy.json @@ -0,0 +1,110 @@ +{ + "attributes" : + { + "absolutePath" : "a", + "exportedSymbols" : + { + "C" : + [ + 5 + ], + "L" : + [ + 1 + ] + } + }, + "children" : + [ + { + "attributes" : + { + "baseContracts" : + [ + null + ], + "contractDependencies" : + [ + null + ], + "contractKind" : "library", + "documentation" : null, + "fullyImplemented" : true, + "linearizedBaseContracts" : + [ + 1 + ], + "name" : "L", + "nodes" : + [ + null + ], + "scope" : 6 + }, + "id" : 1, + "name" : "ContractDefinition", + "src" : "0:12:1" + }, + { + "attributes" : + { + "baseContracts" : + [ + null + ], + "contractDependencies" : + [ + null + ], + "contractKind" : "contract", + "documentation" : null, + "fullyImplemented" : true, + "linearizedBaseContracts" : + [ + 5 + ], + "name" : "C", + "scope" : 6 + }, + "children" : + [ + { + "children" : + [ + { + "attributes" : + { + "contractScope" : null, + "name" : "L", + "referencedDeclaration" : 1, + "type" : "library L" + }, + "id" : 2, + "name" : "UserDefinedTypeName", + "src" : "32:1:1" + }, + { + "attributes" : + { + "name" : "uint", + "type" : "uint256" + }, + "id" : 3, + "name" : "ElementaryTypeName", + "src" : "38:4:1" + } + ], + "id" : 4, + "name" : "UsingForDirective", + "src" : "26:17:1" + } + ], + "id" : 5, + "name" : "ContractDefinition", + "src" : "13:32:1" + } + ], + "id" : 6, + "name" : "SourceUnit", + "src" : "0:46:1" +} diff --git a/test/libsolidity/ASTJSONTest.cpp b/test/libsolidity/ASTJSONTest.cpp new file mode 100644 index 00000000..be482d99 --- /dev/null +++ b/test/libsolidity/ASTJSONTest.cpp @@ -0,0 +1,200 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <test/libsolidity/ASTJSONTest.h> +#include <test/Options.h> +#include <libsolidity/ast/ASTJsonConverter.h> +#include <libsolidity/interface/CompilerStack.h> +#include <boost/algorithm/string.hpp> +#include <boost/algorithm/string/predicate.hpp> +#include <boost/throw_exception.hpp> +#include <fstream> +#include <memory> +#include <stdexcept> + +using namespace dev; +using namespace solidity; +using namespace dev::solidity::test; +using namespace dev::solidity::test::formatting; +using namespace std; +namespace fs = boost::filesystem; +using namespace boost::unit_test; + +ASTJSONTest::ASTJSONTest(string const& _filename) +{ + if (!boost::algorithm::ends_with(_filename, ".sol")) + BOOST_THROW_EXCEPTION(runtime_error("Invalid test contract file name: \"" + _filename + "\".")); + + m_astFilename = _filename.substr(0, _filename.size() - 4) + ".json"; + m_legacyAstFilename = _filename.substr(0, _filename.size() - 4) + "_legacy.json"; + + ifstream file(_filename); + if (!file) + BOOST_THROW_EXCEPTION(runtime_error("Cannot open test contract: \"" + _filename + "\".")); + file.exceptions(ios::badbit); + + string sourceName; + string source; + string line; + string const sourceDelimiter("// ---- SOURCE: "); + string const delimiter("// ----"); + while (getline(file, line)) + { + if (boost::algorithm::starts_with(line, sourceDelimiter)) + { + if (!sourceName.empty()) + m_sources.emplace_back(sourceName, source); + + sourceName = line.substr(sourceDelimiter.size(), string::npos); + source = string(); + } + else if (!line.empty() && !boost::algorithm::starts_with(line, delimiter)) + source += line + "\n"; + } + + m_sources.emplace_back(sourceName.empty() ? "a" : sourceName, source); + + file.close(); + file.open(m_astFilename); + if (file) + { + string line; + while (getline(file, line)) + m_expectation += line + "\n"; + } + + file.close(); + file.open(m_legacyAstFilename); + if (file) + { + string line; + while (getline(file, line)) + m_expectationLegacy += line + "\n"; + } +} + +bool ASTJSONTest::run(ostream& _stream, string const& _linePrefix, bool const _formatted) +{ + CompilerStack c; + + map<string, unsigned> sourceIndices; + for (size_t i = 0; i < m_sources.size(); i++) + { + c.addSource(m_sources[i].first, m_sources[i].second); + sourceIndices[m_sources[i].first] = i + 1; + } + + c.setEVMVersion(dev::test::Options::get().evmVersion()); + c.parseAndAnalyze(); + + for (size_t i = 0; i < m_sources.size(); i++) + { + ostringstream result; + ASTJsonConverter(false, sourceIndices).print(result, c.ast(m_sources[i].first)); + m_result += result.str(); + if (i != m_sources.size() - 1) + m_result += ","; + m_result += "\n"; + } + + bool resultsMatch = true; + + if (m_expectation != m_result) + { + string nextIndentLevel = _linePrefix + " "; + FormattedScope(_stream, _formatted, {BOLD, CYAN}) << _linePrefix << "Expected result:" << endl; + { + istringstream stream(m_expectation); + string line; + while (getline(stream, line)) + _stream << nextIndentLevel << line << endl; + } + _stream << endl; + FormattedScope(_stream, _formatted, {BOLD, CYAN}) << _linePrefix << "Obtained result:" << endl; + { + istringstream stream(m_result); + string line; + while (getline(stream, line)) + _stream << nextIndentLevel << line << endl; + } + _stream << endl; + resultsMatch = false; + } + + for (size_t i = 0; i < m_sources.size(); i++) + { + ostringstream result; + ASTJsonConverter(true, sourceIndices).print(result, c.ast(m_sources[i].first)); + m_resultLegacy = result.str(); + if (i != m_sources.size() - 1) + m_resultLegacy += ","; + m_resultLegacy += "\n"; + } + + if (m_expectationLegacy != m_resultLegacy) + { + string nextIndentLevel = _linePrefix + " "; + FormattedScope(_stream, _formatted, {BOLD, CYAN}) << _linePrefix << "Expected result (legacy):" << endl; + { + istringstream stream(m_expectationLegacy); + string line; + while (getline(stream, line)) + _stream << nextIndentLevel << line << endl; + } + _stream << endl; + FormattedScope(_stream, _formatted, {BOLD, CYAN}) << _linePrefix << "Obtained result (legacy):" << endl; + { + istringstream stream(m_resultLegacy); + string line; + while (getline(stream, line)) + _stream << nextIndentLevel << line << endl; + } + _stream << endl; + resultsMatch = false; + } + + return resultsMatch; +} + +void ASTJSONTest::printSource(ostream& _stream, string const& _linePrefix, bool const) const +{ + for (auto const& source: m_sources) + { + if (m_sources.size() > 1 || source.first != "a") + _stream << _linePrefix << "// ---- SOURCE: " << source.first << endl << endl; + stringstream stream(source.second); + string line; + while (getline(stream, line)) + _stream << _linePrefix << line << endl; + _stream << endl; + } +} + +void ASTJSONTest::printUpdatedExpectations(std::ostream&, std::string const&) const +{ + ofstream file(m_astFilename.c_str()); + if (!file) BOOST_THROW_EXCEPTION(runtime_error("Cannot write AST expectation to \"" + m_astFilename + "\".")); + file.exceptions(ios::badbit); + file << m_result; + file.flush(); + file.close(); + file.open(m_legacyAstFilename.c_str()); + if (!file) BOOST_THROW_EXCEPTION(runtime_error("Cannot write legacy AST expectation to \"" + m_legacyAstFilename + "\".")); + file << m_resultLegacy; + file.flush(); + file.close(); +} diff --git a/test/libsolidity/ASTJSONTest.h b/test/libsolidity/ASTJSONTest.h new file mode 100644 index 00000000..6f24bb60 --- /dev/null +++ b/test/libsolidity/ASTJSONTest.h @@ -0,0 +1,58 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see <http://www.gnu.org/licenses/>. +*/ + +#pragma once + +#include <test/libsolidity/FormattedScope.h> +#include <test/libsolidity/TestCase.h> + +#include <iosfwd> +#include <string> +#include <vector> +#include <utility> + +namespace dev +{ +namespace solidity +{ +namespace test +{ + +class ASTJSONTest: public TestCase +{ +public: + static std::unique_ptr<TestCase> create(std::string const& _filename) + { return std::unique_ptr<TestCase>(new ASTJSONTest(_filename)); } + ASTJSONTest(std::string const& _filename); + + virtual bool run(std::ostream& _stream, std::string const& _linePrefix = "", bool const _formatted = false) override; + + virtual void printSource(std::ostream& _stream, std::string const& _linePrefix = "", bool const _formatted = false) const override; + virtual void printUpdatedExpectations(std::ostream& _stream, std::string const& _linePrefix) const override; +private: + std::vector<std::pair<std::string, std::string>> m_sources; + std::string m_expectation; + std::string m_expectationLegacy; + std::string m_astFilename; + std::string m_legacyAstFilename; + std::string m_result; + std::string m_resultLegacy; +}; + +} +} +} diff --git a/test/libsolidity/ASTLegacyJSON.cpp b/test/libsolidity/ASTLegacyJSON.cpp deleted file mode 100644 index 13148682..00000000 --- a/test/libsolidity/ASTLegacyJSON.cpp +++ /dev/null @@ -1,324 +0,0 @@ -/* - This file is part of solidity. - - solidity is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - solidity is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with solidity. If not, see <http://www.gnu.org/licenses/>. -*/ -/** - * @author Christian <c@ethdev.com> - * @date 2016 - * Tests for the json ast output. - */ - -#include <test/Options.h> - -#include <libsolidity/interface/Exceptions.h> -#include <libsolidity/interface/CompilerStack.h> -#include <libsolidity/ast/ASTJsonConverter.h> - -#include <boost/test/unit_test.hpp> - -#include <string> - -using namespace std; - -namespace dev -{ -namespace solidity -{ -namespace test -{ - -BOOST_AUTO_TEST_SUITE(SolidityASTLegacyJSON) - -BOOST_AUTO_TEST_CASE(smoke_test) -{ - CompilerStack c; - c.addSource("a", "contract C {}"); - c.setEVMVersion(dev::test::Options::get().evmVersion()); - c.parseAndAnalyze(); - map<string, unsigned> sourceIndices; - sourceIndices["a"] = 1; - Json::Value astJson = ASTJsonConverter(true, sourceIndices).toJson(c.ast("a")); - BOOST_CHECK_EQUAL(astJson["name"], "SourceUnit"); -} - -BOOST_AUTO_TEST_CASE(source_location) -{ - CompilerStack c; - c.addSource("a", "contract C { function f() { var x = 2; x++; } }"); - c.setEVMVersion(dev::test::Options::get().evmVersion()); - c.parseAndAnalyze(); - map<string, unsigned> sourceIndices; - sourceIndices["a"] = 1; - Json::Value astJson = ASTJsonConverter(true, sourceIndices).toJson(c.ast("a")); - BOOST_CHECK_EQUAL(astJson["name"], "SourceUnit"); - BOOST_CHECK_EQUAL(astJson["children"][0]["name"], "ContractDefinition"); - BOOST_CHECK_EQUAL(astJson["children"][0]["children"][0]["name"], "FunctionDefinition"); - BOOST_CHECK_EQUAL(astJson["children"][0]["children"][0]["src"], "13:32:1"); - -} - -BOOST_AUTO_TEST_CASE(inheritance_specifier) -{ - CompilerStack c; - c.addSource("a", "contract C1 {} contract C2 is C1 {}"); - c.setEVMVersion(dev::test::Options::get().evmVersion()); - c.parseAndAnalyze(); - map<string, unsigned> sourceIndices; - sourceIndices["a"] = 1; - Json::Value astJson = ASTJsonConverter(true, sourceIndices).toJson(c.ast("a")); - BOOST_CHECK_EQUAL(astJson["children"][1]["attributes"]["name"], "C2"); - BOOST_CHECK_EQUAL(astJson["children"][1]["children"][0]["name"], "InheritanceSpecifier"); - BOOST_CHECK_EQUAL(astJson["children"][1]["children"][0]["src"], "30:2:1"); - BOOST_CHECK_EQUAL(astJson["children"][1]["children"][0]["children"][0]["name"], "UserDefinedTypeName"); - BOOST_CHECK_EQUAL(astJson["children"][1]["children"][0]["children"][0]["attributes"]["name"], "C1"); -} - -BOOST_AUTO_TEST_CASE(using_for_directive) -{ - CompilerStack c; - c.addSource("a", "library L {} contract C { using L for uint; }"); - c.setEVMVersion(dev::test::Options::get().evmVersion()); - c.parseAndAnalyze(); - map<string, unsigned> sourceIndices; - sourceIndices["a"] = 1; - Json::Value astJson = ASTJsonConverter(true, sourceIndices).toJson(c.ast("a")); - Json::Value usingFor = astJson["children"][1]["children"][0]; - BOOST_CHECK_EQUAL(usingFor["name"], "UsingForDirective"); - BOOST_CHECK_EQUAL(usingFor["src"], "26:17:1"); - BOOST_CHECK_EQUAL(usingFor["children"][0]["name"], "UserDefinedTypeName"); - BOOST_CHECK_EQUAL(usingFor["children"][0]["attributes"]["name"], "L"); - BOOST_CHECK_EQUAL(usingFor["children"][1]["name"], "ElementaryTypeName"); - BOOST_CHECK_EQUAL(usingFor["children"][1]["attributes"]["name"], "uint"); -} - -BOOST_AUTO_TEST_CASE(enum_value) -{ - CompilerStack c; - c.addSource("a", "contract C { enum E { A, B } }"); - c.setEVMVersion(dev::test::Options::get().evmVersion()); - c.parseAndAnalyze(); - map<string, unsigned> sourceIndices; - sourceIndices["a"] = 1; - Json::Value astJson = ASTJsonConverter(true, sourceIndices).toJson(c.ast("a")); - Json::Value enumDefinition = astJson["children"][0]["children"][0]; - BOOST_CHECK_EQUAL(enumDefinition["children"][0]["name"], "EnumValue"); - BOOST_CHECK_EQUAL(enumDefinition["children"][0]["attributes"]["name"], "A"); - BOOST_CHECK_EQUAL(enumDefinition["children"][0]["src"], "22:1:1"); - BOOST_CHECK_EQUAL(enumDefinition["children"][1]["name"], "EnumValue"); - BOOST_CHECK_EQUAL(enumDefinition["children"][1]["attributes"]["name"], "B"); - BOOST_CHECK_EQUAL(enumDefinition["children"][1]["src"], "25:1:1"); -} - -BOOST_AUTO_TEST_CASE(modifier_definition) -{ - CompilerStack c; - c.addSource("a", "contract C { modifier M(uint i) { _; } function F() M(1) {} }"); - c.setEVMVersion(dev::test::Options::get().evmVersion()); - c.parseAndAnalyze(); - map<string, unsigned> sourceIndices; - sourceIndices["a"] = 1; - Json::Value astJson = ASTJsonConverter(true, sourceIndices).toJson(c.ast("a")); - Json::Value modifier = astJson["children"][0]["children"][0]; - BOOST_CHECK_EQUAL(modifier["name"], "ModifierDefinition"); - BOOST_CHECK_EQUAL(modifier["attributes"]["name"], "M"); - BOOST_CHECK_EQUAL(modifier["src"], "13:25:1"); -} - -BOOST_AUTO_TEST_CASE(modifier_invocation) -{ - CompilerStack c; - c.addSource("a", "contract C { modifier M(uint i) { _; } function F() M(1) {} }"); - c.setEVMVersion(dev::test::Options::get().evmVersion()); - c.parseAndAnalyze(); - map<string, unsigned> sourceIndices; - sourceIndices["a"] = 1; - Json::Value astJson = ASTJsonConverter(true, sourceIndices).toJson(c.ast("a")); - Json::Value modifier = astJson["children"][0]["children"][1]["children"][2]; - BOOST_CHECK_EQUAL(modifier["name"], "ModifierInvocation"); - BOOST_CHECK_EQUAL(modifier["src"], "52:4:1"); - BOOST_CHECK_EQUAL(modifier["children"][0]["attributes"]["type"], "modifier (uint256)"); - BOOST_CHECK_EQUAL(modifier["children"][0]["attributes"]["value"], "M"); - BOOST_CHECK_EQUAL(modifier["children"][1]["attributes"]["value"], "1"); -} - -BOOST_AUTO_TEST_CASE(event_definition) -{ - CompilerStack c; - c.addSource("a", "contract C { event E(); }"); - c.setEVMVersion(dev::test::Options::get().evmVersion()); - c.parseAndAnalyze(); - map<string, unsigned> sourceIndices; - sourceIndices["a"] = 1; - Json::Value astJson = ASTJsonConverter(true, sourceIndices).toJson(c.ast("a")); - Json::Value event = astJson["children"][0]["children"][0]; - BOOST_CHECK_EQUAL(event["name"], "EventDefinition"); - BOOST_CHECK_EQUAL(event["attributes"]["name"], "E"); - BOOST_CHECK_EQUAL(event["src"], "13:10:1"); -} - -BOOST_AUTO_TEST_CASE(array_type_name) -{ - CompilerStack c; - c.addSource("a", "contract C { uint[] i; }"); - c.setEVMVersion(dev::test::Options::get().evmVersion()); - c.parseAndAnalyze(); - map<string, unsigned> sourceIndices; - sourceIndices["a"] = 1; - Json::Value astJson = ASTJsonConverter(true, sourceIndices).toJson(c.ast("a")); - Json::Value arrayDecl = astJson["children"][0]["children"][0]["attributes"]; - BOOST_CHECK_EQUAL(arrayDecl["storageLocation"], "default"); - BOOST_CHECK_EQUAL(arrayDecl["type"], "uint256[]"); - Json::Value array = astJson["children"][0]["children"][0]["children"][0]; - BOOST_CHECK_EQUAL(array["name"], "ArrayTypeName"); - BOOST_CHECK_EQUAL(array["src"], "13:6:1"); -} - -BOOST_AUTO_TEST_CASE(short_type_name) -{ - CompilerStack c; - c.addSource("a", "contract c { function f() { uint[] memory x; } }"); - c.setEVMVersion(dev::test::Options::get().evmVersion()); - c.parseAndAnalyze(); - map<string, unsigned> sourceIndices; - sourceIndices["a"] = 1; - Json::Value astJson = ASTJsonConverter(true, sourceIndices).toJson(c.ast("a")); - Json::Value arrayDecl = astJson["children"][0]["children"][0]["children"][2]["children"][0]["children"][0]; - BOOST_CHECK_EQUAL(arrayDecl["attributes"]["storageLocation"], "memory"); - BOOST_CHECK_EQUAL(arrayDecl["attributes"]["type"], "uint256[]"); -} - -BOOST_AUTO_TEST_CASE(short_type_name_ref) -{ - CompilerStack c; - c.addSource("a", "contract c { function f() { uint[][] memory rows; } }"); - c.setEVMVersion(dev::test::Options::get().evmVersion()); - c.parseAndAnalyze(); - map<string, unsigned> sourceIndices; - sourceIndices["a"] = 1; - Json::Value astJson = ASTJsonConverter(true, sourceIndices).toJson(c.ast("a")); - Json::Value arrayDecl = astJson["children"][0]["children"][0]["children"][2]["children"][0]["children"][0]; - BOOST_CHECK_EQUAL(arrayDecl["attributes"]["storageLocation"], "memory"); - BOOST_CHECK_EQUAL(arrayDecl["attributes"]["type"], "uint256[][]"); -} - -BOOST_AUTO_TEST_CASE(placeholder_statement) -{ - CompilerStack c; - c.addSource("a", "contract C { modifier M { _; } }"); - c.setEVMVersion(dev::test::Options::get().evmVersion()); - c.parseAndAnalyze(); - map<string, unsigned> sourceIndices; - sourceIndices["a"] = 1; - Json::Value astJson = ASTJsonConverter(true, sourceIndices).toJson(c.ast("a")); - Json::Value placeholder = astJson["children"][0]["children"][0]["children"][1]["children"][0]; - BOOST_CHECK_EQUAL(placeholder["name"], "PlaceholderStatement"); - BOOST_CHECK_EQUAL(placeholder["src"], "26:1:1"); -} - -BOOST_AUTO_TEST_CASE(non_utf8) -{ - CompilerStack c; - c.addSource("a", "contract C { function f() { var x = hex\"ff\"; } }"); - c.setEVMVersion(dev::test::Options::get().evmVersion()); - c.parseAndAnalyze(); - map<string, unsigned> sourceIndices; - sourceIndices["a"] = 1; - Json::Value astJson = ASTJsonConverter(true, sourceIndices).toJson(c.ast("a")); - Json::Value varDecl = astJson["children"][0]["children"][0]["children"][2]["children"][0]["children"][0]; - BOOST_CHECK_EQUAL(varDecl["attributes"]["type"], "string"); - BOOST_CHECK_EQUAL(varDecl["attributes"]["typeName"], Json::nullValue); - Json::Value literal = astJson["children"][0]["children"][0]["children"][2]["children"][0]["children"][1]; - BOOST_CHECK_EQUAL(literal["name"], "Literal"); - BOOST_CHECK_EQUAL(literal["attributes"]["hexvalue"], "ff"); - BOOST_CHECK_EQUAL(literal["attributes"]["token"], "string"); - BOOST_CHECK_EQUAL(literal["attributes"]["value"], Json::nullValue); - BOOST_CHECK(literal["attributes"]["type"].asString().find("invalid") != string::npos); -} - -BOOST_AUTO_TEST_CASE(function_type) -{ - CompilerStack c; - c.addSource("a", - "contract C { function f(function() external payable returns (uint) x) " - "returns (function() external constant returns (uint)) {} }" - ); - c.setEVMVersion(dev::test::Options::get().evmVersion()); - c.parseAndAnalyze(); - map<string, unsigned> sourceIndices; - sourceIndices["a"] = 1; - Json::Value astJson = ASTJsonConverter(true, sourceIndices).toJson(c.ast("a")); - Json::Value fun = astJson["children"][0]["children"][0]; - BOOST_CHECK_EQUAL(fun["name"], "FunctionDefinition"); - Json::Value argument = fun["children"][0]["children"][0]; - BOOST_CHECK_EQUAL(argument["name"], "VariableDeclaration"); - BOOST_CHECK_EQUAL(argument["attributes"]["name"], "x"); - BOOST_CHECK_EQUAL(argument["attributes"]["type"], "function () payable external returns (uint256)"); - Json::Value funType = argument["children"][0]; - BOOST_CHECK_EQUAL(funType["attributes"]["constant"], false); - BOOST_CHECK_EQUAL(funType["attributes"]["payable"], true); - BOOST_CHECK_EQUAL(funType["attributes"]["visibility"], "external"); - Json::Value retval = fun["children"][1]["children"][0]; - BOOST_CHECK_EQUAL(retval["name"], "VariableDeclaration"); - BOOST_CHECK_EQUAL(retval["attributes"]["name"], ""); - BOOST_CHECK_EQUAL(retval["attributes"]["type"], "function () view external returns (uint256)"); - funType = retval["children"][0]; - BOOST_CHECK_EQUAL(funType["attributes"]["constant"], true); - BOOST_CHECK_EQUAL(funType["attributes"]["payable"], false); - BOOST_CHECK_EQUAL(funType["attributes"]["visibility"], "external"); -} - -BOOST_AUTO_TEST_CASE(documentation) -{ - CompilerStack c; - c.addSource("a", "/**This contract is empty*/ contract C {}"); - c.addSource("b", - "/**This contract is empty" - " and has a line-breaking comment.*/" - "contract C {}" - ); - c.addSource("c", - "contract C {" - " /** Some comment on Evt.*/ event Evt();" - " /** Some comment on mod.*/ modifier mod() { _; }" - " /** Some comment on fn.*/ function fn() public {}" - "}" - ); - c.setEVMVersion(dev::test::Options::get().evmVersion()); - c.parseAndAnalyze(); - map<string, unsigned> sourceIndices; - sourceIndices["a"] = 0; - sourceIndices["b"] = 1; - sourceIndices["c"] = 2; - Json::Value astJsonA = ASTJsonConverter(true, sourceIndices).toJson(c.ast("a")); - Json::Value documentationA = astJsonA["children"][0]["attributes"]["documentation"]; - BOOST_CHECK_EQUAL(documentationA, "This contract is empty"); - Json::Value astJsonB = ASTJsonConverter(true, sourceIndices).toJson(c.ast("b")); - Json::Value documentationB = astJsonB["children"][0]["attributes"]["documentation"]; - BOOST_CHECK_EQUAL(documentationB, "This contract is empty and has a line-breaking comment."); - Json::Value astJsonC = ASTJsonConverter(true, sourceIndices).toJson(c.ast("c")); - Json::Value documentationC0 = astJsonC["children"][0]["children"][0]["attributes"]["documentation"]; - Json::Value documentationC1 = astJsonC["children"][0]["children"][1]["attributes"]["documentation"]; - Json::Value documentationC2 = astJsonC["children"][0]["children"][2]["attributes"]["documentation"]; - BOOST_CHECK_EQUAL(documentationC0, "Some comment on Evt."); - BOOST_CHECK_EQUAL(documentationC1, "Some comment on mod."); - BOOST_CHECK_EQUAL(documentationC2, "Some comment on fn."); -} - - -BOOST_AUTO_TEST_SUITE_END() - -} -} -} // end namespaces diff --git a/test/libsolidity/AnalysisFramework.cpp b/test/libsolidity/AnalysisFramework.cpp index 72b86767..ec98026c 100644 --- a/test/libsolidity/AnalysisFramework.cpp +++ b/test/libsolidity/AnalysisFramework.cpp @@ -29,7 +29,7 @@ #include <libsolidity/parsing/Scanner.h> -#include <libdevcore/SHA3.h> +#include <libdevcore/Keccak256.h> #include <boost/test/unit_test.hpp> diff --git a/test/libsolidity/Assembly.cpp b/test/libsolidity/Assembly.cpp index 7b3df043..e815d7d5 100644 --- a/test/libsolidity/Assembly.cpp +++ b/test/libsolidity/Assembly.cpp @@ -152,7 +152,7 @@ BOOST_AUTO_TEST_CASE(location_test) { char const* sourceCode = R"( contract test { - function f() returns (uint256 a) { + function f() public returns (uint256 a) { return 16; } } @@ -160,19 +160,19 @@ BOOST_AUTO_TEST_CASE(location_test) AssemblyItems items = compileContract(sourceCode); bool hasShifts = dev::test::Options::get().evmVersion().hasBitwiseShifting(); vector<SourceLocation> locations = - vector<SourceLocation>(hasShifts ? 23 : 24, SourceLocation(2, 75, make_shared<string>(""))) + - vector<SourceLocation>(2, SourceLocation(20, 72, make_shared<string>(""))) + + vector<SourceLocation>(hasShifts ? 23 : 24, SourceLocation(2, 82, make_shared<string>(""))) + + vector<SourceLocation>(2, SourceLocation(20, 79, make_shared<string>(""))) + vector<SourceLocation>(1, SourceLocation(8, 17, make_shared<string>("--CODEGEN--"))) + vector<SourceLocation>(3, SourceLocation(5, 7, make_shared<string>("--CODEGEN--"))) + vector<SourceLocation>(1, SourceLocation(30, 31, make_shared<string>("--CODEGEN--"))) + vector<SourceLocation>(1, SourceLocation(27, 28, make_shared<string>("--CODEGEN--"))) + vector<SourceLocation>(1, SourceLocation(20, 32, make_shared<string>("--CODEGEN--"))) + vector<SourceLocation>(1, SourceLocation(5, 7, make_shared<string>("--CODEGEN--"))) + - vector<SourceLocation>(24, SourceLocation(20, 72, make_shared<string>(""))) + - vector<SourceLocation>(1, SourceLocation(42, 51, make_shared<string>(""))) + - vector<SourceLocation>(1, SourceLocation(65, 67, make_shared<string>(""))) + - vector<SourceLocation>(2, SourceLocation(58, 67, make_shared<string>(""))) + - vector<SourceLocation>(2, SourceLocation(20, 72, make_shared<string>(""))); + vector<SourceLocation>(24, SourceLocation(20, 79, make_shared<string>(""))) + + vector<SourceLocation>(1, SourceLocation(49, 58, make_shared<string>(""))) + + vector<SourceLocation>(1, SourceLocation(72, 74, make_shared<string>(""))) + + vector<SourceLocation>(2, SourceLocation(65, 74, make_shared<string>(""))) + + vector<SourceLocation>(2, SourceLocation(20, 79, make_shared<string>(""))); checkAssemblyLocations(items, locations); } diff --git a/test/libsolidity/GasMeter.cpp b/test/libsolidity/GasMeter.cpp index f16d9abe..4887dd5b 100644 --- a/test/libsolidity/GasMeter.cpp +++ b/test/libsolidity/GasMeter.cpp @@ -87,6 +87,7 @@ public: for (bytes const& arguments: _argumentVariants) { sendMessage(hash.asBytes() + arguments, false, 0); + BOOST_CHECK(m_transactionSuccessful); gasUsed = max(gasUsed, m_gasUsed); gas = max(gas, gasForTransaction(hash.asBytes() + arguments, false)); } @@ -118,10 +119,10 @@ BOOST_AUTO_TEST_CASE(non_overlapping_filtered_costs) char const* sourceCode = R"( contract test { bytes x; - function f(uint a) returns (uint b) { + function f(uint a) public returns (uint b) { x.length = a; for (; a < 200; ++a) { - x[a] = 9; + x[a] = 0x09; b = a * a; } return f(a - 1); @@ -151,8 +152,8 @@ BOOST_AUTO_TEST_CASE(simple_contract) char const* sourceCode = R"( contract test { bytes32 public shaValue; - function f(uint a) { - shaValue = keccak256(a); + function f(uint a) public { + shaValue = keccak256(abi.encodePacked(a)); } } )"; @@ -164,8 +165,8 @@ BOOST_AUTO_TEST_CASE(store_keccak256) char const* sourceCode = R"( contract test { bytes32 public shaValue; - function test(uint a) { - shaValue = keccak256(a); + constructor(uint a) public { + shaValue = keccak256(abi.encodePacked(a)); } } )"; @@ -178,7 +179,7 @@ BOOST_AUTO_TEST_CASE(updating_store) contract test { uint data; uint data2; - function test() { + constructor() public { data = 1; data = 2; data2 = 0; @@ -194,7 +195,7 @@ BOOST_AUTO_TEST_CASE(branches) contract test { uint data; uint data2; - function f(uint x) { + function f(uint x) public { if (x > 7) data2 = 1; else @@ -212,7 +213,7 @@ BOOST_AUTO_TEST_CASE(function_calls) contract test { uint data; uint data2; - function f(uint x) { + function f(uint x) public { if (x > 7) data2 = g(x**8) + 1; else @@ -233,13 +234,13 @@ BOOST_AUTO_TEST_CASE(multiple_external_functions) contract test { uint data; uint data2; - function f(uint x) { + function f(uint x) public { if (x > 7) data2 = g(x**8) + 1; else data = 1; } - function g(uint x) returns (uint) { + function g(uint x) public returns (uint) { return data2; } } @@ -253,10 +254,10 @@ BOOST_AUTO_TEST_CASE(exponent_size) { char const* sourceCode = R"( contract A { - function g(uint x) returns (uint) { + function g(uint x) public returns (uint) { return x ** 0x100; } - function h(uint x) returns (uint) { + function h(uint x) public returns (uint) { return x ** 0x10000; } } @@ -270,7 +271,7 @@ BOOST_AUTO_TEST_CASE(balance_gas) { char const* sourceCode = R"( contract A { - function lookup_balance(address a) returns (uint) { + function lookup_balance(address a) public returns (uint) { return a.balance; } } @@ -283,7 +284,7 @@ BOOST_AUTO_TEST_CASE(extcodesize_gas) { char const* sourceCode = R"( contract A { - function f() returns (uint _s) { + function f() public returns (uint _s) { assembly { _s := extcodesize(0x30) } @@ -301,7 +302,7 @@ BOOST_AUTO_TEST_CASE(regular_functions_exclude_fallback) char const* sourceCode = R"( contract A { uint public x; - function() { x = 2; } + function() external { x = 2; } } )"; testCreationTimeGas(sourceCode); @@ -315,7 +316,7 @@ BOOST_AUTO_TEST_CASE(complex_control_flow) // we previously considered. This of course reduces accuracy. char const* sourceCode = R"( contract log { - function ln(int128 x) constant returns (int128 result) { + function ln(int128 x) public pure returns (int128 result) { int128 t = x / 256; int128 y = 5545177; x = t; diff --git a/test/libsolidity/Imports.cpp b/test/libsolidity/Imports.cpp index 1b5dd4a5..dc33d577 100644 --- a/test/libsolidity/Imports.cpp +++ b/test/libsolidity/Imports.cpp @@ -109,7 +109,7 @@ BOOST_AUTO_TEST_CASE(simple_alias) { CompilerStack c; c.addSource("a", "contract A {} pragma solidity >=0.0;"); - c.addSource("dir/a/b/c", "import \"../../.././a\" as x; contract B is x.A { function() { x.A r = x.A(20); } } pragma solidity >=0.0;"); + c.addSource("dir/a/b/c", "import \"../../.././a\" as x; contract B is x.A { function() external { x.A r = x.A(20); } } pragma solidity >=0.0;"); c.setEVMVersion(dev::test::Options::get().evmVersion()); BOOST_CHECK(c.compile()); } @@ -138,7 +138,7 @@ BOOST_AUTO_TEST_CASE(complex_import) CompilerStack c; c.addSource("a", "contract A {} contract B {} contract C { struct S { uint a; } } pragma solidity >=0.0;"); c.addSource("b", "import \"a\" as x; import {B as b, C as c, C} from \"a\"; " - "contract D is b { function f(c.S var1, x.C.S var2, C.S var3) internal {} } pragma solidity >=0.0;"); + "contract D is b { function f(c.S memory var1, x.C.S memory var2, C.S memory var3) internal {} } pragma solidity >=0.0;"); c.setEVMVersion(dev::test::Options::get().evmVersion()); BOOST_CHECK(c.compile()); } @@ -167,7 +167,7 @@ BOOST_AUTO_TEST_CASE(name_clash_in_import) BOOST_AUTO_TEST_CASE(remappings) { CompilerStack c; - c.setRemappings(vector<string>{"s=s_1.4.6", "t=Tee"}); + c.setRemappings(vector<CompilerStack::Remapping>{{"", "s", "s_1.4.6"},{"", "t", "Tee"}}); c.addSource("a", "import \"s/s.sol\"; contract A is S {} pragma solidity >=0.0;"); c.addSource("b", "import \"t/tee.sol\"; contract A is Tee {} pragma solidity >=0.0;"); c.addSource("s_1.4.6/s.sol", "contract S {} pragma solidity >=0.0;"); @@ -179,7 +179,7 @@ BOOST_AUTO_TEST_CASE(remappings) BOOST_AUTO_TEST_CASE(context_dependent_remappings) { CompilerStack c; - c.setRemappings(vector<string>{"a:s=s_1.4.6", "b:s=s_1.4.7"}); + c.setRemappings(vector<CompilerStack::Remapping>{{"a", "s", "s_1.4.6"}, {"b", "s", "s_1.4.7"}}); c.addSource("a/a.sol", "import \"s/s.sol\"; contract A is SSix {} pragma solidity >=0.0;"); c.addSource("b/b.sol", "import \"s/s.sol\"; contract B is SSeven {} pragma solidity >=0.0;"); c.addSource("s_1.4.6/s.sol", "contract SSix {} pragma solidity >=0.0;"); @@ -200,7 +200,11 @@ BOOST_AUTO_TEST_CASE(filename_with_period) BOOST_AUTO_TEST_CASE(context_dependent_remappings_ensure_default_and_module_preserved) { CompilerStack c; - c.setRemappings(vector<string>{"foo=vendor/foo_2.0.0", "vendor/bar:foo=vendor/foo_1.0.0", "bar=vendor/bar"}); + c.setRemappings(vector<CompilerStack::Remapping>{ + {"", "foo", "vendor/foo_2.0.0"}, + {"vendor/bar", "foo", "vendor/foo_1.0.0"}, + {"", "bar", "vendor/bar"} + }); c.addSource("main.sol", "import \"foo/foo.sol\"; import {Bar} from \"bar/bar.sol\"; contract Main is Foo2, Bar {} pragma solidity >=0.0;"); c.addSource("vendor/bar/bar.sol", "import \"foo/foo.sol\"; contract Bar {Foo1 foo;} pragma solidity >=0.0;"); c.addSource("vendor/foo_1.0.0/foo.sol", "contract Foo1 {} pragma solidity >=0.0;"); @@ -212,7 +216,7 @@ BOOST_AUTO_TEST_CASE(context_dependent_remappings_ensure_default_and_module_pres BOOST_AUTO_TEST_CASE(context_dependent_remappings_order_independent) { CompilerStack c; - c.setRemappings(vector<string>{"a:x/y/z=d", "a/b:x=e"}); + c.setRemappings(vector<CompilerStack::Remapping>{{"a", "x/y/z", "d"}, {"a/b", "x", "e"}}); c.addSource("a/main.sol", "import \"x/y/z/z.sol\"; contract Main is D {} pragma solidity >=0.0;"); c.addSource("a/b/main.sol", "import \"x/y/z/z.sol\"; contract Main is E {} pragma solidity >=0.0;"); c.addSource("d/z.sol", "contract D {} pragma solidity >=0.0;"); @@ -220,7 +224,7 @@ BOOST_AUTO_TEST_CASE(context_dependent_remappings_order_independent) c.setEVMVersion(dev::test::Options::get().evmVersion()); BOOST_CHECK(c.compile()); CompilerStack d; - d.setRemappings(vector<string>{"a/b:x=e", "a:x/y/z=d"}); + d.setRemappings(vector<CompilerStack::Remapping>{{"a/b", "x", "e"}, {"a", "x/y/z", "d"}}); d.addSource("a/main.sol", "import \"x/y/z/z.sol\"; contract Main is D {} pragma solidity >=0.0;"); d.addSource("a/b/main.sol", "import \"x/y/z/z.sol\"; contract Main is E {} pragma solidity >=0.0;"); d.addSource("d/z.sol", "contract D {} pragma solidity >=0.0;"); diff --git a/test/libsolidity/InlineAssembly.cpp b/test/libsolidity/InlineAssembly.cpp index 181ca959..a9ce6e49 100644 --- a/test/libsolidity/InlineAssembly.cpp +++ b/test/libsolidity/InlineAssembly.cpp @@ -178,9 +178,9 @@ BOOST_AUTO_TEST_CASE(simple_instructions) BOOST_CHECK(successParse("{ dup1 dup1 mul dup1 sub pop }")); } -BOOST_AUTO_TEST_CASE(suicide_selfdestruct) +BOOST_AUTO_TEST_CASE(selfdestruct) { - BOOST_CHECK(successParse("{ 0x01 suicide 0x02 selfdestruct }")); + BOOST_CHECK(successParse("{ 0x02 selfdestruct }")); } BOOST_AUTO_TEST_CASE(keywords) @@ -461,7 +461,7 @@ BOOST_AUTO_TEST_CASE(recursion_depth) BOOST_AUTO_TEST_CASE(multiple_assignment) { CHECK_PARSE_ERROR("{ let x function f() -> a, b {} 123, x := f() }", ParserError, "Label name / variable name must precede \",\" (multiple assignment)."); - CHECK_PARSE_ERROR("{ let x function f() -> a, b {} x, 123 := f() }", ParserError, "Variable name expected in multiple assignemnt."); + CHECK_PARSE_ERROR("{ let x function f() -> a, b {} x, 123 := f() }", ParserError, "Variable name expected in multiple assignment."); /// NOTE: Travis hiccups if not having a variable char const* text = R"( @@ -740,8 +740,6 @@ BOOST_AUTO_TEST_CASE(keccak256) { BOOST_CHECK(successAssemble("{ 0 0 keccak256 pop }")); BOOST_CHECK(successAssemble("{ pop(keccak256(0, 0)) }")); - BOOST_CHECK(successAssemble("{ 0 0 sha3 pop }")); - BOOST_CHECK(successAssemble("{ pop(sha3(0, 0)) }")); } BOOST_AUTO_TEST_CASE(returndatasize) diff --git a/test/libsolidity/JSONCompiler.cpp b/test/libsolidity/JSONCompiler.cpp deleted file mode 100644 index 2b3df3a7..00000000 --- a/test/libsolidity/JSONCompiler.cpp +++ /dev/null @@ -1,208 +0,0 @@ -/* - This file is part of solidity. - - solidity is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - solidity is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with solidity. If not, see <http://www.gnu.org/licenses/>. -*/ -/** - * @date 2017 - * Unit tests for solc/jsonCompiler.cpp. - */ - -#include <string> -#include <boost/test/unit_test.hpp> -#include <libdevcore/JSON.h> -#include <libsolidity/interface/Version.h> -#include <libsolc/libsolc.h> - -#include <test/Metadata.h> -#include <test/Options.h> - -using namespace std; - -namespace dev -{ -namespace solidity -{ -namespace test -{ - -namespace -{ - -Json::Value compileSingle(string const& _input) -{ - string output(compileJSON(_input.c_str(), dev::test::Options::get().optimize)); - Json::Value ret; - BOOST_REQUIRE(jsonParseStrict(output, ret)); - return ret; -} - -Json::Value compileMulti(string const& _input, bool _callback) -{ - string output( - _callback ? - compileJSONCallback(_input.c_str(), dev::test::Options::get().optimize, NULL) : - compileJSONMulti(_input.c_str(), dev::test::Options::get().optimize) - ); - Json::Value ret; - BOOST_REQUIRE(jsonParseStrict(output, ret)); - return ret; -} - -Json::Value compile(string const& _input) -{ - string output(compileStandard(_input.c_str(), NULL)); - Json::Value ret; - BOOST_REQUIRE(jsonParseStrict(output, ret)); - return ret; -} - -} // end anonymous namespace - -BOOST_AUTO_TEST_SUITE(JSONCompiler) - -BOOST_AUTO_TEST_CASE(read_version) -{ - string output(version()); - BOOST_CHECK(output.find(VersionString) == 0); -} - -BOOST_AUTO_TEST_CASE(read_license) -{ - string output(license()); - BOOST_CHECK(output.find("GNU GENERAL PUBLIC LICENSE") != string::npos); -} - -BOOST_AUTO_TEST_CASE(basic_compilation) -{ - char const* input = R"( - { - "sources": { - "fileA": "contract A { }" - } - } - )"; - Json::Value result = compileMulti(input, false); - BOOST_CHECK(result.isObject()); - - // Compare with compileJSONCallback - BOOST_CHECK_EQUAL( - dev::jsonCompactPrint(result), - dev::jsonCompactPrint(compileMulti(input, true)) - ); - - BOOST_CHECK(result["contracts"].isObject()); - BOOST_CHECK(result["contracts"]["fileA:A"].isObject()); - Json::Value contract = result["contracts"]["fileA:A"]; - BOOST_CHECK(contract.isObject()); - BOOST_CHECK(contract["interface"].isString()); - BOOST_CHECK_EQUAL(contract["interface"].asString(), "[]"); - BOOST_CHECK(contract["bytecode"].isString()); - BOOST_CHECK_EQUAL( - dev::test::bytecodeSansMetadata(contract["bytecode"].asString()), - "6080604052348015600f57600080fd5b50603580601d6000396000f3006080604052600080fd00" - ); - BOOST_CHECK(contract["runtimeBytecode"].isString()); - BOOST_CHECK_EQUAL( - dev::test::bytecodeSansMetadata(contract["runtimeBytecode"].asString()), - "6080604052600080fd00" - ); - BOOST_CHECK(contract["functionHashes"].isObject()); - BOOST_CHECK(contract["gasEstimates"].isObject()); - BOOST_CHECK_EQUAL( - dev::jsonCompactPrint(contract["gasEstimates"]), - "{\"creation\":[66,10600],\"external\":{},\"internal\":{}}" - ); - BOOST_CHECK(contract["metadata"].isString()); - BOOST_CHECK(dev::test::isValidMetadata(contract["metadata"].asString())); - BOOST_CHECK(result["sources"].isObject()); - BOOST_CHECK(result["sources"]["fileA"].isObject()); - BOOST_CHECK(result["sources"]["fileA"]["AST"].isObject()); - BOOST_CHECK_EQUAL( - dev::jsonCompactPrint(result["sources"]["fileA"]["AST"]), - "{\"attributes\":{\"absolutePath\":\"fileA\",\"exportedSymbols\":{\"A\":[1]}}," - "\"children\":[{\"attributes\":{\"baseContracts\":[null],\"contractDependencies\":[null]," - "\"contractKind\":\"contract\",\"documentation\":null,\"fullyImplemented\":true,\"linearizedBaseContracts\":[1]," - "\"name\":\"A\",\"nodes\":[null],\"scope\":2},\"id\":1,\"name\":\"ContractDefinition\"," - "\"src\":\"0:14:0\"}],\"id\":2,\"name\":\"SourceUnit\",\"src\":\"0:14:0\"}" - ); -} - -BOOST_AUTO_TEST_CASE(single_compilation) -{ - Json::Value result = compileSingle("contract A { }"); - BOOST_CHECK(result.isObject()); - - BOOST_CHECK(result["contracts"].isObject()); - BOOST_CHECK(result["contracts"][":A"].isObject()); - Json::Value contract = result["contracts"][":A"]; - BOOST_CHECK(contract.isObject()); - BOOST_CHECK(contract["interface"].isString()); - BOOST_CHECK_EQUAL(contract["interface"].asString(), "[]"); - BOOST_CHECK(contract["bytecode"].isString()); - BOOST_CHECK_EQUAL( - dev::test::bytecodeSansMetadata(contract["bytecode"].asString()), - "6080604052348015600f57600080fd5b50603580601d6000396000f3006080604052600080fd00" - ); - BOOST_CHECK(contract["runtimeBytecode"].isString()); - BOOST_CHECK_EQUAL( - dev::test::bytecodeSansMetadata(contract["runtimeBytecode"].asString()), - "6080604052600080fd00" - ); - BOOST_CHECK(contract["functionHashes"].isObject()); - BOOST_CHECK(contract["gasEstimates"].isObject()); - BOOST_CHECK_EQUAL( - dev::jsonCompactPrint(contract["gasEstimates"]), - "{\"creation\":[66,10600],\"external\":{},\"internal\":{}}" - ); - BOOST_CHECK(contract["metadata"].isString()); - BOOST_CHECK(dev::test::isValidMetadata(contract["metadata"].asString())); - BOOST_CHECK(result["sources"].isObject()); - BOOST_CHECK(result["sources"][""].isObject()); - BOOST_CHECK(result["sources"][""]["AST"].isObject()); - BOOST_CHECK_EQUAL( - dev::jsonCompactPrint(result["sources"][""]["AST"]), - "{\"attributes\":{\"absolutePath\":\"\",\"exportedSymbols\":{\"A\":[1]}}," - "\"children\":[{\"attributes\":{\"baseContracts\":[null],\"contractDependencies\":[null]," - "\"contractKind\":\"contract\",\"documentation\":null,\"fullyImplemented\":true,\"linearizedBaseContracts\":[1]," - "\"name\":\"A\",\"nodes\":[null],\"scope\":2},\"id\":1,\"name\":\"ContractDefinition\"," - "\"src\":\"0:14:0\"}],\"id\":2,\"name\":\"SourceUnit\",\"src\":\"0:14:0\"}" - ); -} - -BOOST_AUTO_TEST_CASE(standard_compilation) -{ - char const* input = R"( - { - "language": "Solidity", - "sources": { - "fileA": { - "content": "contract A { }" - } - } - } - )"; - Json::Value result = compile(input); - BOOST_CHECK(result.isObject()); - - // Only tests some assumptions. The StandardCompiler is tested properly in another suite. - BOOST_CHECK(result.isMember("sources")); - BOOST_CHECK(result.isMember("contracts")); -} - -BOOST_AUTO_TEST_SUITE_END() - -} -} -} // end namespaces diff --git a/test/libsolidity/LibSolc.cpp b/test/libsolidity/LibSolc.cpp new file mode 100644 index 00000000..09c08700 --- /dev/null +++ b/test/libsolidity/LibSolc.cpp @@ -0,0 +1,91 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see <http://www.gnu.org/licenses/>. +*/ +/** + * @date 2017 + * Unit tests for libsolc/libsolc.cpp. + */ + +#include <string> +#include <boost/test/unit_test.hpp> +#include <libdevcore/JSON.h> +#include <libsolidity/interface/Version.h> +#include <libsolc/libsolc.h> + +#include <test/Metadata.h> +#include <test/Options.h> + +using namespace std; + +namespace dev +{ +namespace solidity +{ +namespace test +{ + +namespace +{ + +Json::Value compile(string const& _input) +{ + string output(solidity_compile(_input.c_str(), nullptr)); + Json::Value ret; + BOOST_REQUIRE(jsonParseStrict(output, ret)); + return ret; +} + +} // end anonymous namespace + +BOOST_AUTO_TEST_SUITE(LibSolc) + +BOOST_AUTO_TEST_CASE(read_version) +{ + string output(solidity_version()); + BOOST_CHECK(output.find(VersionString) == 0); +} + +BOOST_AUTO_TEST_CASE(read_license) +{ + string output(solidity_license()); + BOOST_CHECK(output.find("GNU GENERAL PUBLIC LICENSE") != string::npos); +} + +BOOST_AUTO_TEST_CASE(standard_compilation) +{ + char const* input = R"( + { + "language": "Solidity", + "sources": { + "fileA": { + "content": "contract A { }" + } + } + } + )"; + Json::Value result = compile(input); + BOOST_CHECK(result.isObject()); + + // Only tests some assumptions. The StandardCompiler is tested properly in another suite. + BOOST_CHECK(result.isMember("sources")); + BOOST_CHECK(result.isMember("contracts")); +} + +BOOST_AUTO_TEST_SUITE_END() + +} +} +} // end namespaces diff --git a/test/libsolidity/Metadata.cpp b/test/libsolidity/Metadata.cpp index 808bd1e1..007ee2b6 100644 --- a/test/libsolidity/Metadata.cpp +++ b/test/libsolidity/Metadata.cpp @@ -41,7 +41,7 @@ BOOST_AUTO_TEST_CASE(metadata_stamp) pragma solidity >=0.0; pragma experimental __testOnlyAnalysis; contract test { - function g(function(uint) external returns (uint) x) {} + function g(function(uint) external returns (uint) x) public {} } )"; CompilerStack compilerStack; @@ -68,7 +68,7 @@ BOOST_AUTO_TEST_CASE(metadata_stamp_experimental) pragma solidity >=0.0; pragma experimental __test; contract test { - function g(function(uint) external returns (uint) x) {} + function g(function(uint) external returns (uint) x) public {} } )"; CompilerStack compilerStack; @@ -97,14 +97,14 @@ BOOST_AUTO_TEST_CASE(metadata_relevant_sources) char const* sourceCode = R"( pragma solidity >=0.0; contract A { - function g(function(uint) external returns (uint) x) {} + function g(function(uint) external returns (uint) x) public {} } )"; compilerStack.addSource("A", std::string(sourceCode)); sourceCode = R"( pragma solidity >=0.0; contract B { - function g(function(uint) external returns (uint) x) {} + function g(function(uint) external returns (uint) x) public {} } )"; compilerStack.addSource("B", std::string(sourceCode)); @@ -127,7 +127,7 @@ BOOST_AUTO_TEST_CASE(metadata_relevant_sources_imports) char const* sourceCode = R"( pragma solidity >=0.0; contract A { - function g(function(uint) external returns (uint) x) {} + function g(function(uint) external returns (uint) x) public {} } )"; compilerStack.addSource("A", std::string(sourceCode)); @@ -135,7 +135,7 @@ BOOST_AUTO_TEST_CASE(metadata_relevant_sources_imports) pragma solidity >=0.0; import "./A"; contract B is A { - function g(function(uint) external returns (uint) x) {} + function g(function(uint) external returns (uint) x) public {} } )"; compilerStack.addSource("B", std::string(sourceCode)); @@ -143,7 +143,7 @@ BOOST_AUTO_TEST_CASE(metadata_relevant_sources_imports) pragma solidity >=0.0; import "./B"; contract C is B { - function g(function(uint) external returns (uint) x) {} + function g(function(uint) external returns (uint) x) public {} } )"; compilerStack.addSource("C", std::string(sourceCode)); diff --git a/test/libsolidity/SMTChecker.cpp b/test/libsolidity/SMTChecker.cpp index 71fdb906..195004cb 100644 --- a/test/libsolidity/SMTChecker.cpp +++ b/test/libsolidity/SMTChecker.cpp @@ -91,7 +91,7 @@ BOOST_AUTO_TEST_CASE(warn_on_struct) pragma experimental ABIEncoderV2; contract C { struct A { uint a; uint b; } - function f() public pure returns (A) { + function f() public pure returns (A memory) { return A({ a: 1, b: 2 }); } } @@ -110,7 +110,7 @@ BOOST_AUTO_TEST_CASE(simple_assert) function f(uint a) public pure { assert(a == 2); } } )"; - CHECK_WARNING(text, "Assertion violation happens here for"); + CHECK_WARNING(text, "Assertion violation happens here"); } BOOST_AUTO_TEST_CASE(simple_assert_with_require) @@ -133,38 +133,6 @@ BOOST_AUTO_TEST_CASE(assignment_in_declaration) CHECK_SUCCESS_NO_WARNINGS(text); } -BOOST_AUTO_TEST_CASE(use_before_declaration) -{ - string text = R"( - contract C { - function f() public pure { a = 3; uint a = 2; assert(a == 2); } - } - )"; - CHECK_SUCCESS_NO_WARNINGS(text); - text = R"( - contract C { - function f() public pure { assert(a == 0); uint a = 2; assert(a == 2); } - } - )"; - CHECK_SUCCESS_NO_WARNINGS(text); -} - -BOOST_AUTO_TEST_CASE(function_call_does_not_clear_local_vars) -{ - string text = R"( - contract C { - function f() public { - uint a = 3; - this.f(); - assert(a == 3); - f(); - assert(a == 3); - } - } - )"; - CHECK_SUCCESS_NO_WARNINGS(text); -} - BOOST_AUTO_TEST_CASE(branches_merge_variables) { // Branch does not touch variable a @@ -359,7 +327,7 @@ BOOST_AUTO_TEST_CASE(bool_simple) text = R"( contract C { function f(bool x) public pure { - if(x) { + if (x) { assert(x); } else { assert(!x); @@ -388,35 +356,6 @@ BOOST_AUTO_TEST_CASE(bool_simple) } )"; CHECK_SUCCESS_NO_WARNINGS(text); - text = R"( - contract C { - function f(bool x) public pure { - bool y; - assert(x <= y); - } - } - )"; - CHECK_WARNING(text, "Assertion violation happens here"); - text = R"( - contract C { - function f(bool x) public pure { - bool y; - assert(x >= y); - } - } - )"; - CHECK_SUCCESS_NO_WARNINGS(text); - text = R"( - contract C { - function f(bool x) public pure { - require(x); - bool y; - assert(x > y); - assert(y < x); - } - } - )"; - CHECK_SUCCESS_NO_WARNINGS(text); } BOOST_AUTO_TEST_CASE(bool_int_mixed) @@ -425,7 +364,7 @@ BOOST_AUTO_TEST_CASE(bool_int_mixed) contract C { function f(bool x) public pure { uint a; - if(x) + if (x) a = 1; assert(!x || a > 0); } @@ -478,15 +417,15 @@ BOOST_AUTO_TEST_CASE(storage_value_vars) function f(uint x) public { if (x == 0) { - a = 100; + a = 0x0000000000000000000000000000000000000100; b = true; } else { - a = 200; + a = 0x0000000000000000000000000000000000000200; b = false; } - assert(a > 0 && b); + assert(a > 0x0000000000000000000000000000000000000000 && b); } } )"; @@ -509,19 +448,19 @@ BOOST_AUTO_TEST_CASE(storage_value_vars) function f(uint x) public { if (x == 0) { - a = 100; + a = 0x0000000000000000000000000000000000000100; b = true; } else { - a = 200; + a = 0x0000000000000000000000000000000000000200; b = false; } - assert(b == (a < 200)); + assert(b == (a < 0x0000000000000000000000000000000000000200)); } function g() public view { - require(a < 100); + require(a < 0x0000000000000000000000000000000000000100); assert(c >= 0); } address a; @@ -614,7 +553,10 @@ BOOST_AUTO_TEST_CASE(constant_condition) } } )"; - CHECK_WARNING(text, "Condition is always true"); + CHECK_WARNING_ALLOW_MULTI(text, (vector<string>{ + "Condition is always true", + "Assertion checker does not yet implement this type of function call" + })); text = R"( contract C { function f(uint x) public pure { @@ -622,7 +564,10 @@ BOOST_AUTO_TEST_CASE(constant_condition) } } )"; - CHECK_WARNING(text, "Condition is always false"); + CHECK_WARNING_ALLOW_MULTI(text, (vector<string>{ + "Condition is always false", + "Assertion checker does not yet implement this type of function call" + })); // a plain literal constant is fine text = R"( contract C { @@ -631,7 +576,7 @@ BOOST_AUTO_TEST_CASE(constant_condition) } } )"; - CHECK_SUCCESS_NO_WARNINGS(text); + CHECK_WARNING(text, "Assertion checker does not yet implement this type of function call"); } diff --git a/test/libsolidity/SemVerMatcher.cpp b/test/libsolidity/SemVerMatcher.cpp index 07f8fba6..43951f73 100644 --- a/test/libsolidity/SemVerMatcher.cpp +++ b/test/libsolidity/SemVerMatcher.cpp @@ -42,13 +42,13 @@ SemVerMatchExpression parseExpression(string const& _input) { Scanner scanner{CharStream(_input)}; vector<string> literals; - vector<Token::Value> tokens; + vector<Token> tokens; while (scanner.currentToken() != Token::EOS) { auto token = scanner.currentToken(); string literal = scanner.currentLiteral(); - if (literal.empty() && Token::toString(token)) - literal = Token::toString(token); + if (literal.empty() && TokenTraits::toString(token)) + literal = TokenTraits::toString(token); literals.push_back(literal); tokens.push_back(token); scanner.next(); diff --git a/test/libsolidity/SolidityABIJSON.cpp b/test/libsolidity/SolidityABIJSON.cpp index 3475b018..a8a67bca 100644 --- a/test/libsolidity/SolidityABIJSON.cpp +++ b/test/libsolidity/SolidityABIJSON.cpp @@ -40,7 +40,7 @@ class JSONInterfaceChecker public: JSONInterfaceChecker(): m_compilerStack() {} - void checkInterface(std::string const& _code, std::string const& _expectedInterfaceString) + void checkInterface(std::string const& _code, std::string const& _contractName, std::string const& _expectedInterfaceString) { m_compilerStack.reset(false); m_compilerStack.addSource("", "pragma solidity >=0.0;\n" + _code); @@ -48,7 +48,7 @@ public: m_compilerStack.setOptimiserSettings(dev::test::Options::get().optimize); BOOST_REQUIRE_MESSAGE(m_compilerStack.parseAndAnalyze(), "Parsing contract failed"); - Json::Value generatedInterface = m_compilerStack.contractABI(m_compilerStack.lastContractName()); + Json::Value generatedInterface = m_compilerStack.contractABI(_contractName); Json::Value expectedInterface; BOOST_REQUIRE(jsonParseStrict(_expectedInterfaceString, expectedInterface)); BOOST_CHECK_MESSAGE( @@ -68,7 +68,7 @@ BOOST_AUTO_TEST_CASE(basic_test) { char const* sourceCode = R"( contract test { - function f(uint a) returns(uint d) { return a * 7; } + function f(uint a) public returns (uint d) { return a * 7; } } )"; @@ -94,7 +94,7 @@ BOOST_AUTO_TEST_CASE(basic_test) } ])"; - checkInterface(sourceCode, interface); + checkInterface(sourceCode, "test", interface); } BOOST_AUTO_TEST_CASE(empty_contract) @@ -104,15 +104,15 @@ BOOST_AUTO_TEST_CASE(empty_contract) )"; char const* interface = "[]"; - checkInterface(sourceCode, interface); + checkInterface(sourceCode, "test", interface); } BOOST_AUTO_TEST_CASE(multiple_methods) { char const* sourceCode = R"( contract test { - function f(uint a) returns(uint d) { return a * 7; } - function g(uint b) returns(uint e) { return b * 8; } + function f(uint a) public returns (uint d) { return a * 7; } + function g(uint b) public returns (uint e) { return b * 8; } } )"; @@ -157,14 +157,14 @@ BOOST_AUTO_TEST_CASE(multiple_methods) } ])"; - checkInterface(sourceCode, interface); + checkInterface(sourceCode, "test", interface); } BOOST_AUTO_TEST_CASE(multiple_params) { char const* sourceCode = R"( contract test { - function f(uint a, uint b) returns(uint d) { return a + b; } + function f(uint a, uint b) public returns (uint d) { return a + b; } } )"; @@ -194,16 +194,16 @@ BOOST_AUTO_TEST_CASE(multiple_params) } ])"; - checkInterface(sourceCode, interface); + checkInterface(sourceCode, "test", interface); } BOOST_AUTO_TEST_CASE(multiple_methods_order) { - // methods are expected to be in alpabetical order + // methods are expected to be in alphabetical order char const* sourceCode = R"( contract test { - function f(uint a) returns(uint d) { return a * 7; } - function c(uint b) returns(uint e) { return b * 8; } + function f(uint a) public returns (uint d) { return a * 7; } + function c(uint b) public returns (uint e) { return b * 8; } } )"; @@ -248,15 +248,15 @@ BOOST_AUTO_TEST_CASE(multiple_methods_order) } ])"; - checkInterface(sourceCode, interface); + checkInterface(sourceCode, "test", interface); } BOOST_AUTO_TEST_CASE(view_function) { char const* sourceCode = R"( contract test { - function foo(uint a, uint b) returns(uint d) { return a + b; } - function boo(uint32 a) view returns(uint b) { return a * 4; } + function foo(uint a, uint b) public returns (uint d) { return a + b; } + function boo(uint32 a) public view returns(uint b) { return a * 4; } } )"; @@ -303,71 +303,15 @@ BOOST_AUTO_TEST_CASE(view_function) } ])"; - checkInterface(sourceCode, interface); -} - -// constant is an alias to view above -BOOST_AUTO_TEST_CASE(constant_function) -{ - char const* sourceCode = R"( - contract test { - function foo(uint a, uint b) returns(uint d) { return a + b; } - function boo(uint32 a) constant returns(uint b) { return a * 4; } - } - )"; - - char const* interface = R"([ - { - "name": "foo", - "constant": false, - "payable" : false, - "stateMutability": "nonpayable", - "type": "function", - "inputs": [ - { - "name": "a", - "type": "uint256" - }, - { - "name": "b", - "type": "uint256" - } - ], - "outputs": [ - { - "name": "d", - "type": "uint256" - } - ] - }, - { - "name": "boo", - "constant": true, - "payable" : false, - "stateMutability": "view", - "type": "function", - "inputs": [{ - "name": "a", - "type": "uint32" - }], - "outputs": [ - { - "name": "b", - "type": "uint256" - } - ] - } - ])"; - - checkInterface(sourceCode, interface); + checkInterface(sourceCode, "test", interface); } BOOST_AUTO_TEST_CASE(pure_function) { char const* sourceCode = R"( contract test { - function foo(uint a, uint b) returns(uint d) { return a + b; } - function boo(uint32 a) pure returns(uint b) { return a * 4; } + function foo(uint a, uint b) public returns (uint d) { return a + b; } + function boo(uint32 a) public pure returns (uint b) { return a * 4; } } )"; @@ -414,14 +358,14 @@ BOOST_AUTO_TEST_CASE(pure_function) } ])"; - checkInterface(sourceCode, interface); + checkInterface(sourceCode, "test", interface); } BOOST_AUTO_TEST_CASE(events) { char const* sourceCode = R"( contract test { - function f(uint a) returns(uint d) { return a * 7; } + function f(uint a) public returns (uint d) { return a * 7; } event e1(uint b, address indexed c); event e2(); event e2(uint a); @@ -492,7 +436,7 @@ BOOST_AUTO_TEST_CASE(events) ])"; - checkInterface(sourceCode, interface); + checkInterface(sourceCode, "test", interface); } BOOST_AUTO_TEST_CASE(events_anonymous) @@ -512,18 +456,18 @@ BOOST_AUTO_TEST_CASE(events_anonymous) ])"; - checkInterface(sourceCode, interface); + checkInterface(sourceCode, "test", interface); } BOOST_AUTO_TEST_CASE(inherited) { char const* sourceCode = R"( contract Base { - function baseFunction(uint p) returns (uint i) { return p; } + function baseFunction(uint p) public returns (uint i) { return p; } event baseEvent(bytes32 indexed evtArgBase); } contract Derived is Base { - function derivedFunction(bytes32 p) returns (bytes32 i) { return p; } + function derivedFunction(bytes32 p) public returns (bytes32 i) { return p; } event derivedEvent(uint indexed evtArgDerived); } )"; @@ -587,13 +531,13 @@ BOOST_AUTO_TEST_CASE(inherited) }])"; - checkInterface(sourceCode, interface); + checkInterface(sourceCode, "Derived", interface); } BOOST_AUTO_TEST_CASE(empty_name_input_parameter_with_named_one) { char const* sourceCode = R"( contract test { - function f(uint, uint k) returns(uint ret_k, uint ret_g) { + function f(uint, uint k) public returns (uint ret_k, uint ret_g) { uint g = 8; ret_k = k; ret_g = g; @@ -631,14 +575,14 @@ BOOST_AUTO_TEST_CASE(empty_name_input_parameter_with_named_one) } ])"; - checkInterface(sourceCode, interface); + checkInterface(sourceCode, "test", interface); } BOOST_AUTO_TEST_CASE(empty_name_return_parameter) { char const* sourceCode = R"( contract test { - function f(uint k) returns(uint) { + function f(uint k) public returns (uint) { return k; } } @@ -665,14 +609,14 @@ BOOST_AUTO_TEST_CASE(empty_name_return_parameter) ] } ])"; - checkInterface(sourceCode, interface); + checkInterface(sourceCode, "test", interface); } BOOST_AUTO_TEST_CASE(constructor_abi) { char const* sourceCode = R"( contract test { - function test(uint param1, test param2, bool param3) {} + constructor(uint param1, test param2, bool param3) public {} } )"; @@ -697,14 +641,14 @@ BOOST_AUTO_TEST_CASE(constructor_abi) "type": "constructor" } ])"; - checkInterface(sourceCode, interface); + checkInterface(sourceCode, "test", interface); } BOOST_AUTO_TEST_CASE(payable_constructor_abi) { char const* sourceCode = R"( contract test { - function test(uint param1, test param2, bool param3) payable {} + constructor(uint param1, test param2, bool param3) public payable {} } )"; @@ -729,7 +673,7 @@ BOOST_AUTO_TEST_CASE(payable_constructor_abi) "type": "constructor" } ])"; - checkInterface(sourceCode, interface); + checkInterface(sourceCode, "test", interface); } BOOST_AUTO_TEST_CASE(return_param_in_abi) @@ -738,8 +682,8 @@ BOOST_AUTO_TEST_CASE(return_param_in_abi) char const* sourceCode = R"( contract test { enum ActionChoices { GoLeft, GoRight, GoStraight, Sit } - function test(ActionChoices param) {} - function ret() returns(ActionChoices) { + constructor(ActionChoices param) public {} + function ret() public returns (ActionChoices) { ActionChoices action = ActionChoices.GoLeft; return action; } @@ -775,7 +719,7 @@ BOOST_AUTO_TEST_CASE(return_param_in_abi) } ] )"; - checkInterface(sourceCode, interface); + checkInterface(sourceCode, "test", interface); } BOOST_AUTO_TEST_CASE(strings_and_arrays) @@ -783,7 +727,7 @@ BOOST_AUTO_TEST_CASE(strings_and_arrays) // bug #1801 char const* sourceCode = R"( contract test { - function f(string a, bytes b, uint[] c) external {} + function f(string calldata a, bytes calldata b, uint[] calldata c) external {} } )"; @@ -804,7 +748,7 @@ BOOST_AUTO_TEST_CASE(strings_and_arrays) } ] )"; - checkInterface(sourceCode, interface); + checkInterface(sourceCode, "test", interface); } BOOST_AUTO_TEST_CASE(library_function) @@ -812,7 +756,7 @@ BOOST_AUTO_TEST_CASE(library_function) char const* sourceCode = R"( library test { struct StructType { uint a; } - function f(StructType storage b, uint[] storage c, test d) returns (uint[] e, StructType storage f) {} + function f(StructType storage b, uint[] storage c, test d) public returns (uint[] memory e, StructType storage f) { f = f; } } )"; @@ -836,14 +780,14 @@ BOOST_AUTO_TEST_CASE(library_function) } ] )"; - checkInterface(sourceCode, interface); + checkInterface(sourceCode, "test", interface); } BOOST_AUTO_TEST_CASE(include_fallback_function) { char const* sourceCode = R"( contract test { - function() {} + function() external {} } )"; @@ -856,15 +800,15 @@ BOOST_AUTO_TEST_CASE(include_fallback_function) } ] )"; - checkInterface(sourceCode, interface); + checkInterface(sourceCode, "test", interface); } BOOST_AUTO_TEST_CASE(payable_function) { char const* sourceCode = R"( contract test { - function f() {} - function g() payable {} + function f() public {} + function g() public payable {} } )"; @@ -890,14 +834,14 @@ BOOST_AUTO_TEST_CASE(payable_function) } ] )"; - checkInterface(sourceCode, interface); + checkInterface(sourceCode, "test", interface); } BOOST_AUTO_TEST_CASE(payable_fallback_function) { char const* sourceCode = R"( contract test { - function () payable {} + function () external payable {} } )"; @@ -910,14 +854,14 @@ BOOST_AUTO_TEST_CASE(payable_fallback_function) } ] )"; - checkInterface(sourceCode, interface); + checkInterface(sourceCode, "test", interface); } BOOST_AUTO_TEST_CASE(function_type) { char const* sourceCode = R"( contract test { - function g(function(uint) external returns (uint) x) {} + function g(function(uint) external returns (uint) x) public {} } )"; @@ -937,17 +881,17 @@ BOOST_AUTO_TEST_CASE(function_type) } ] )"; - checkInterface(sourceCode, interface); + checkInterface(sourceCode, "test", interface); } BOOST_AUTO_TEST_CASE(return_structs) { - char const* text = R"( + char const* sourceCode = R"( pragma experimental ABIEncoderV2; contract C { struct S { uint a; T[] sub; } struct T { uint[2] x; } - function f() returns (uint x, S s) { + function f() public returns (uint x, S memory s) { } } )"; @@ -987,16 +931,16 @@ BOOST_AUTO_TEST_CASE(return_structs) "type" : "function" }] )"; - checkInterface(text, interface); + checkInterface(sourceCode, "C", interface); } BOOST_AUTO_TEST_CASE(return_structs_with_contracts) { - char const* text = R"( + char const* sourceCode = R"( pragma experimental ABIEncoderV2; contract C { struct S { C[] x; C y; } - function f() returns (S s, C c) { + function f() public returns (S memory s, C c) { } } )"; @@ -1030,12 +974,12 @@ BOOST_AUTO_TEST_CASE(return_structs_with_contracts) "type": "function" }] )"; - checkInterface(text, interface); + checkInterface(sourceCode, "C", interface); } BOOST_AUTO_TEST_CASE(event_structs) { - char const* text = R"( + char const* sourceCode = R"( pragma experimental ABIEncoderV2; contract C { struct S { uint a; T[] sub; bytes b; } @@ -1088,18 +1032,18 @@ BOOST_AUTO_TEST_CASE(event_structs) "type": "event" }] )"; - checkInterface(text, interface); + checkInterface(sourceCode, "C", interface); } BOOST_AUTO_TEST_CASE(structs_in_libraries) { - char const* text = R"( + char const* sourceCode = R"( pragma experimental ABIEncoderV2; library L { struct S { uint a; T[] sub; bytes b; } struct T { uint[2] x; } - function f(L.S storage s) {} - function g(L.S s) {} + function f(L.S storage s) public {} + function g(L.S memory s) public {} } )"; char const* interface = R"( @@ -1152,7 +1096,7 @@ BOOST_AUTO_TEST_CASE(structs_in_libraries) "type": "function" }] )"; - checkInterface(text, interface); + checkInterface(sourceCode, "L", interface); } BOOST_AUTO_TEST_SUITE_END() diff --git a/test/libsolidity/SolidityCompiler.cpp b/test/libsolidity/SolidityCompiler.cpp index 90540f3e..0cc80dd8 100644 --- a/test/libsolidity/SolidityCompiler.cpp +++ b/test/libsolidity/SolidityCompiler.cpp @@ -38,7 +38,7 @@ BOOST_AUTO_TEST_CASE(does_not_include_creation_time_only_internal_functions) char const* sourceCode = R"( contract C { uint x; - function C() { f(); } + constructor() public { f(); } function f() internal { for (uint i = 0; i < 10; ++i) x += 3 + i; } } )"; diff --git a/test/libsolidity/SolidityEndToEndTest.cpp b/test/libsolidity/SolidityEndToEndTest.cpp index f2cc78bf..87646737 100644 --- a/test/libsolidity/SolidityEndToEndTest.cpp +++ b/test/libsolidity/SolidityEndToEndTest.cpp @@ -49,11 +49,30 @@ namespace test BOOST_FIXTURE_TEST_SUITE(SolidityEndToEndTest, SolidityExecutionFramework) +BOOST_AUTO_TEST_CASE(transaction_status) +{ + char const* sourceCode = R"( + contract test { + function f() public { } + function g() public { revert(); } + function h() public { assert(false); } + } + )"; + compileAndRun(sourceCode); + callContractFunction("f()"); + BOOST_CHECK(m_transactionSuccessful); + callContractFunction("g()"); + BOOST_CHECK(!m_transactionSuccessful); + callContractFunction("h()"); + BOOST_CHECK(!m_transactionSuccessful); +} + + BOOST_AUTO_TEST_CASE(smoke_test) { char const* sourceCode = R"( contract test { - function f(uint a) returns(uint d) { return a * 7; } + function f(uint a) public returns(uint d) { return a * 7; } } )"; compileAndRun(sourceCode); @@ -73,7 +92,7 @@ BOOST_AUTO_TEST_CASE(exp_operator) { char const* sourceCode = R"( contract test { - function f(uint a) returns(uint d) { return 2 ** a; } + function f(uint a) public returns(uint d) { return 2 ** a; } } )"; compileAndRun(sourceCode); @@ -84,7 +103,7 @@ BOOST_AUTO_TEST_CASE(exp_operator_const) { char const* sourceCode = R"( contract test { - function f() returns(uint d) { return 2 ** 3; } + function f() public returns(uint d) { return 2 ** 3; } } )"; compileAndRun(sourceCode); @@ -95,7 +114,7 @@ BOOST_AUTO_TEST_CASE(exp_operator_const_signed) { char const* sourceCode = R"( contract test { - function f() returns(int d) { return (-2) ** 3; } + function f() public returns(int d) { return (-2) ** 3; } } )"; compileAndRun(sourceCode); @@ -106,7 +125,7 @@ BOOST_AUTO_TEST_CASE(exp_zero) { char const* sourceCode = R"( contract test { - function f(uint a) returns(uint d) { return a ** 0; } + function f(uint a) public returns(uint d) { return a ** 0; } } )"; compileAndRun(sourceCode); @@ -117,7 +136,7 @@ BOOST_AUTO_TEST_CASE(exp_zero_literal) { char const* sourceCode = R"( contract test { - function f() returns(uint d) { return 0 ** 0; } + function f() public returns(uint d) { return 0 ** 0; } } )"; compileAndRun(sourceCode); @@ -129,7 +148,7 @@ BOOST_AUTO_TEST_CASE(conditional_expression_true_literal) { char const* sourceCode = R"( contract test { - function f() returns(uint d) { + function f() public returns(uint d) { return true ? 5 : 10; } } @@ -142,7 +161,7 @@ BOOST_AUTO_TEST_CASE(conditional_expression_false_literal) { char const* sourceCode = R"( contract test { - function f() returns(uint d) { + function f() public returns(uint d) { return false ? 5 : 10; } } @@ -155,7 +174,7 @@ BOOST_AUTO_TEST_CASE(conditional_expression_multiple) { char const* sourceCode = R"( contract test { - function f(uint x) returns(uint d) { + function f(uint x) public returns(uint d) { return x > 100 ? x > 1000 ? 1000 : 100 : @@ -174,10 +193,11 @@ BOOST_AUTO_TEST_CASE(conditional_expression_with_return_values) { char const* sourceCode = R"( contract test { - function f(bool cond, uint v) returns (uint a, uint b) { + function f(bool cond, uint v) public returns (uint a, uint b) { cond ? a = v : b = v; } - })"; + } + )"; compileAndRun(sourceCode); ABI_CHECK(callContractFunction("f(bool,uint256)", true, u256(20)), encodeArgs(u256(20), u256(0))); ABI_CHECK(callContractFunction("f(bool,uint256)", false, u256(20)), encodeArgs(u256(0), u256(20))); @@ -188,7 +208,7 @@ BOOST_AUTO_TEST_CASE(conditional_expression_storage_memory_1) char const* sourceCode = R"( contract test { bytes2[2] data1; - function f(bool cond) returns (uint) { + function f(bool cond) public returns (uint) { bytes2[2] memory x; x[0] = "aa"; bytes2[2] memory y; @@ -221,7 +241,7 @@ BOOST_AUTO_TEST_CASE(conditional_expression_storage_memory_2) char const* sourceCode = R"( contract test { bytes2[2] data1; - function f(bool cond) returns (uint) { + function f(bool cond) public returns (uint) { data1[0] = "cc"; bytes2[2] memory x; @@ -254,7 +274,7 @@ BOOST_AUTO_TEST_CASE(conditional_expression_different_types) { char const* sourceCode = R"( contract test { - function f(bool cond) returns (uint) { + function f(bool cond) public returns (uint) { uint8 x = 0xcd; uint16 y = 0xabab; return cond ? x : y; @@ -271,7 +291,7 @@ BOOST_AUTO_TEST_CASE(conditional_expression_string_literal) { char const* sourceCode = R"( contract test { - function f(bool cond) returns (bytes32) { + function f(bool cond) public returns (bytes32) { return cond ? "true" : "false"; } } @@ -286,7 +306,7 @@ BOOST_AUTO_TEST_CASE(conditional_expression_tuples) { char const* sourceCode = R"( contract test { - function f(bool cond) returns (uint, uint) { + function f(bool cond) public returns (uint, uint) { return cond ? (1, 2) : (3, 4); } } @@ -300,11 +320,11 @@ BOOST_AUTO_TEST_CASE(conditional_expression_functions) { char const* sourceCode = R"( contract test { - function x() returns (uint) { return 1; } - function y() returns (uint) { return 2; } + function x() public returns (uint) { return 1; } + function y() public returns (uint) { return 2; } - function f(bool cond) returns (uint) { - var z = cond ? x : y; + function f(bool cond) public returns (uint) { + function () returns (uint) z = cond ? x : y; return z(); } } @@ -314,10 +334,9 @@ BOOST_AUTO_TEST_CASE(conditional_expression_functions) ABI_CHECK(callContractFunction("f(bool)", false), encodeArgs(u256(2))); } -BOOST_AUTO_TEST_CASE(C99_scoping_activation) +BOOST_AUTO_TEST_CASE(c99_scoping_activation) { char const* sourceCode = R"( - pragma experimental "v0.5.0"; contract test { function f() pure public returns (uint) { uint x = 7; @@ -366,7 +385,7 @@ BOOST_AUTO_TEST_CASE(recursive_calls) { char const* sourceCode = R"( contract test { - function f(uint n) returns(uint nfac) { + function f(uint n) public returns(uint nfac) { if (n <= 1) return 1; else return n * f(n - 1); } @@ -388,10 +407,10 @@ BOOST_AUTO_TEST_CASE(multiple_functions) { char const* sourceCode = R"( contract test { - function a() returns(uint n) { return 0; } - function b() returns(uint n) { return 1; } - function c() returns(uint n) { return 2; } - function f() returns(uint n) { return 3; } + function a() public returns(uint n) { return 0; } + function b() public returns(uint n) { return 1; } + function c() public returns(uint n) { return 2; } + function f() public returns(uint n) { return 3; } } )"; compileAndRun(sourceCode); @@ -406,8 +425,8 @@ BOOST_AUTO_TEST_CASE(named_args) { char const* sourceCode = R"( contract test { - function a(uint a, uint b, uint c) returns (uint r) { r = a * 100 + b * 10 + c * 1; } - function b() returns (uint r) { r = a({a: 1, b: 2, c: 3}); } + function a(uint a, uint b, uint c) public returns (uint r) { r = a * 100 + b * 10 + c * 1; } + function b() public returns (uint r) { r = a({a: 1, b: 2, c: 3}); } } )"; compileAndRun(sourceCode); @@ -418,8 +437,8 @@ BOOST_AUTO_TEST_CASE(disorder_named_args) { char const* sourceCode = R"( contract test { - function a(uint a, uint b, uint c) returns (uint r) { r = a * 100 + b * 10 + c * 1; } - function b() returns (uint r) { r = a({c: 3, a: 1, b: 2}); } + function a(uint a, uint b, uint c) public returns (uint r) { r = a * 100 + b * 10 + c * 1; } + function b() public returns (uint r) { r = a({c: 3, a: 1, b: 2}); } } )"; compileAndRun(sourceCode); @@ -430,9 +449,9 @@ BOOST_AUTO_TEST_CASE(while_loop) { char const* sourceCode = R"( contract test { - function f(uint n) returns(uint nfac) { + function f(uint n) public returns(uint nfac) { nfac = 1; - var i = 2; + uint i = 2; while (i <= n) nfac *= i++; } } @@ -457,9 +476,9 @@ BOOST_AUTO_TEST_CASE(do_while_loop) { char const* sourceCode = R"( contract test { - function f(uint n) returns(uint nfac) { + function f(uint n) public returns(uint nfac) { nfac = 1; - var i = 2; + uint i = 2; do { nfac *= i++; } while (i <= n); } } @@ -482,12 +501,123 @@ BOOST_AUTO_TEST_CASE(do_while_loop) testContractAgainstCppOnRange("f(uint256)", do_while_loop_cpp, 0, 5); } +BOOST_AUTO_TEST_CASE(do_while_loop_continue) +{ + char const* sourceCode = R"( + contract test { + function f() public pure returns(uint r) { + uint i = 0; + do + { + if (i > 0) return 0; + i++; + continue; + } while (false); + return 42; + } + } + )"; + compileAndRun(sourceCode); + + ABI_CHECK(callContractFunction("f()"), encodeArgs(42)); +} + +BOOST_AUTO_TEST_CASE(array_multiple_local_vars) +{ + char const* sourceCode = R"( + contract test { + function f(uint256[] calldata seq) external pure returns (uint256) { + uint i = 0; + uint sum = 0; + while (i < seq.length) + { + uint idx = i; + if (idx >= 10) break; + uint x = seq[idx]; + if (x >= 1000) { + uint n = i + 1; + i = n; + continue; + } + else { + uint y = sum + x; + sum = y; + } + if (sum >= 500) return sum; + i++; + } + return sum; + } + } + )"; + compileAndRun(sourceCode); + + ABI_CHECK(callContractFunction("f(uint256[])", 32, 3, u256(1000), u256(1), u256(2)), encodeArgs(3)); + ABI_CHECK(callContractFunction("f(uint256[])", 32, 3, u256(100), u256(500), u256(300)), encodeArgs(600)); + ABI_CHECK(callContractFunction( + "f(uint256[])", 32, 11, + u256(1), u256(2), u256(3), u256(4), u256(5), u256(6), u256(7), u256(8), u256(9), u256(10), u256(111) + ), encodeArgs(55)); +} + + +BOOST_AUTO_TEST_CASE(do_while_loop_multiple_local_vars) +{ + char const* sourceCode = R"( + contract test { + function f(uint x) public pure returns(uint r) { + uint i = 0; + do + { + uint z = x * 2; + if (z < 4) break; + else { + uint k = z + 1; + if (k < 8) { + x++; + continue; + } + } + if (z > 12) return 0; + x++; + i++; + } while (true); + return 42; + } + } + )"; + compileAndRun(sourceCode); + + auto do_while = [](u256 n) -> u256 + { + u256 i = 0; + do + { + u256 z = n * 2; + if (z < 4) break; + else { + u256 k = z + 1; + if (k < 8) { + n++; + continue; + } + } + if (z > 12) return 0; + n++; + i++; + } while (true); + return 42; + }; + + testContractAgainstCppOnRange("f(uint256)", do_while, 0, 12); +} + BOOST_AUTO_TEST_CASE(nested_loops) { // tests that break and continue statements in nested loops jump to the correct place char const* sourceCode = R"( contract test { - function f(uint x) returns(uint y) { + function f(uint x) public returns(uint y) { while (x > 1) { if (x == 10) break; while (x > 5) { @@ -533,13 +663,186 @@ BOOST_AUTO_TEST_CASE(nested_loops) testContractAgainstCppOnRange("f(uint256)", nested_loops_cpp, 0, 12); } +BOOST_AUTO_TEST_CASE(nested_loops_multiple_local_vars) +{ + // tests that break and continue statements in nested loops jump to the correct place + // and free local variables properly + char const* sourceCode = R"( + contract test { + function f(uint x) public returns(uint y) { + while (x > 0) { + uint z = x + 10; + uint k = z + 1; + if (k > 20) { + break; + uint p = 100; + k += p; + } + if (k > 15) { + x--; + continue; + uint t = 1000; + x += t; + } + while (k > 10) { + uint m = k - 1; + if (m == 10) return x; + return k; + uint h = 10000; + z += h; + } + x--; + break; + } + return x; + } + } + )"; + compileAndRun(sourceCode); + + auto nested_loops_cpp = [](u256 n) -> u256 + { + while (n > 0) + { + u256 z = n + 10; + u256 k = z + 1; + if (k > 20) break; + if (k > 15) { + n--; + continue; + } + while (k > 10) + { + u256 m = k - 1; + if (m == 10) return n; + return k; + } + n--; + break; + } + + return n; + }; + + testContractAgainstCppOnRange("f(uint256)", nested_loops_cpp, 0, 12); +} + +BOOST_AUTO_TEST_CASE(for_loop_multiple_local_vars) +{ + char const* sourceCode = R"( + contract test { + function f(uint x) public pure returns(uint r) { + for (uint i = 0; i < 12; i++) + { + uint z = x + 1; + if (z < 4) break; + else { + uint k = z * 2; + if (i + k < 10) { + x++; + continue; + } + } + if (z > 8) return 0; + x++; + } + return 42; + } + } + )"; + compileAndRun(sourceCode); + + auto for_loop = [](u256 n) -> u256 + { + for (u256 i = 0; i < 12; i++) + { + u256 z = n + 1; + if (z < 4) break; + else { + u256 k = z * 2; + if (i + k < 10) { + n++; + continue; + } + } + if (z > 8) return 0; + n++; + } + return 42; + }; + + testContractAgainstCppOnRange("f(uint256)", for_loop, 0, 12); +} + +BOOST_AUTO_TEST_CASE(nested_for_loop_multiple_local_vars) +{ + char const* sourceCode = R"( + contract test { + function f(uint x) public pure returns(uint r) { + for (uint i = 0; i < 5; i++) + { + uint z = x + 1; + if (z < 3) { + break; + uint p = z + 2; + } + for (uint j = 0; j < 5; j++) + { + uint k = z * 2; + if (j + k < 8) { + x++; + continue; + uint t = z * 3; + } + x++; + if (x > 20) { + return 84; + uint h = x + 42; + } + } + if (x > 30) { + return 42; + uint b = 0xcafe; + } + } + return 42; + } + } + )"; + compileAndRun(sourceCode); + + auto for_loop = [](u256 n) -> u256 + { + for (u256 i = 0; i < 5; i++) + { + u256 z = n + 1; + if (z < 3) break; + for (u256 j = 0; j < 5; j++) + { + u256 k = z * 2; + if (j + k < 8) { + n++; + continue; + } + n++; + if (n > 20) return 84; + } + if (n > 30) return 42; + } + return 42; + }; + + testContractAgainstCppOnRange("f(uint256)", for_loop, 0, 12); +} + BOOST_AUTO_TEST_CASE(for_loop) { char const* sourceCode = R"( contract test { - function f(uint n) returns(uint nfac) { + function f(uint n) public returns(uint nfac) { nfac = 1; - for (var i = 2; i <= n; i++) + uint i; + for (i = 2; i <= n; i++) nfac *= i; } } @@ -561,7 +864,7 @@ BOOST_AUTO_TEST_CASE(for_loop_empty) { char const* sourceCode = R"( contract test { - function f() returns(uint ret) { + function f() public returns(uint ret) { ret = 1; for (;;) { ret += 1; @@ -590,7 +893,7 @@ BOOST_AUTO_TEST_CASE(for_loop_simple_init_expr) { char const* sourceCode = R"( contract test { - function f(uint n) returns(uint nfac) { + function f(uint n) public returns(uint nfac) { nfac = 1; uint256 i; for (i = 2; i <= n; i++) @@ -616,7 +919,7 @@ BOOST_AUTO_TEST_CASE(for_loop_break_continue) { char const* sourceCode = R"( contract test { - function f(uint n) returns (uint r) + function f(uint n) public returns (uint r) { uint i = 1; uint k = 0; @@ -662,16 +965,16 @@ BOOST_AUTO_TEST_CASE(calling_other_functions) { char const* sourceCode = R"( contract collatz { - function run(uint x) returns(uint y) { + function run(uint x) public returns(uint y) { while ((y = x) > 1) { if (x % 2 == 0) x = evenStep(x); else x = oddStep(x); } } - function evenStep(uint x) returns(uint y) { + function evenStep(uint x) public returns(uint y) { return x / 2; } - function oddStep(uint x) returns(uint y) { + function oddStep(uint x) public returns(uint y) { return 3 * x + 1; } } @@ -712,8 +1015,8 @@ BOOST_AUTO_TEST_CASE(many_local_variables) { char const* sourceCode = R"( contract test { - function run(uint x1, uint x2, uint x3) returns(uint y) { - var a = 0x1; var b = 0x10; var c = 0x100; + function run(uint x1, uint x2, uint x3) public returns(uint y) { + uint8 a = 0x1; uint8 b = 0x10; uint16 c = 0x100; y = a + b + c + x1 + x2 + x3; y += b + x2; } @@ -735,7 +1038,7 @@ BOOST_AUTO_TEST_CASE(packing_unpacking_types) { char const* sourceCode = R"( contract test { - function run(bool a, uint32 b, uint64 c) returns(uint256 y) { + function run(bool a, uint32 b, uint64 c) public returns(uint256 y) { if (a) y = 1; y = y * 0x100000000 | ~b; y = y * 0x10000000000000000 | ~c; @@ -753,7 +1056,7 @@ BOOST_AUTO_TEST_CASE(packing_signed_types) { char const* sourceCode = R"( contract test { - function run() returns(int8 y) { + function run() public returns(int8 y) { uint8 x = 0xfa; return int8(x); } @@ -770,7 +1073,7 @@ BOOST_AUTO_TEST_CASE(multiple_return_values) { char const* sourceCode = R"( contract test { - function run(bool x1, uint x2) returns(uint y1, bool y2, uint y3) { + function run(bool x1, uint x2) public returns(uint y1, bool y2, uint y3) { y1 = x2; y2 = x1; } } @@ -783,7 +1086,7 @@ BOOST_AUTO_TEST_CASE(short_circuiting) { char const* sourceCode = R"( contract test { - function run(uint x) returns(uint y) { + function run(uint x) public returns(uint y) { x == 0 || ((x = 8) > 0); return x; } @@ -804,7 +1107,7 @@ BOOST_AUTO_TEST_CASE(high_bits_cleaning) { char const* sourceCode = R"( contract test { - function run() returns(uint256 y) { + function run() public returns(uint256 y) { uint32 t = uint32(0xffffffff); uint32 x = t + 10; if (x >= 0xffffffff) return 0; @@ -828,7 +1131,7 @@ BOOST_AUTO_TEST_CASE(sign_extension) { char const* sourceCode = R"( contract test { - function run() returns(uint256 y) { + function run() public returns(uint256 y) { int64 x = -int32(0xff); if (x >= 0xff) return 0; return -uint256(x); @@ -850,7 +1153,7 @@ BOOST_AUTO_TEST_CASE(small_unsigned_types) { char const* sourceCode = R"( contract test { - function run() returns(uint256 y) { + function run() public returns(uint256 y) { uint32 t = uint32(0xffffff); uint32 x = t * 0xffffff; return x / 0x100; @@ -871,7 +1174,7 @@ BOOST_AUTO_TEST_CASE(small_signed_types) { char const* sourceCode = R"( contract test { - function run() returns(int256 y) { + function run() public returns(int256 y) { return -int32(10) * -int64(20); } } @@ -888,10 +1191,10 @@ BOOST_AUTO_TEST_CASE(strings) { char const* sourceCode = R"( contract test { - function fixedBytes() returns(bytes32 ret) { + function fixedBytes() public returns(bytes32 ret) { return "abc\x00\xff__"; } - function pipeThrough(bytes2 small, bool one) returns(bytes16 large, bool oneRet) { + function pipeThrough(bytes2 small, bool one) public returns(bytes16 large, bool oneRet) { oneRet = one; large = small; } @@ -908,7 +1211,7 @@ BOOST_AUTO_TEST_CASE(inc_dec_operators) contract test { uint8 x; uint v; - function f() returns (uint r) { + function f() public returns (uint r) { uint a = 6; r = a; r += (a++) * 0x10; @@ -927,7 +1230,7 @@ BOOST_AUTO_TEST_CASE(bytes_comparison) { char const* sourceCode = R"( contract test { - function f() returns (bool) { + function f() public returns (bool) { bytes2 a = "a"; bytes2 x = "aa"; bytes2 b = "b"; @@ -945,25 +1248,25 @@ BOOST_AUTO_TEST_CASE(state_smoke_test) contract test { uint256 value1; uint256 value2; - function get(uint8 which) returns (uint256 value) { + function get(uint8 which) public returns (uint256 value) { if (which == 0) return value1; else return value2; } - function set(uint8 which, uint256 value) { + function set(uint8 which, uint256 value) public { if (which == 0) value1 = value; else value2 = value; } } )"; compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("get(uint8)", byte(0x00)), encodeArgs(0)); - ABI_CHECK(callContractFunction("get(uint8)", byte(0x01)), encodeArgs(0)); - ABI_CHECK(callContractFunction("set(uint8,uint256)", byte(0x00), 0x1234), encodeArgs()); - ABI_CHECK(callContractFunction("set(uint8,uint256)", byte(0x01), 0x8765), encodeArgs()); - ABI_CHECK(callContractFunction("get(uint8)", byte( 0x00)), encodeArgs(0x1234)); - ABI_CHECK(callContractFunction("get(uint8)", byte(0x01)), encodeArgs(0x8765)); - ABI_CHECK(callContractFunction("set(uint8,uint256)", byte(0x00), 0x3), encodeArgs()); - ABI_CHECK(callContractFunction("get(uint8)", byte(0x00)), encodeArgs(0x3)); + ABI_CHECK(callContractFunction("get(uint8)", uint8_t(0x00)), encodeArgs(0)); + ABI_CHECK(callContractFunction("get(uint8)", uint8_t(0x01)), encodeArgs(0)); + ABI_CHECK(callContractFunction("set(uint8,uint256)", uint8_t(0x00), 0x1234), encodeArgs()); + ABI_CHECK(callContractFunction("set(uint8,uint256)", uint8_t(0x01), 0x8765), encodeArgs()); + ABI_CHECK(callContractFunction("get(uint8)", uint8_t( 0x00)), encodeArgs(0x1234)); + ABI_CHECK(callContractFunction("get(uint8)", uint8_t(0x01)), encodeArgs(0x8765)); + ABI_CHECK(callContractFunction("set(uint8,uint256)", uint8_t(0x00), 0x3), encodeArgs()); + ABI_CHECK(callContractFunction("get(uint8)", uint8_t(0x00)), encodeArgs(0x3)); } BOOST_AUTO_TEST_CASE(compound_assign) @@ -972,7 +1275,7 @@ BOOST_AUTO_TEST_CASE(compound_assign) contract test { uint value1; uint value2; - function f(uint x, uint y) returns (uint w) { + function f(uint x, uint y) public returns (uint w) { uint value3 = y; value1 += x; value3 *= x; @@ -1008,31 +1311,31 @@ BOOST_AUTO_TEST_CASE(simple_mapping) char const* sourceCode = R"( contract test { mapping(uint8 => uint8) table; - function get(uint8 k) returns (uint8 v) { + function get(uint8 k) public returns (uint8 v) { return table[k]; } - function set(uint8 k, uint8 v) { + function set(uint8 k, uint8 v) public { table[k] = v; } } )"; compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("get(uint8)", byte(0)), encodeArgs(byte(0x00))); - ABI_CHECK(callContractFunction("get(uint8)", byte(0x01)), encodeArgs(byte(0x00))); - ABI_CHECK(callContractFunction("get(uint8)", byte(0xa7)), encodeArgs(byte(0x00))); - callContractFunction("set(uint8,uint8)", byte(0x01), byte(0xa1)); - ABI_CHECK(callContractFunction("get(uint8)", byte(0x00)), encodeArgs(byte(0x00))); - ABI_CHECK(callContractFunction("get(uint8)", byte(0x01)), encodeArgs(byte(0xa1))); - ABI_CHECK(callContractFunction("get(uint8)", byte(0xa7)), encodeArgs(byte(0x00))); - callContractFunction("set(uint8,uint8)", byte(0x00), byte(0xef)); - ABI_CHECK(callContractFunction("get(uint8)", byte(0x00)), encodeArgs(byte(0xef))); - ABI_CHECK(callContractFunction("get(uint8)", byte(0x01)), encodeArgs(byte(0xa1))); - ABI_CHECK(callContractFunction("get(uint8)", byte(0xa7)), encodeArgs(byte(0x00))); - callContractFunction("set(uint8,uint8)", byte(0x01), byte(0x05)); - ABI_CHECK(callContractFunction("get(uint8)", byte(0x00)), encodeArgs(byte(0xef))); - ABI_CHECK(callContractFunction("get(uint8)", byte(0x01)), encodeArgs(byte(0x05))); - ABI_CHECK(callContractFunction("get(uint8)", byte(0xa7)), encodeArgs(byte(0x00))); + ABI_CHECK(callContractFunction("get(uint8)", uint8_t(0)), encodeArgs(uint8_t(0x00))); + ABI_CHECK(callContractFunction("get(uint8)", uint8_t(0x01)), encodeArgs(uint8_t(0x00))); + ABI_CHECK(callContractFunction("get(uint8)", uint8_t(0xa7)), encodeArgs(uint8_t(0x00))); + callContractFunction("set(uint8,uint8)", uint8_t(0x01), uint8_t(0xa1)); + ABI_CHECK(callContractFunction("get(uint8)", uint8_t(0x00)), encodeArgs(uint8_t(0x00))); + ABI_CHECK(callContractFunction("get(uint8)", uint8_t(0x01)), encodeArgs(uint8_t(0xa1))); + ABI_CHECK(callContractFunction("get(uint8)", uint8_t(0xa7)), encodeArgs(uint8_t(0x00))); + callContractFunction("set(uint8,uint8)", uint8_t(0x00), uint8_t(0xef)); + ABI_CHECK(callContractFunction("get(uint8)", uint8_t(0x00)), encodeArgs(uint8_t(0xef))); + ABI_CHECK(callContractFunction("get(uint8)", uint8_t(0x01)), encodeArgs(uint8_t(0xa1))); + ABI_CHECK(callContractFunction("get(uint8)", uint8_t(0xa7)), encodeArgs(uint8_t(0x00))); + callContractFunction("set(uint8,uint8)", uint8_t(0x01), uint8_t(0x05)); + ABI_CHECK(callContractFunction("get(uint8)", uint8_t(0x00)), encodeArgs(uint8_t(0xef))); + ABI_CHECK(callContractFunction("get(uint8)", uint8_t(0x01)), encodeArgs(uint8_t(0x05))); + ABI_CHECK(callContractFunction("get(uint8)", uint8_t(0xa7)), encodeArgs(uint8_t(0x00))); } BOOST_AUTO_TEST_CASE(mapping_state) @@ -1042,13 +1345,13 @@ BOOST_AUTO_TEST_CASE(mapping_state) mapping(address => bool) canVote; mapping(address => uint) voteCount; mapping(address => bool) voted; - function getVoteCount(address addr) returns (uint retVoteCount) { + function getVoteCount(address addr) public returns (uint retVoteCount) { return voteCount[addr]; } - function grantVoteRight(address addr) { + function grantVoteRight(address addr) public { canVote[addr] = true; } - function vote(address voter, address vote) returns (bool success) { + function vote(address voter, address vote) public returns (bool success) { if (!canVote[voter] || voted[voter]) return false; voted[voter] = true; voteCount[vote] = voteCount[vote] + 1; @@ -1118,7 +1421,7 @@ BOOST_AUTO_TEST_CASE(mapping_state_inc_dec) contract test { uint value; mapping(uint => uint) table; - function f(uint x) returns (uint y) { + function f(uint x) public returns (uint y) { value = x; if (x > 0) table[++value] = 8; if (x > 1) value--; @@ -1150,7 +1453,7 @@ BOOST_AUTO_TEST_CASE(multi_level_mapping) char const* sourceCode = R"( contract test { mapping(uint => mapping(uint => uint)) table; - function f(uint x, uint y, uint z) returns (uint w) { + function f(uint x, uint y, uint z) public returns (uint w) { if (z == 0) return table[x][y]; else return table[x][y] = z; } @@ -1174,6 +1477,159 @@ BOOST_AUTO_TEST_CASE(multi_level_mapping) testContractAgainstCpp("f(uint256,uint256,uint256)", f, u256(5), u256(4), u256(0)); } +BOOST_AUTO_TEST_CASE(mapping_local_assignment) +{ + char const* sourceCode = R"( + contract test { + mapping(uint8 => uint8) m1; + mapping(uint8 => uint8) m2; + function f() public returns (uint8, uint8, uint8, uint8) { + mapping(uint8 => uint8) storage m = m1; + m[1] = 42; + + m = m2; + m[2] = 21; + + return (m1[1], m1[2], m2[1], m2[2]); + } + } + )"; + compileAndRun(sourceCode); + + ABI_CHECK(callContractFunction("f()"), encodeArgs(uint8_t(42), uint8_t(0), uint8_t(0), uint8_t(21))); +} + +BOOST_AUTO_TEST_CASE(mapping_local_tuple_assignment) +{ + char const* sourceCode = R"( + contract test { + mapping(uint8 => uint8) m1; + mapping(uint8 => uint8) m2; + function f() public returns (uint8, uint8, uint8, uint8) { + mapping(uint8 => uint8) storage m = m1; + m[1] = 42; + + uint8 v; + (m, v) = (m2, 21); + m[2] = v; + + return (m1[1], m1[2], m2[1], m2[2]); + } + } + )"; + compileAndRun(sourceCode); + + ABI_CHECK(callContractFunction("f()"), encodeArgs(uint8_t(42), uint8_t(0), uint8_t(0), uint8_t(21))); +} + +BOOST_AUTO_TEST_CASE(mapping_local_compound_assignment) +{ + char const* sourceCode = R"( + contract test { + mapping(uint8 => uint8) m1; + mapping(uint8 => uint8) m2; + function f() public returns (uint8, uint8, uint8, uint8) { + mapping(uint8 => uint8) storage m = m1; + m[1] = 42; + + (m = m2)[2] = 21; + + return (m1[1], m1[2], m2[1], m2[2]); + } + } + )"; + compileAndRun(sourceCode); + + ABI_CHECK(callContractFunction("f()"), encodeArgs(uint8_t(42), uint8_t(0), uint8_t(0), uint8_t(21))); +} + +BOOST_AUTO_TEST_CASE(mapping_internal_argument) +{ + char const* sourceCode = R"( + contract test { + mapping(uint8 => uint8) a; + mapping(uint8 => uint8) b; + function set_internal(mapping(uint8 => uint8) storage m, uint8 key, uint8 value) internal returns (uint8) { + uint8 oldValue = m[key]; + m[key] = value; + return oldValue; + } + function set(uint8 key, uint8 value_a, uint8 value_b) public returns (uint8 old_a, uint8 old_b) { + old_a = set_internal(a, key, value_a); + old_b = set_internal(b, key, value_b); + } + function get(uint8 key) public returns (uint8, uint8) { + return (a[key], b[key]); + } + } + )"; + compileAndRun(sourceCode); + + ABI_CHECK(callContractFunction("set(uint8,uint8,uint8)", uint8_t(1), uint8_t(21), uint8_t(42)), encodeArgs(uint8_t(0), uint8_t(0))); + ABI_CHECK(callContractFunction("get(uint8)", uint8_t(1)), encodeArgs(uint8_t(21), uint8_t(42))); + ABI_CHECK(callContractFunction("set(uint8,uint8,uint8)", uint8_t(1), uint8_t(10), uint8_t(11)), encodeArgs(uint8_t(21), uint8_t(42))); + ABI_CHECK(callContractFunction("get(uint8)", uint8_t(1)), encodeArgs(uint8_t(10), uint8_t(11))); +} + +BOOST_AUTO_TEST_CASE(mapping_array_internal_argument) +{ + char const* sourceCode = R"( + contract test { + mapping(uint8 => uint8)[2] a; + mapping(uint8 => uint8)[2] b; + function set_internal(mapping(uint8 => uint8)[2] storage m, uint8 key, uint8 value1, uint8 value2) internal returns (uint8, uint8) { + uint8 oldValue1 = m[0][key]; + uint8 oldValue2 = m[1][key]; + m[0][key] = value1; + m[1][key] = value2; + return (oldValue1, oldValue2); + } + function set(uint8 key, uint8 value_a1, uint8 value_a2, uint8 value_b1, uint8 value_b2) public returns (uint8 old_a1, uint8 old_a2, uint8 old_b1, uint8 old_b2) { + (old_a1, old_a2) = set_internal(a, key, value_a1, value_a2); + (old_b1, old_b2) = set_internal(b, key, value_b1, value_b2); + } + function get(uint8 key) public returns (uint8, uint8, uint8, uint8) { + return (a[0][key], a[1][key], b[0][key], b[1][key]); + } + } + )"; + compileAndRun(sourceCode); + + ABI_CHECK(callContractFunction("set(uint8,uint8,uint8,uint8,uint8)", uint8_t(1), uint8_t(21), uint8_t(22), uint8_t(42), uint8_t(43)), encodeArgs(uint8_t(0), uint8_t(0), uint8_t(0), uint8_t(0))); + ABI_CHECK(callContractFunction("get(uint8)", uint8_t(1)), encodeArgs(uint8_t(21), uint8_t(22), uint8_t(42), uint8_t(43))); + ABI_CHECK(callContractFunction("set(uint8,uint8,uint8,uint8,uint8)", uint8_t(1), uint8_t(10), uint8_t(30), uint8_t(11), uint8_t(31)), encodeArgs(uint8_t(21), uint8_t(22), uint8_t(42), uint8_t(43))); + ABI_CHECK(callContractFunction("get(uint8)", uint8_t(1)), encodeArgs(uint8_t(10), uint8_t(30), uint8_t(11), uint8_t(31))); +} + +BOOST_AUTO_TEST_CASE(mapping_internal_return) +{ + char const* sourceCode = R"( + contract test { + mapping(uint8 => uint8) a; + mapping(uint8 => uint8) b; + function f() internal returns (mapping(uint8 => uint8) storage r) { + r = a; + r[1] = 42; + r = b; + r[1] = 84; + } + function g() public returns (uint8, uint8, uint8, uint8, uint8, uint8) { + f()[2] = 21; + return (a[0], a[1], a[2], b[0], b[1], b[2]); + } + function h() public returns (uint8, uint8, uint8, uint8, uint8, uint8) { + mapping(uint8 => uint8) storage m = f(); + m[2] = 17; + return (a[0], a[1], a[2], b[0], b[1], b[2]); + } + } + )"; + compileAndRun(sourceCode); + + ABI_CHECK(callContractFunction("g()"), encodeArgs(uint8_t(0), uint8_t(42), uint8_t(0), uint8_t(0), uint8_t(84), uint8_t (21))); + ABI_CHECK(callContractFunction("h()"), encodeArgs(uint8_t(0), uint8_t(42), uint8_t(0), uint8_t(0), uint8_t(84), uint8_t (17))); +} + BOOST_AUTO_TEST_CASE(structs) { char const* sourceCode = R"( @@ -1188,7 +1644,7 @@ BOOST_AUTO_TEST_CASE(structs) mapping(uint8 => s2) recursive; } s2 data; - function check() returns (bool ok) { + function check() public returns (bool ok) { return data.z == 1 && data.s1data.x == 2 && data.s1data.y == true && data.recursive[3].recursive[4].z == 5 && @@ -1196,7 +1652,7 @@ BOOST_AUTO_TEST_CASE(structs) data.recursive[0].s1data.y == false && data.recursive[4].z == 9; } - function set() { + function set() public { data.z = 1; data.s1data.x = 2; data.s1data.y = true; @@ -1222,16 +1678,16 @@ BOOST_AUTO_TEST_CASE(struct_reference) mapping(uint8 => s2) recursive; } s2 data; - function check() returns (bool ok) { + function check() public returns (bool ok) { return data.z == 2 && data.recursive[0].z == 3 && data.recursive[0].recursive[1].z == 0 && data.recursive[0].recursive[0].z == 1; } - function set() { + function set() public { data.z = 2; - var map = data.recursive; - s2 inner = map[0]; + mapping(uint8 => s2) storage map = data.recursive; + s2 storage inner = map[0]; inner.z = 3; inner.recursive[0].z = inner.recursive[1].z + 1; } @@ -1249,7 +1705,6 @@ BOOST_AUTO_TEST_CASE(deleteStruct) contract test { struct topStruct { nestedStruct nstr; - emptyStruct empty; uint topValue; mapping (uint => uint) topMapping; } @@ -1259,9 +1714,7 @@ BOOST_AUTO_TEST_CASE(deleteStruct) uint nestedValue; mapping (uint => bool) nestedMapping; } - struct emptyStruct{ - } - function test(){ + constructor() public { toDelete = 5; str.topValue = 1; str.topMapping[0] = 1; @@ -1273,19 +1726,19 @@ BOOST_AUTO_TEST_CASE(deleteStruct) delete str; delete toDelete; } - function getToDelete() returns (uint res){ + function getToDelete() public returns (uint res){ res = toDelete; } - function getTopValue() returns(uint topValue){ + function getTopValue() public returns(uint topValue){ topValue = str.topValue; } - function getNestedValue() returns(uint nestedValue){ + function getNestedValue() public returns(uint nestedValue){ nestedValue = str.nstr.nestedValue; } - function getTopMapping(uint index) returns(uint ret) { + function getTopMapping(uint index) public returns(uint ret) { ret = str.topMapping[index]; } - function getNestedMapping(uint index) returns(bool ret) { + function getNestedMapping(uint index) public returns(bool ret) { return str.nstr.nestedMapping[index]; } } @@ -1305,7 +1758,7 @@ BOOST_AUTO_TEST_CASE(deleteLocal) { char const* sourceCode = R"( contract test { - function delLocal() returns (uint res){ + function delLocal() public returns (uint res){ uint v = 5; delete v; res = v; @@ -1320,7 +1773,7 @@ BOOST_AUTO_TEST_CASE(deleteLocals) { char const* sourceCode = R"( contract test { - function delLocal() returns (uint res1, uint res2){ + function delLocal() public returns (uint res1, uint res2){ uint v = 5; uint w = 6; uint x = 7; @@ -1334,21 +1787,39 @@ BOOST_AUTO_TEST_CASE(deleteLocals) ABI_CHECK(callContractFunction("delLocal()"), encodeArgs(6, 7)); } +BOOST_AUTO_TEST_CASE(deleteLength) +{ + char const* sourceCode = R"( + contract test { + uint[] x; + function f() public returns (uint){ + x.length = 1; + x[0] = 1; + delete x.length; + return x.length; + } + } + )"; + compileAndRun(sourceCode); + ABI_CHECK(callContractFunction("f()"), encodeArgs(0)); + BOOST_CHECK(storageEmpty(m_contractAddress)); +} + BOOST_AUTO_TEST_CASE(constructor) { char const* sourceCode = R"( contract test { mapping(uint => uint) data; - function test() { + constructor() public { data[7] = 8; } - function get(uint key) returns (uint value) { + function get(uint key) public returns (uint value) { return data[key]; } } )"; compileAndRun(sourceCode); - map<u256, byte> data; + map<u256, uint8_t> data; data[7] = 8; auto get = [&](u256 const& _x) -> u256 { @@ -1363,7 +1834,7 @@ BOOST_AUTO_TEST_CASE(simple_accessor) char const* sourceCode = R"( contract test { uint256 public data; - function test() { + constructor() public { data = 8; } } @@ -1382,7 +1853,7 @@ BOOST_AUTO_TEST_CASE(array_accessor) struct st { uint a; uint[] finalArray; } mapping(uint256 => mapping(uint256 => st[5])) public multiple_map; - function test() { + constructor() public { data[0] = 8; dynamicData.length = 3; dynamicData[2] = 8; @@ -1412,7 +1883,7 @@ BOOST_AUTO_TEST_CASE(accessors_mapping_for_array) contract test { mapping(uint => uint[8]) public data; mapping(uint => uint[]) public dynamicData; - function test() { + constructor() public { data[2][2] = 8; dynamicData[2].length = 3; dynamicData[2][2] = 8; @@ -1434,10 +1905,10 @@ BOOST_AUTO_TEST_CASE(multiple_elementary_accessors) bytes6 public name; bytes32 public a_hash; address public an_address; - function test() { + constructor() public { data = 8; name = "Celina"; - a_hash = keccak256(123); + a_hash = keccak256("\x7b"); an_address = address(0x1337); super_secret_data = 42; } @@ -1460,7 +1931,7 @@ BOOST_AUTO_TEST_CASE(complex_accessors) mapping(uint256 => bool) public to_bool_map; mapping(uint256 => uint256) public to_uint_map; mapping(uint256 => mapping(uint256 => uint256)) public to_multiple_map; - function test() { + constructor() public { to_string_map[42] = "24"; to_bool_map[42] = false; to_uint_map[42] = 12; @@ -1481,7 +1952,7 @@ BOOST_AUTO_TEST_CASE(struct_accessor) contract test { struct Data { uint a; uint8 b; mapping(uint => uint) c; bool d; } mapping(uint => Data) public data; - function test() { + constructor() public { data[7].a = 1; data[7].b = 2; data[7].c[0] = 3; @@ -1497,8 +1968,8 @@ BOOST_AUTO_TEST_CASE(balance) { char const* sourceCode = R"( contract test { - function test() payable {} - function getBalance() returns (uint256 balance) { + constructor() public payable {} + function getBalance() public returns (uint256 balance) { return address(this).balance; } } @@ -1511,8 +1982,8 @@ BOOST_AUTO_TEST_CASE(blockchain) { char const* sourceCode = R"( contract test { - function test() payable {} - function someInfo() payable returns (uint256 value, address coinbase, uint256 blockNumber) { + constructor() public payable {} + function someInfo() public payable returns (uint256 value, address coinbase, uint256 blockNumber) { value = msg.value; coinbase = block.coinbase; blockNumber = block.number; @@ -1529,7 +2000,7 @@ BOOST_AUTO_TEST_CASE(msg_sig) { char const* sourceCode = R"( contract test { - function foo(uint256 a) returns (bytes4 value) { + function foo(uint256 a) public returns (bytes4 value) { return msg.sig; } } @@ -1542,10 +2013,10 @@ BOOST_AUTO_TEST_CASE(msg_sig_after_internal_call_is_same) { char const* sourceCode = R"( contract test { - function boo() returns (bytes4 value) { + function boo() public returns (bytes4 value) { return msg.sig; } - function foo(uint256 a) returns (bytes4 value) { + function foo(uint256 a) public returns (bytes4 value) { return boo(); } } @@ -1558,7 +2029,7 @@ BOOST_AUTO_TEST_CASE(now) { char const* sourceCode = R"( contract test { - function someInfo() returns (bool equal, uint val) { + function someInfo() public returns (bool equal, uint val) { equal = block.timestamp == now; val = now; } @@ -1581,7 +2052,7 @@ BOOST_AUTO_TEST_CASE(type_conversions_cleanup) // integer should drop the first two bytes char const* sourceCode = R"( contract Test { - function test() returns (uint ret) { return uint(address(Test(address(0x11223344556677889900112233445566778899001122)))); } + function test() public returns (uint ret) { return uint(address(Test(address(0x11223344556677889900112233445566778899001122)))); } } )"; compileAndRun(sourceCode); @@ -1594,7 +2065,7 @@ BOOST_AUTO_TEST_CASE(convert_fixed_bytes_to_fixed_bytes_smaller_size) { char const* sourceCode = R"( contract Test { - function bytesToBytes(bytes4 input) returns (bytes2 ret) { + function bytesToBytes(bytes4 input) public returns (bytes2 ret) { return bytes2(input); } } @@ -1607,7 +2078,7 @@ BOOST_AUTO_TEST_CASE(convert_fixed_bytes_to_fixed_bytes_greater_size) { char const* sourceCode = R"( contract Test { - function bytesToBytes(bytes2 input) returns (bytes4 ret) { + function bytesToBytes(bytes2 input) public returns (bytes4 ret) { return bytes4(input); } } @@ -1620,7 +2091,7 @@ BOOST_AUTO_TEST_CASE(convert_fixed_bytes_to_fixed_bytes_same_size) { char const* sourceCode = R"( contract Test { - function bytesToBytes(bytes4 input) returns (bytes4 ret) { + function bytesToBytes(bytes4 input) public returns (bytes4 ret) { return bytes4(input); } } @@ -1634,7 +2105,7 @@ BOOST_AUTO_TEST_CASE(convert_fixed_bytes_to_uint_same_size) { char const* sourceCode = R"( contract Test { - function bytesToUint(bytes32 s) returns (uint256 h) { + function bytesToUint(bytes32 s) public returns (uint256 h) { return uint(s); } } @@ -1650,7 +2121,7 @@ BOOST_AUTO_TEST_CASE(convert_fixed_bytes_to_uint_same_min_size) { char const* sourceCode = R"( contract Test { - function bytesToUint(bytes1 s) returns (uint8 h) { + function bytesToUint(bytes1 s) public returns (uint8 h) { return uint8(s); } } @@ -1666,8 +2137,8 @@ BOOST_AUTO_TEST_CASE(convert_fixed_bytes_to_uint_smaller_size) { char const* sourceCode = R"( contract Test { - function bytesToUint(bytes4 s) returns (uint16 h) { - return uint16(s); + function bytesToUint(bytes4 s) public returns (uint16 h) { + return uint16(uint32(s)); } } )"; @@ -1682,8 +2153,8 @@ BOOST_AUTO_TEST_CASE(convert_fixed_bytes_to_uint_greater_size) { char const* sourceCode = R"( contract Test { - function bytesToUint(bytes4 s) returns (uint64 h) { - return uint64(s); + function bytesToUint(bytes4 s) public returns (uint64 h) { + return uint64(uint32(s)); } } )"; @@ -1699,7 +2170,7 @@ BOOST_AUTO_TEST_CASE(convert_uint_to_fixed_bytes_same_size) { char const* sourceCode = R"( contract Test { - function uintToBytes(uint256 h) returns (bytes32 s) { + function uintToBytes(uint256 h) public returns (bytes32 s) { return bytes32(h); } } @@ -1713,7 +2184,7 @@ BOOST_AUTO_TEST_CASE(convert_uint_to_fixed_bytes_same_min_size) { char const* sourceCode = R"( contract Test { - function UintToBytes(uint8 h) returns (bytes1 s) { + function UintToBytes(uint8 h) public returns (bytes1 s) { return bytes1(h); } } @@ -1729,8 +2200,8 @@ BOOST_AUTO_TEST_CASE(convert_uint_to_fixed_bytes_smaller_size) { char const* sourceCode = R"( contract Test { - function uintToBytes(uint32 h) returns (bytes2 s) { - return bytes2(h); + function uintToBytes(uint32 h) public returns (bytes2 s) { + return bytes2(uint16(h)); } } )"; @@ -1745,8 +2216,8 @@ BOOST_AUTO_TEST_CASE(convert_uint_to_fixed_bytes_greater_size) { char const* sourceCode = R"( contract Test { - function UintToBytes(uint16 h) returns (bytes8 s) { - return bytes8(h); + function UintToBytes(uint16 h) public returns (bytes8 s) { + return bytes8(uint64(h)); } } )"; @@ -1761,8 +2232,8 @@ BOOST_AUTO_TEST_CASE(send_ether) { char const* sourceCode = R"( contract test { - function test() payable {} - function a(address addr, uint amount) returns (uint ret) { + constructor() payable public {} + function a(address payable addr, uint amount) public returns (uint ret) { addr.send(amount); return address(this).balance; } @@ -1779,12 +2250,12 @@ BOOST_AUTO_TEST_CASE(transfer_ether) { char const* sourceCode = R"( contract A { - function A() payable {} - function a(address addr, uint amount) returns (uint) { + constructor() public payable {} + function a(address payable addr, uint amount) public returns (uint) { addr.transfer(amount); - return this.balance; + return address(this).balance; } - function b(address addr, uint amount) { + function b(address payable addr, uint amount) public { addr.transfer(amount); } } @@ -1793,8 +2264,8 @@ BOOST_AUTO_TEST_CASE(transfer_ether) } contract C { - function () payable { - throw; + function () external payable { + revert(); } } )"; @@ -1817,8 +2288,7 @@ BOOST_AUTO_TEST_CASE(uncalled_blockhash) contract C { function f() public view returns (bytes32) { - var x = block.blockhash; - return x(block.number - 1); + return (blockhash)(block.number - 1); } } )"; @@ -1844,8 +2314,8 @@ BOOST_AUTO_TEST_CASE(log0) { char const* sourceCode = R"( contract test { - function a() { - log0(1); + function a() public { + log0(bytes32(uint256(1))); } } )"; @@ -1861,8 +2331,8 @@ BOOST_AUTO_TEST_CASE(log1) { char const* sourceCode = R"( contract test { - function a() { - log1(1, 2); + function a() public { + log1(bytes32(uint256(1)), bytes32(uint256(2))); } } )"; @@ -1879,8 +2349,8 @@ BOOST_AUTO_TEST_CASE(log2) { char const* sourceCode = R"( contract test { - function a() { - log2(1, 2, 3); + function a() public { + log2(bytes32(uint256(1)), bytes32(uint256(2)), bytes32(uint256(3))); } } )"; @@ -1898,8 +2368,8 @@ BOOST_AUTO_TEST_CASE(log3) { char const* sourceCode = R"( contract test { - function a() { - log3(1, 2, 3, 4); + function a() public { + log3(bytes32(uint256(1)), bytes32(uint256(2)), bytes32(uint256(3)), bytes32(uint256(4))); } } )"; @@ -1917,8 +2387,8 @@ BOOST_AUTO_TEST_CASE(log4) { char const* sourceCode = R"( contract test { - function a() { - log4(1, 2, 3, 4, 5); + function a() public { + log4(bytes32(uint256(1)), bytes32(uint256(2)), bytes32(uint256(3)), bytes32(uint256(4)), bytes32(uint256(5))); } } )"; @@ -1936,8 +2406,8 @@ BOOST_AUTO_TEST_CASE(log_in_constructor) { char const* sourceCode = R"( contract test { - function test() { - log1(1, 2); + constructor() public { + log1(bytes32(uint256(1)), bytes32(uint256(2))); } } )"; @@ -1949,31 +2419,12 @@ BOOST_AUTO_TEST_CASE(log_in_constructor) BOOST_CHECK_EQUAL(m_logs[0].topics[0], h256(u256(2))); } -BOOST_AUTO_TEST_CASE(suicide) -{ - char const* sourceCode = R"( - contract test { - function test() payable {} - function a(address receiver) returns (uint ret) { - suicide(receiver); - return 10; - } - } - )"; - u256 amount(130); - compileAndRun(sourceCode, amount); - u160 address(23); - ABI_CHECK(callContractFunction("a(address)", address), bytes()); - BOOST_CHECK(!addressHasCode(m_contractAddress)); - BOOST_CHECK_EQUAL(balanceAt(address), amount); -} - BOOST_AUTO_TEST_CASE(selfdestruct) { char const* sourceCode = R"( contract test { - function test() payable {} - function a(address receiver) returns (uint ret) { + constructor() public payable {} + function a(address payable receiver) public returns (uint ret) { selfdestruct(receiver); return 10; } @@ -1991,8 +2442,8 @@ BOOST_AUTO_TEST_CASE(keccak256) { char const* sourceCode = R"( contract test { - function a(bytes32 input) returns (bytes32 hash) { - return keccak256(input); + function a(bytes32 input) public returns (bytes32 hash) { + return keccak256(abi.encodePacked(input)); } } )"; @@ -2006,29 +2457,12 @@ BOOST_AUTO_TEST_CASE(keccak256) testContractAgainstCpp("a(bytes32)", f, u256(-1)); } -BOOST_AUTO_TEST_CASE(sha3) -{ - char const* sourceCode = R"( - contract test { - // to confuse the optimiser - function b(bytes32 input) returns (bytes32) { - return sha3(input); - } - function a(bytes32 input) returns (bool) { - return keccak256(input) == b(input); - } - } - )"; - compileAndRun(sourceCode); - BOOST_REQUIRE(callContractFunction("a(bytes32)", u256(42)) == encodeArgs(true)); -} - BOOST_AUTO_TEST_CASE(sha256) { char const* sourceCode = R"( contract test { - function a(bytes32 input) returns (bytes32 sha256hash) { - return sha256(input); + function a(bytes32 input) public returns (bytes32 sha256hash) { + return sha256(abi.encodePacked(input)); } } )"; @@ -2052,8 +2486,8 @@ BOOST_AUTO_TEST_CASE(ripemd) { char const* sourceCode = R"( contract test { - function a(bytes32 input) returns (bytes32 sha256hash) { - return ripemd160(input); + function a(bytes32 input) public returns (bytes32 sha256hash) { + return ripemd160(abi.encodePacked(input)); } } )"; @@ -2077,10 +2511,10 @@ BOOST_AUTO_TEST_CASE(packed_keccak256) { char const* sourceCode = R"( contract test { - function a(bytes32 input) returns (bytes32 hash) { - var b = 65536; + function a(bytes32 input) public returns (bytes32 hash) { + uint24 b = 65536; uint c = 256; - return keccak256(8, input, b, input, c); + return keccak256(abi.encodePacked(uint8(8), input, b, input, c)); } } )"; @@ -2105,14 +2539,14 @@ BOOST_AUTO_TEST_CASE(packed_keccak256_complex_types) char const* sourceCode = R"( contract test { uint120[3] x; - function f() returns (bytes32 hash1, bytes32 hash2, bytes32 hash3) { + function f() public returns (bytes32 hash1, bytes32 hash2, bytes32 hash3) { uint120[] memory y = new uint120[](3); x[0] = y[0] = uint120(-2); x[1] = y[1] = uint120(-3); x[2] = y[2] = uint120(-4); - hash1 = keccak256(x); - hash2 = keccak256(y); - hash3 = keccak256(this.f); + hash1 = keccak256(abi.encodePacked(x)); + hash2 = keccak256(abi.encodePacked(y)); + hash3 = keccak256(abi.encodePacked(this.f)); } } )"; @@ -2129,10 +2563,10 @@ BOOST_AUTO_TEST_CASE(packed_sha256) { char const* sourceCode = R"( contract test { - function a(bytes32 input) returns (bytes32 hash) { - var b = 65536; + function a(bytes32 input) public returns (bytes32 hash) { + uint24 b = 65536; uint c = 256; - return sha256(8, input, b, input, c); + return sha256(abi.encodePacked(uint8(8), input, b, input, c)); } } )"; @@ -2156,10 +2590,10 @@ BOOST_AUTO_TEST_CASE(packed_ripemd160) { char const* sourceCode = R"( contract test { - function a(bytes32 input) returns (bytes32 hash) { - var b = 65536; + function a(bytes32 input) public returns (bytes32 hash) { + uint24 b = 65536; uint c = 256; - return ripemd160(8, input, b, input, c); + return ripemd160(abi.encodePacked(uint8(8), input, b, input, c)); } } )"; @@ -2183,14 +2617,14 @@ BOOST_AUTO_TEST_CASE(ecrecover) { char const* sourceCode = R"( contract test { - function a(bytes32 h, uint8 v, bytes32 r, bytes32 s) returns (address addr) { + function a(bytes32 h, uint8 v, bytes32 r, bytes32 s) public returns (address addr) { return ecrecover(h, v, r, s); } } )"; compileAndRun(sourceCode); u256 h("0x18c547e4f7b0f325ad1e56f57e26c745b09a3e503d86e00e5255ff7f715d3d1c"); - byte v = 28; + uint8_t v = 28; u256 r("0x73b1693892219d736caba55bdb67216e485557ea6b6af75f37096c9aa6a5a75f"); u256 s("0xeeb940b1d03b21e36b0e47e79769f095fe2ab855bd91e3a38756b7d75a9c4549"); u160 addr("0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b"); @@ -2201,22 +2635,23 @@ BOOST_AUTO_TEST_CASE(inter_contract_calls) { char const* sourceCode = R"( contract Helper { - function multiply(uint a, uint b) returns (uint c) { + function multiply(uint a, uint b) public returns (uint c) { return a * b; } } contract Main { Helper h; - function callHelper(uint a, uint b) returns (uint c) { + function callHelper(uint a, uint b) public returns (uint c) { return h.multiply(a, b); } - function getHelper() returns (address haddress) { + function getHelper() public returns (address haddress) { return address(h); } - function setHelper(address haddress) { + function setHelper(address haddress) public { h = Helper(haddress); } - })"; + } + )"; compileAndRun(sourceCode, 0, "Helper"); u160 const c_helperAddress = m_contractAddress; compileAndRun(sourceCode, 0, "Main"); @@ -2231,22 +2666,23 @@ BOOST_AUTO_TEST_CASE(inter_contract_calls_with_complex_parameters) { char const* sourceCode = R"( contract Helper { - function sel(uint a, bool select, uint b) returns (uint c) { + function sel(uint a, bool select, uint b) public returns (uint c) { if (select) return a; else return b; } } contract Main { Helper h; - function callHelper(uint a, bool select, uint b) returns (uint c) { + function callHelper(uint a, bool select, uint b) public returns (uint c) { return h.sel(a, select, b) * 3; } - function getHelper() returns (address haddress) { + function getHelper() public returns (address haddress) { return address(h); } - function setHelper(address haddress) { + function setHelper(address haddress) public { h = Helper(haddress); } - })"; + } + )"; compileAndRun(sourceCode, 0, "Helper"); u160 const c_helperAddress = m_contractAddress; compileAndRun(sourceCode, 0, "Main"); @@ -2262,22 +2698,23 @@ BOOST_AUTO_TEST_CASE(inter_contract_calls_accessing_this) { char const* sourceCode = R"( contract Helper { - function getAddress() returns (address addr) { + function getAddress() public returns (address addr) { return address(this); } } contract Main { Helper h; - function callHelper() returns (address addr) { + function callHelper() public returns (address addr) { return h.getAddress(); } - function getHelper() returns (address addr) { + function getHelper() public returns (address addr) { return address(h); } - function setHelper(address addr) { + function setHelper(address addr) public { h = Helper(addr); } - })"; + } + )"; compileAndRun(sourceCode, 0, "Helper"); u160 const c_helperAddress = m_contractAddress; compileAndRun(sourceCode, 0, "Main"); @@ -2290,25 +2727,26 @@ BOOST_AUTO_TEST_CASE(calls_to_this) { char const* sourceCode = R"( contract Helper { - function invoke(uint a, uint b) returns (uint c) { + function invoke(uint a, uint b) public returns (uint c) { return this.multiply(a, b, 10); } - function multiply(uint a, uint b, uint8 c) returns (uint ret) { + function multiply(uint a, uint b, uint8 c) public returns (uint ret) { return a * b + c; } } contract Main { Helper h; - function callHelper(uint a, uint b) returns (uint ret) { + function callHelper(uint a, uint b) public returns (uint ret) { return h.invoke(a, b); } - function getHelper() returns (address addr) { + function getHelper() public returns (address addr) { return address(h); } - function setHelper(address addr) { + function setHelper(address addr) public { h = Helper(addr); } - })"; + } + )"; compileAndRun(sourceCode, 0, "Helper"); u160 const c_helperAddress = m_contractAddress; compileAndRun(sourceCode, 0, "Main"); @@ -2325,25 +2763,25 @@ BOOST_AUTO_TEST_CASE(inter_contract_calls_with_local_vars) // so this tests correct stack slot allocation char const* sourceCode = R"( contract Helper { - function multiply(uint a, uint b) returns (uint c) { + function multiply(uint a, uint b) public returns (uint c) { return a * b; } } contract Main { Helper h; - function callHelper(uint a, uint b) returns (uint c) { - var fu = h.multiply; - var y = 9; - var ret = fu(a, b); + function callHelper(uint a, uint b) public returns (uint c) { + uint8 y = 9; + uint256 ret = h.multiply(a, b); return ret + y; } - function getHelper() returns (address haddress) { + function getHelper() public returns (address haddress) { return address(h); } - function setHelper(address haddress) { + function setHelper(address haddress) public { h = Helper(haddress); } - })"; + } + )"; compileAndRun(sourceCode, 0, "Helper"); u160 const c_helperAddress = m_contractAddress; compileAndRun(sourceCode, 0, "Main"); @@ -2358,22 +2796,23 @@ BOOST_AUTO_TEST_CASE(fixed_bytes_in_calls) { char const* sourceCode = R"( contract Helper { - function invoke(bytes3 x, bool stop) returns (bytes4 ret) { + function invoke(bytes3 x, bool stop) public returns (bytes4 ret) { return x; } } contract Main { Helper h; - function callHelper(bytes2 x, bool stop) returns (bytes5 ret) { + function callHelper(bytes2 x, bool stop) public returns (bytes5 ret) { return h.invoke(x, stop); } - function getHelper() returns (address addr) { + function getHelper() public returns (address addr) { return address(h); } - function setHelper(address addr) { + function setHelper(address addr) public { h = Helper(addr); } - })"; + } + )"; compileAndRun(sourceCode, 0, "Helper"); u160 const c_helperAddress = m_contractAddress; compileAndRun(sourceCode, 0, "Main"); @@ -2389,21 +2828,22 @@ BOOST_AUTO_TEST_CASE(constructor_arguments_internal) bytes3 name; bool flag; - function Helper(bytes3 x, bool f) { + constructor(bytes3 x, bool f) public { name = x; flag = f; } - function getName() returns (bytes3 ret) { return name; } - function getFlag() returns (bool ret) { return flag; } + function getName() public returns (bytes3 ret) { return name; } + function getFlag() public returns (bool ret) { return flag; } } contract Main { Helper h; - function Main() { + constructor() public { h = new Helper("abc", true); } - function getFlag() returns (bool ret) { return h.getFlag(); } - function getName() returns (bytes3 ret) { return h.getName(); } - })"; + function getFlag() public returns (bool ret) { return h.getFlag(); } + function getName() public returns (bytes3 ret) { return h.getName(); } + } + )"; compileAndRun(sourceCode, 0, "Main"); ABI_CHECK(callContractFunction("getFlag()"), encodeArgs(true)); ABI_CHECK(callContractFunction("getName()"), encodeArgs("abc")); @@ -2416,12 +2856,12 @@ BOOST_AUTO_TEST_CASE(constructor_arguments_external) bytes3 name; bool flag; - function Main(bytes3 x, bool f) { + constructor(bytes3 x, bool f) public { name = x; flag = f; } - function getName() returns (bytes3 ret) { return name; } - function getFlag() returns (bool ret) { return flag; } + function getName() public returns (bytes3 ret) { return name; } + function getFlag() public returns (bool ret) { return flag; } } )"; compileAndRun(sourceCode, 0, "Main", encodeArgs("abc", true)); @@ -2436,7 +2876,7 @@ BOOST_AUTO_TEST_CASE(constructor_with_long_arguments) string public a; string public b; - function Main(string _a, string _b) { + constructor(string memory _a, string memory _b) public { a = _a; b = _b; } @@ -2464,7 +2904,7 @@ BOOST_AUTO_TEST_CASE(constructor_static_array_argument) uint public a; uint[3] public b; - function C(uint _a, uint[3] _b) { + constructor(uint _a, uint[3] memory _b) public { a = _a; b = _b; } @@ -2484,7 +2924,7 @@ BOOST_AUTO_TEST_CASE(constant_var_as_array_length) uint constant LEN = 3; uint[LEN] public a; - function C(uint[LEN] _a) { + constructor(uint[LEN] memory _a) public { a = _a; } } @@ -2501,12 +2941,13 @@ BOOST_AUTO_TEST_CASE(functions_called_by_constructor) contract Test { bytes3 name; bool flag; - function Test() { + constructor() public { setName("abc"); } - function getName() returns (bytes3 ret) { return name; } + function getName() public returns (bytes3 ret) { return name; } function setName(bytes3 _name) private { name = _name; } - })"; + } + )"; compileAndRun(sourceCode); BOOST_REQUIRE(callContractFunction("getName()") == encodeArgs("abc")); } @@ -2515,14 +2956,14 @@ BOOST_AUTO_TEST_CASE(contracts_as_addresses) { char const* sourceCode = R"( contract helper { - function() payable { } // can receive ether + function() external payable { } // can receive ether } contract test { helper h; - function test() payable { h = new helper(); h.send(5); } - function getBalance() returns (uint256 myBalance, uint256 helperBalance) { - myBalance = this.balance; - helperBalance = h.balance; + constructor() public payable { h = new helper(); address(h).send(5); } + function getBalance() public returns (uint256 myBalance, uint256 helperBalance) { + myBalance = address(this).balance; + helperBalance = address(h).balance; } } )"; @@ -2536,25 +2977,25 @@ BOOST_AUTO_TEST_CASE(gas_and_value_basic) char const* sourceCode = R"( contract helper { bool flag; - function getBalance() payable returns (uint256 myBalance) { - return this.balance; + function getBalance() payable public returns (uint256 myBalance) { + return address(this).balance; } - function setFlag() { flag = true; } - function getFlag() returns (bool fl) { return flag; } + function setFlag() public { flag = true; } + function getFlag() public returns (bool fl) { return flag; } } contract test { helper h; - function test() payable { h = new helper(); } - function sendAmount(uint amount) payable returns (uint256 bal) { + constructor() public payable { h = new helper(); } + function sendAmount(uint amount) public payable returns (uint256 bal) { return h.getBalance.value(amount)(); } - function outOfGas() returns (bool ret) { + function outOfGas() public returns (bool ret) { h.setFlag.gas(2)(); // should fail due to OOG return true; } - function checkState() returns (bool flagAfter, uint myBal) { + function checkState() public returns (bool flagAfter, uint myBal) { flagAfter = h.getFlag(); - myBal = this.balance; + myBal = address(this).balance; } } )"; @@ -2565,22 +3006,104 @@ BOOST_AUTO_TEST_CASE(gas_and_value_basic) BOOST_REQUIRE(callContractFunction("checkState()") == encodeArgs(false, 20 - 5)); } +BOOST_AUTO_TEST_CASE(gasleft_decrease) +{ + char const* sourceCode = R"( + contract C { + uint v; + function f() public returns (bool) { + uint startGas = gasleft(); + v++; + assert(startGas > gasleft()); + return true; + } + function g() public returns (bool) { + uint startGas = gasleft(); + assert(startGas > gasleft()); + return true; + } + } + )"; + compileAndRun(sourceCode); + ABI_CHECK(callContractFunction("f()"), encodeArgs(true)); + ABI_CHECK(callContractFunction("g()"), encodeArgs(true)); +} + +BOOST_AUTO_TEST_CASE(gaslimit) +{ + char const* sourceCode = R"( + contract C { + function f() public returns (uint) { + return block.gaslimit; + } + } + )"; + compileAndRun(sourceCode); + ABI_CHECK(callContractFunction("f()"), encodeArgs(gasLimit())); +} + +BOOST_AUTO_TEST_CASE(gasprice) +{ + char const* sourceCode = R"( + contract C { + function f() public returns (uint) { + return tx.gasprice; + } + } + )"; + compileAndRun(sourceCode); + ABI_CHECK(callContractFunction("f()"), encodeArgs(gasPrice())); +} + +BOOST_AUTO_TEST_CASE(blockhash) +{ + // depending on the aleth version, this test only works for pre-constantinople + if (Options::get().evmVersion() < EVMVersion::constantinople()) + { + char const* sourceCode = R"( + contract C { + uint256 counter; + function g() public returns (bool) { counter++; return true; } + function f() public returns (bytes32[] memory r) { + r = new bytes32[](259); + for (uint i = 0; i < 259; i++) + r[i] = blockhash(block.number - 257 + i); + } + } + )"; + compileAndRun(sourceCode); + // generate a sufficient amount of blocks + while (blockNumber() < u256(255)) + ABI_CHECK(callContractFunction("g()"), encodeArgs(true)); + + vector<u256> hashes; + // ``blockhash()`` is only valid for the last 256 blocks, otherwise zero + hashes.emplace_back(0); + for (u256 i = blockNumber() - u256(255); i <= blockNumber(); i++) + hashes.emplace_back(blockHash(i)); + // the current block hash is not yet known at execution time and therefore zero + hashes.emplace_back(0); + // future block hashes are zero + hashes.emplace_back(0); + + ABI_CHECK(callContractFunction("f()"), encodeDyn(hashes)); + } +} + BOOST_AUTO_TEST_CASE(value_complex) { char const* sourceCode = R"( contract helper { - function getBalance() payable returns (uint256 myBalance) { - return this.balance; + function getBalance() payable public returns (uint256 myBalance) { + return address(this).balance; } } contract test { helper h; - function test() payable { h = new helper(); } - function sendAmount(uint amount) payable returns (uint256 bal) { - var x1 = h.getBalance.value(amount); + constructor() public payable { h = new helper(); } + function sendAmount(uint amount) public payable returns (uint256 bal) { uint someStackElement = 20; - var x2 = x1.gas(1000); - return x2.value(amount + 3)();// overwrite value + return h.getBalance.value(amount).gas(1000).value(amount + 3)(); } } )"; @@ -2592,18 +3115,15 @@ BOOST_AUTO_TEST_CASE(value_insane) { char const* sourceCode = R"( contract helper { - function getBalance() payable returns (uint256 myBalance) { - return this.balance; + function getBalance() payable public returns (uint256 myBalance) { + return address(this).balance; } } contract test { helper h; - function test() payable { h = new helper(); } - function sendAmount(uint amount) returns (uint256 bal) { - var x1 = h.getBalance.value; - var x2 = x1(amount).gas; - var x3 = x2(1000).value; - return x3(amount + 3)();// overwrite value + constructor() public payable { h = new helper(); } + function sendAmount(uint amount) public returns (uint256 bal) { + return h.getBalance.value(amount).gas(1000).value(amount + 3)();// overwrite value } } )"; @@ -2617,22 +3137,23 @@ BOOST_AUTO_TEST_CASE(value_for_constructor) contract Helper { bytes3 name; bool flag; - function Helper(bytes3 x, bool f) payable { + constructor(bytes3 x, bool f) public payable { name = x; flag = f; } - function getName() returns (bytes3 ret) { return name; } - function getFlag() returns (bool ret) { return flag; } + function getName() public returns (bytes3 ret) { return name; } + function getFlag() public returns (bool ret) { return flag; } } contract Main { Helper h; - function Main() payable { + constructor() public payable { h = (new Helper).value(10)("abc", true); } - function getFlag() returns (bool ret) { return h.getFlag(); } - function getName() returns (bytes3 ret) { return h.getName(); } - function getBalances() returns (uint me, uint them) { me = this.balance; them = h.balance;} - })"; + function getFlag() public returns (bool ret) { return h.getFlag(); } + function getName() public returns (bytes3 ret) { return h.getName(); } + function getBalances() public returns (uint me, uint them) { me = address(this).balance; them = address(h).balance;} + } + )"; compileAndRun(sourceCode, 22, "Main"); BOOST_REQUIRE(callContractFunction("getFlag()") == encodeArgs(true)); BOOST_REQUIRE(callContractFunction("getName()") == encodeArgs("abc")); @@ -2643,11 +3164,11 @@ BOOST_AUTO_TEST_CASE(virtual_function_calls) { char const* sourceCode = R"( contract Base { - function f() returns (uint i) { return g(); } - function g() returns (uint i) { return 1; } + function f() public returns (uint i) { return g(); } + function g() public returns (uint i) { return 1; } } contract Derived is Base { - function g() returns (uint i) { return 2; } + function g() public returns (uint i) { return 2; } } )"; compileAndRun(sourceCode, 0, "Derived"); @@ -2660,16 +3181,16 @@ BOOST_AUTO_TEST_CASE(access_base_storage) char const* sourceCode = R"( contract Base { uint dataBase; - function getViaBase() returns (uint i) { return dataBase; } + function getViaBase() public returns (uint i) { return dataBase; } } contract Derived is Base { uint dataDerived; - function setData(uint base, uint derived) returns (bool r) { + function setData(uint base, uint derived) public returns (bool r) { dataBase = base; dataDerived = derived; return true; } - function getViaDerived() returns (uint base, uint derived) { + function getViaDerived() public returns (uint base, uint derived) { base = dataBase; derived = dataDerived; } @@ -2686,11 +3207,11 @@ BOOST_AUTO_TEST_CASE(single_copy_with_multiple_inheritance) char const* sourceCode = R"( contract Base { uint data; - function setData(uint i) { data = i; } - function getViaBase() returns (uint i) { return data; } + function setData(uint i) public { data = i; } + function getViaBase() public returns (uint i) { return data; } } - contract A is Base { function setViaA(uint i) { setData(i); } } - contract B is Base { function getViaB() returns (uint i) { return getViaBase(); } } + contract A is Base { function setViaA(uint i) public { setData(i); } } + contract B is Base { function getViaB() public returns (uint i) { return getViaBase(); } } contract Derived is Base, B, A { } )"; compileAndRun(sourceCode, 0, "Derived"); @@ -2702,11 +3223,11 @@ BOOST_AUTO_TEST_CASE(single_copy_with_multiple_inheritance) BOOST_AUTO_TEST_CASE(explicit_base_class) { char const* sourceCode = R"( - contract BaseBase { function g() returns (uint r) { return 1; } } - contract Base is BaseBase { function g() returns (uint r) { return 2; } } + contract BaseBase { function g() public returns (uint r) { return 1; } } + contract Base is BaseBase { function g() public returns (uint r) { return 2; } } contract Derived is Base { - function f() returns (uint r) { return BaseBase.g(); } - function g() returns (uint r) { return 3; } + function f() public returns (uint r) { return BaseBase.g(); } + function g() public returns (uint r) { return 3; } } )"; compileAndRun(sourceCode, 0, "Derived"); @@ -2719,17 +3240,17 @@ BOOST_AUTO_TEST_CASE(base_constructor_arguments) char const* sourceCode = R"( contract BaseBase { uint m_a; - function BaseBase(uint a) { + constructor(uint a) public { m_a = a; } } contract Base is BaseBase(7) { - function Base() { + constructor() public { m_a *= m_a; } } contract Derived is Base() { - function getA() returns (uint r) { return m_a; } + function getA() public returns (uint r) { return m_a; } } )"; compileAndRun(sourceCode, 0, "Derived"); @@ -2741,15 +3262,15 @@ BOOST_AUTO_TEST_CASE(function_usage_in_constructor_arguments) char const* sourceCode = R"( contract BaseBase { uint m_a; - function BaseBase(uint a) { + constructor(uint a) public { m_a = a; } - function g() returns (uint r) { return 2; } + function g() public returns (uint r) { return 2; } } contract Base is BaseBase(BaseBase.g()) { } contract Derived is Base() { - function getA() returns (uint r) { return m_a; } + function getA() public returns (uint r) { return m_a; } } )"; compileAndRun(sourceCode, 0, "Derived"); @@ -2761,46 +3282,28 @@ BOOST_AUTO_TEST_CASE(virtual_function_usage_in_constructor_arguments) char const* sourceCode = R"( contract BaseBase { uint m_a; - function BaseBase(uint a) { + constructor(uint a) public { m_a = a; } - function overridden() returns (uint r) { return 1; } - function g() returns (uint r) { return overridden(); } + function overridden() public returns (uint r) { return 1; } + function g() public returns (uint r) { return overridden(); } } contract Base is BaseBase(BaseBase.g()) { } contract Derived is Base() { - function getA() returns (uint r) { return m_a; } - function overridden() returns (uint r) { return 2; } + function getA() public returns (uint r) { return m_a; } + function overridden() public returns (uint r) { return 2; } } )"; compileAndRun(sourceCode, 0, "Derived"); ABI_CHECK(callContractFunction("getA()"), encodeArgs(2)); } -BOOST_AUTO_TEST_CASE(constructor_argument_overriding) -{ - char const* sourceCode = R"( - contract BaseBase { - uint m_a; - function BaseBase(uint a) { - m_a = a; - } - } - contract Base is BaseBase(2) { } - contract Derived is BaseBase(3), Base { - function getA() returns (uint r) { return m_a; } - } - )"; - compileAndRun(sourceCode, 0, "Derived"); - ABI_CHECK(callContractFunction("getA()"), encodeArgs(3)); -} - BOOST_AUTO_TEST_CASE(internal_constructor) { char const* sourceCode = R"( contract C { - function C() internal {} + constructor() internal {} } )"; BOOST_CHECK(compileAndRunWithoutCheck(sourceCode, 0, "C").empty()); @@ -2810,7 +3313,7 @@ BOOST_AUTO_TEST_CASE(function_modifier) { char const* sourceCode = R"( contract C { - function getOne() payable nonFree returns (uint r) { return 1; } + function getOne() payable nonFree public returns (uint r) { return 1; } modifier nonFree { if (msg.value > 0) _; } } )"; @@ -2823,9 +3326,9 @@ BOOST_AUTO_TEST_CASE(function_modifier_local_variables) { char const* sourceCode = R"( contract C { - modifier mod1 { var a = 1; var b = 2; _; } + modifier mod1 { uint8 a = 1; uint8 b = 2; _; } modifier mod2(bool a) { if (a) return; else _; } - function f(bool a) mod1 mod2(a) returns (uint r) { return 3; } + function f(bool a) mod1 mod2(a) public returns (uint r) { return 3; } } )"; compileAndRun(sourceCode); @@ -2837,8 +3340,8 @@ BOOST_AUTO_TEST_CASE(function_modifier_loop) { char const* sourceCode = R"( contract C { - modifier repeat(uint count) { for (var i = 0; i < count; ++i) _; } - function f() repeat(10) returns (uint r) { r += 1; } + modifier repeat(uint count) { uint i; for (i = 0; i < count; ++i) _; } + function f() repeat(10) public returns (uint r) { r += 1; } } )"; compileAndRun(sourceCode); @@ -2850,7 +3353,7 @@ BOOST_AUTO_TEST_CASE(function_modifier_multi_invocation) char const* sourceCode = R"( contract C { modifier repeat(bool twice) { if (twice) _; _; } - function f(bool twice) repeat(twice) returns (uint r) { r += 1; } + function f(bool twice) repeat(twice) public returns (uint r) { r += 1; } } )"; compileAndRun(sourceCode); @@ -2865,7 +3368,7 @@ BOOST_AUTO_TEST_CASE(function_modifier_multi_with_return) char const* sourceCode = R"( contract C { modifier repeat(bool twice) { if (twice) _; _; } - function f(bool twice) repeat(twice) returns (uint r) { r += 1; return r; } + function f(bool twice) repeat(twice) public returns (uint r) { r += 1; return r; } } )"; compileAndRun(sourceCode); @@ -2877,7 +3380,7 @@ BOOST_AUTO_TEST_CASE(function_modifier_overriding) { char const* sourceCode = R"( contract A { - function f() mod returns (bool r) { return true; } + function f() mod public returns (bool r) { return true; } modifier mod { _; } } contract C is A { @@ -2893,18 +3396,18 @@ BOOST_AUTO_TEST_CASE(function_modifier_calling_functions_in_creation_context) char const* sourceCode = R"( contract A { uint data; - function A() mod1 { f1(); } - function f1() mod2 { data |= 0x1; } - function f2() { data |= 0x20; } - function f3() { } + constructor() mod1 public { f1(); } + function f1() mod2 public { data |= 0x1; } + function f2() public { data |= 0x20; } + function f3() public { } modifier mod1 { f2(); _; } modifier mod2 { f3(); if (false) _; } - function getData() returns (uint r) { return data; } + function getData() public returns (uint r) { return data; } } contract C is A { modifier mod1 { f4(); _; } - function f3() { data |= 0x300; } - function f4() { data |= 0x4000; } + function f3() public { data |= 0x300; } + function f4() public { data |= 0x4000; } } )"; compileAndRun(sourceCode); @@ -2916,9 +3419,9 @@ BOOST_AUTO_TEST_CASE(function_modifier_for_constructor) char const* sourceCode = R"( contract A { uint data; - function A() mod1 { data |= 2; } + constructor() mod1 public { data |= 2; } modifier mod1 { data |= 1; _; } - function getData() returns (uint r) { return data; } + function getData() public returns (uint r) { return data; } } contract C is A { modifier mod1 { data |= 4; _; } @@ -2934,7 +3437,7 @@ BOOST_AUTO_TEST_CASE(function_modifier_multiple_times) contract C { uint public a; modifier mod(uint x) { a += x; _; } - function f(uint x) mod(2) mod(5) mod(x) returns(uint) { return a; } + function f(uint x) mod(2) mod(5) mod(x) public returns(uint) { return a; } } )"; compileAndRun(sourceCode); @@ -2948,7 +3451,7 @@ BOOST_AUTO_TEST_CASE(function_modifier_multiple_times_local_vars) contract C { uint public a; modifier mod(uint x) { uint b = x; a += b; _; a -= b; assert(b == x); } - function f(uint x) mod(2) mod(5) mod(x) returns(uint) { return a; } + function f(uint x) mod(2) mod(5) mod(x) public returns(uint) { return a; } } )"; compileAndRun(sourceCode); @@ -3012,11 +3515,10 @@ BOOST_AUTO_TEST_CASE(crazy_elementary_typenames_on_stack) { char const* sourceCode = R"( contract C { - function f() returns (uint r) { + function f() public returns (uint r) { uint; uint; uint; uint; int x = -7; - var a = uint; - return a(x); + return uint(x); } } )"; @@ -3027,10 +3529,10 @@ BOOST_AUTO_TEST_CASE(crazy_elementary_typenames_on_stack) BOOST_AUTO_TEST_CASE(super) { char const* sourceCode = R"( - contract A { function f() returns (uint r) { return 1; } } - contract B is A { function f() returns (uint r) { return super.f() | 2; } } - contract C is A { function f() returns (uint r) { return super.f() | 4; } } - contract D is B, C { function f() returns (uint r) { return super.f() | 8; } } + contract A { function f() public returns (uint r) { return 1; } } + contract B is A { function f() public returns (uint r) { return super.f() | 2; } } + contract C is A { function f() public returns (uint r) { return super.f() | 4; } } + contract D is B, C { function f() public returns (uint r) { return super.f() | 8; } } )"; compileAndRun(sourceCode, 0, "D"); ABI_CHECK(callContractFunction("f()"), encodeArgs(1 | 2 | 4 | 8)); @@ -3039,10 +3541,10 @@ BOOST_AUTO_TEST_CASE(super) BOOST_AUTO_TEST_CASE(super_in_constructor) { char const* sourceCode = R"( - contract A { function f() returns (uint r) { return 1; } } - contract B is A { function f() returns (uint r) { return super.f() | 2; } } - contract C is A { function f() returns (uint r) { return super.f() | 4; } } - contract D is B, C { uint data; function D() { data = super.f() | 8; } function f() returns (uint r) { return data; } } + contract A { function f() public returns (uint r) { return 1; } } + contract B is A { function f() public returns (uint r) { return super.f() | 2; } } + contract C is A { function f() public returns (uint r) { return super.f() | 4; } } + contract D is B, C { uint data; constructor() public { data = super.f() | 8; } function f() public returns (uint r) { return data; } } )"; compileAndRun(sourceCode, 0, "D"); ABI_CHECK(callContractFunction("f()"), encodeArgs(1 | 2 | 4 | 8)); @@ -3051,7 +3553,7 @@ BOOST_AUTO_TEST_CASE(super_in_constructor) BOOST_AUTO_TEST_CASE(super_alone) { char const* sourceCode = R"( - contract A { function f() { super; } } + contract A { function f() public { super; } } )"; compileAndRun(sourceCode, 0, "A"); ABI_CHECK(callContractFunction("f()"), encodeArgs()); @@ -3062,8 +3564,8 @@ BOOST_AUTO_TEST_CASE(fallback_function) char const* sourceCode = R"( contract A { uint data; - function() { data = 1; } - function getData() returns (uint r) { return data; } + function() external { data = 1; } + function getData() public returns (uint r) { return data; } } )"; compileAndRun(sourceCode); @@ -3077,8 +3579,8 @@ BOOST_AUTO_TEST_CASE(inherited_fallback_function) char const* sourceCode = R"( contract A { uint data; - function() { data = 1; } - function getData() returns (uint r) { return data; } + function() external { data = 1; } + function getData() public returns (uint r) { return data; } } contract B is A {} )"; @@ -3090,15 +3592,31 @@ BOOST_AUTO_TEST_CASE(inherited_fallback_function) BOOST_AUTO_TEST_CASE(default_fallback_throws) { - char const* sourceCode = R"( + char const* sourceCode = R"YY( contract A { - function f() returns (bool) { - return this.call(); + function f() public returns (bool) { + (bool success,) = address(this).call(""); + return success; } } - )"; + )YY"; compileAndRun(sourceCode); ABI_CHECK(callContractFunction("f()"), encodeArgs(0)); + + if (dev::test::Options::get().evmVersion().hasStaticCall()) + { + char const* sourceCode = R"YY( + contract A { + function f() public returns (bool) { + (bool success, bytes memory data) = address(this).staticcall(""); + assert(data.length == 0); + return success; + } + } + )YY"; + compileAndRun(sourceCode); + ABI_CHECK(callContractFunction("f()"), encodeArgs(0)); + } } BOOST_AUTO_TEST_CASE(short_data_calls_fallback) @@ -3107,16 +3625,18 @@ BOOST_AUTO_TEST_CASE(short_data_calls_fallback) contract A { uint public x; // Signature is d88e0b00 - function fow() { x = 3; } - function () { x = 2; } + function fow() public { x = 3; } + function () external { x = 2; } } )"; compileAndRun(sourceCode); // should call fallback sendMessage(asBytes("\xd8\x8e\x0b"), false, 0); + BOOST_CHECK(m_transactionSuccessful); ABI_CHECK(callContractFunction("x()"), encodeArgs(2)); // should call function sendMessage(asBytes(string("\xd8\x8e\x0b") + string(1, 0)), false, 0); + BOOST_CHECK(m_transactionSuccessful); ABI_CHECK(callContractFunction("x()"), encodeArgs(3)); } @@ -3125,12 +3645,12 @@ BOOST_AUTO_TEST_CASE(event) char const* sourceCode = R"( contract ClientReceipt { event Deposit(address indexed _from, bytes32 indexed _id, uint _value); - function deposit(bytes32 _id, bool _manually) payable { + function deposit(bytes32 _id, bool _manually) public payable { if (_manually) { bytes32 s = 0x19dacbf83c5de6658e14cbf7bcae5c15eca2eedecf1c66fbca928e4d351bea0f; - log3(bytes32(msg.value), s, bytes32(msg.sender), _id); + log3(bytes32(msg.value), s, bytes32(uint256(msg.sender)), _id); } else { - Deposit(msg.sender, _id, msg.value); + emit Deposit(msg.sender, _id, msg.value); } } } @@ -3156,7 +3676,7 @@ BOOST_AUTO_TEST_CASE(event_emit) char const* sourceCode = R"( contract ClientReceipt { event Deposit(address indexed _from, bytes32 indexed _id, uint _value); - function deposit(bytes32 _id) payable { + function deposit(bytes32 _id) public payable { emit Deposit(msg.sender, _id, msg.value); } } @@ -3179,8 +3699,8 @@ BOOST_AUTO_TEST_CASE(event_no_arguments) char const* sourceCode = R"( contract ClientReceipt { event Deposit(); - function deposit() { - Deposit(); + function deposit() public { + emit Deposit(); } } )"; @@ -3194,28 +3714,6 @@ BOOST_AUTO_TEST_CASE(event_no_arguments) BOOST_CHECK_EQUAL(m_logs[0].topics[0], dev::keccak256(string("Deposit()"))); } -BOOST_AUTO_TEST_CASE(event_access_through_base_name) -{ - char const* sourceCode = R"( - contract A { - event x(); - } - contract B is A { - function f() returns (uint) { - A.x(); - return 1; - } - } - )"; - compileAndRun(sourceCode); - callContractFunction("f()"); - BOOST_REQUIRE_EQUAL(m_logs.size(), 1); - BOOST_CHECK_EQUAL(m_logs[0].address, m_contractAddress); - BOOST_CHECK(m_logs[0].data.empty()); - BOOST_REQUIRE_EQUAL(m_logs[0].topics.size(), 1); - BOOST_CHECK_EQUAL(m_logs[0].topics[0], dev::keccak256(string("x()"))); -} - BOOST_AUTO_TEST_CASE(event_access_through_base_name_emit) { char const* sourceCode = R"( @@ -3223,7 +3721,7 @@ BOOST_AUTO_TEST_CASE(event_access_through_base_name_emit) event x(); } contract B is A { - function f() returns (uint) { + function f() public returns (uint) { emit A.x(); return 1; } @@ -3245,68 +3743,16 @@ BOOST_AUTO_TEST_CASE(events_with_same_name) event Deposit(); event Deposit(address _addr); event Deposit(address _addr, uint _amount); - function deposit() returns (uint) { - Deposit(); - return 1; - } - function deposit(address _addr) returns (uint) { - Deposit(_addr); - return 1; - } - function deposit(address _addr, uint _amount) returns (uint) { - Deposit(_addr, _amount); - return 1; - } - } - )"; - u160 const c_loggedAddress = m_contractAddress; - - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("deposit()"), encodeArgs(u256(1))); - BOOST_REQUIRE_EQUAL(m_logs.size(), 1); - BOOST_CHECK_EQUAL(m_logs[0].address, m_contractAddress); - BOOST_CHECK(m_logs[0].data.empty()); - BOOST_REQUIRE_EQUAL(m_logs[0].topics.size(), 1); - BOOST_CHECK_EQUAL(m_logs[0].topics[0], dev::keccak256(string("Deposit()"))); - - ABI_CHECK(callContractFunction("deposit(address)", c_loggedAddress), encodeArgs(u256(1))); - BOOST_REQUIRE_EQUAL(m_logs.size(), 1); - BOOST_CHECK_EQUAL(m_logs[0].address, m_contractAddress); - BOOST_CHECK(m_logs[0].data == encodeArgs(c_loggedAddress)); - BOOST_REQUIRE_EQUAL(m_logs[0].topics.size(), 1); - BOOST_CHECK_EQUAL(m_logs[0].topics[0], dev::keccak256(string("Deposit(address)"))); - - ABI_CHECK(callContractFunction("deposit(address,uint256)", c_loggedAddress, u256(100)), encodeArgs(u256(1))); - BOOST_REQUIRE_EQUAL(m_logs.size(), 1); - BOOST_CHECK_EQUAL(m_logs[0].address, m_contractAddress); - BOOST_CHECK(m_logs[0].data == encodeArgs(c_loggedAddress, 100)); - BOOST_REQUIRE_EQUAL(m_logs[0].topics.size(), 1); - BOOST_CHECK_EQUAL(m_logs[0].topics[0], dev::keccak256(string("Deposit(address,uint256)"))); -} - -BOOST_AUTO_TEST_CASE(events_with_same_name_inherited) -{ - char const* sourceCode = R"( - contract A { - event Deposit(); - } - - contract B { - event Deposit(address _addr); - } - - contract ClientReceipt is A, B { - event Deposit(address _addr, uint _amount); - function deposit() returns (uint) { - Deposit(); + function deposit() public returns (uint) { + emit Deposit(); return 1; } - function deposit(address _addr) returns (uint) { - Deposit(_addr); + function deposit(address _addr) public returns (uint) { + emit Deposit(_addr); return 1; } - function deposit(address _addr, uint _amount) returns (uint) { - Deposit(_addr, _amount); + function deposit(address _addr, uint _amount) public returns (uint) { + emit Deposit(_addr, _amount); return 1; } } @@ -3349,15 +3795,15 @@ BOOST_AUTO_TEST_CASE(events_with_same_name_inherited_emit) contract ClientReceipt is A, B { event Deposit(address _addr, uint _amount); - function deposit() returns (uint) { + function deposit() public returns (uint) { emit Deposit(); return 1; } - function deposit(address _addr) returns (uint) { + function deposit(address _addr) public returns (uint) { emit Deposit(_addr); return 1; } - function deposit(address _addr, uint _amount) returns (uint) { + function deposit(address _addr, uint _amount) public returns (uint) { emit Deposit(_addr, _amount); return 1; } @@ -3393,8 +3839,8 @@ BOOST_AUTO_TEST_CASE(event_anonymous) char const* sourceCode = R"( contract ClientReceipt { event Deposit() anonymous; - function deposit() { - Deposit(); + function deposit() public { + emit Deposit(); } } )"; @@ -3408,8 +3854,8 @@ BOOST_AUTO_TEST_CASE(event_anonymous_with_topics) char const* sourceCode = R"( contract ClientReceipt { event Deposit(address indexed _from, bytes32 indexed _id, uint indexed _value, uint indexed _value2, bytes32 data) anonymous; - function deposit(bytes32 _id) payable { - Deposit(msg.sender, _id, msg.value, 2, "abc"); + function deposit(bytes32 _id) public payable { + emit Deposit(msg.sender, _id, msg.value, 2, "abc"); } } )"; @@ -3432,8 +3878,8 @@ BOOST_AUTO_TEST_CASE(event_lots_of_data) char const* sourceCode = R"( contract ClientReceipt { event Deposit(address _from, bytes32 _id, uint _value, bool _flag); - function deposit(bytes32 _id) payable { - Deposit(msg.sender, _id, msg.value, true); + function deposit(bytes32 _id) public payable { + emit Deposit(msg.sender, _id, msg.value, true); } } )"; @@ -3453,8 +3899,8 @@ BOOST_AUTO_TEST_CASE(event_really_lots_of_data) char const* sourceCode = R"( contract ClientReceipt { event Deposit(uint fixeda, bytes dynx, uint fixedb); - function deposit() { - Deposit(10, msg.data, 15); + function deposit() public { + emit Deposit(10, msg.data, 15); } } )"; @@ -3462,7 +3908,7 @@ BOOST_AUTO_TEST_CASE(event_really_lots_of_data) callContractFunction("deposit()"); BOOST_REQUIRE_EQUAL(m_logs.size(), 1); BOOST_CHECK_EQUAL(m_logs[0].address, m_contractAddress); - BOOST_CHECK_EQUAL(toHex(m_logs[0].data), toHex(encodeArgs(10, 0x60, 15, 4) + FixedHash<4>(dev::keccak256("deposit()")).asBytes())); + BOOST_CHECK_EQUAL(toHex(m_logs[0].data), toHex(encodeArgs(10, 0x60, 15, 4, asString(FixedHash<4>(dev::keccak256("deposit()")).asBytes())))); BOOST_REQUIRE_EQUAL(m_logs[0].topics.size(), 1); BOOST_CHECK_EQUAL(m_logs[0].topics[0], dev::keccak256(string("Deposit(uint256,bytes,uint256)"))); } @@ -3473,12 +3919,12 @@ BOOST_AUTO_TEST_CASE(event_really_lots_of_data_from_storage) contract ClientReceipt { bytes x; event Deposit(uint fixeda, bytes dynx, uint fixedb); - function deposit() { + function deposit() public { x.length = 3; x[0] = "A"; x[1] = "B"; x[2] = "C"; - Deposit(10, x, 15); + emit Deposit(10, x, 15); } } )"; @@ -3497,13 +3943,13 @@ BOOST_AUTO_TEST_CASE(event_really_really_lots_of_data_from_storage) contract ClientReceipt { bytes x; event Deposit(uint fixeda, bytes dynx, uint fixedb); - function deposit() { + function deposit() public { x.length = 31; x[0] = "A"; x[1] = "B"; x[2] = "C"; x[30] = "Z"; - Deposit(10, x, 15); + emit Deposit(10, x, 15); } } )"; @@ -3726,15 +4172,15 @@ BOOST_AUTO_TEST_CASE(event_indexed_string) string x; uint[4] y; event E(string indexed r, uint[4] indexed t); - function deposit() { + function deposit() public { bytes(x).length = 90; - for (uint i = 0; i < 90; i++) + for (uint8 i = 0; i < 90; i++) bytes(x)[i] = byte(i); y[0] = 4; y[1] = 5; y[2] = 6; y[3] = 7; - E(x, y); + emit E(x, y); } } )"; @@ -3758,7 +4204,7 @@ BOOST_AUTO_TEST_CASE(empty_name_input_parameter_with_named_one) { char const* sourceCode = R"( contract test { - function f(uint, uint k) returns(uint ret_k, uint ret_g){ + function f(uint, uint k) public returns(uint ret_k, uint ret_g){ uint g = 8; ret_k = k; ret_g = g; @@ -3774,7 +4220,7 @@ BOOST_AUTO_TEST_CASE(empty_name_return_parameter) { char const* sourceCode = R"( contract test { - function f(uint k) returns(uint){ + function f(uint k) public returns(uint){ return k; } } @@ -3787,8 +4233,8 @@ BOOST_AUTO_TEST_CASE(sha256_empty) { char const* sourceCode = R"( contract C { - function f() returns (bytes32) { - return sha256(); + function f() public returns (bytes32) { + return sha256(""); } } )"; @@ -3800,8 +4246,8 @@ BOOST_AUTO_TEST_CASE(ripemd160_empty) { char const* sourceCode = R"( contract C { - function f() returns (bytes20) { - return ripemd160(); + function f() public returns (bytes20) { + return ripemd160(""); } } )"; @@ -3813,8 +4259,8 @@ BOOST_AUTO_TEST_CASE(keccak256_empty) { char const* sourceCode = R"( contract C { - function f() returns (bytes32) { - return keccak256(); + function f() public returns (bytes32) { + return keccak256(""); } } )"; @@ -3826,9 +4272,9 @@ BOOST_AUTO_TEST_CASE(keccak256_multiple_arguments) { char const* sourceCode = R"( contract c { - function foo(uint a, uint b, uint c) returns (bytes32 d) + function foo(uint a, uint b, uint c) public returns (bytes32 d) { - d = keccak256(a, b, c); + d = keccak256(abi.encodePacked(a, b, c)); } } )"; @@ -3847,9 +4293,9 @@ BOOST_AUTO_TEST_CASE(keccak256_multiple_arguments_with_numeric_literals) { char const* sourceCode = R"( contract c { - function foo(uint a, uint16 b) returns (bytes32 d) + function foo(uint a, uint16 b) public returns (bytes32 d) { - d = keccak256(a, b, 145); + d = keccak256(abi.encodePacked(a, b, uint8(145))); } } )"; @@ -3868,13 +4314,13 @@ BOOST_AUTO_TEST_CASE(keccak256_multiple_arguments_with_string_literals) { char const* sourceCode = R"( contract c { - function foo() returns (bytes32 d) + function foo() public returns (bytes32 d) { d = keccak256("foo"); } - function bar(uint a, uint16 b) returns (bytes32 d) + function bar(uint a, uint16 b) public returns (bytes32 d) { - d = keccak256(a, b, 145, "foo"); + d = keccak256(abi.encodePacked(a, b, uint8(145), "foo")); } } )"; @@ -3897,7 +4343,7 @@ BOOST_AUTO_TEST_CASE(keccak256_with_bytes) char const* sourceCode = R"( contract c { bytes data; - function foo() returns (bool) + function foo() public returns (bool) { data.length = 3; data[0] = "f"; @@ -3913,58 +4359,38 @@ BOOST_AUTO_TEST_CASE(keccak256_with_bytes) BOOST_AUTO_TEST_CASE(iterated_keccak256_with_bytes) { - char const* sourceCode = R"( + char const* sourceCode = R"ABC( contract c { bytes data; - function foo() returns (bytes32) + function foo() public returns (bytes32) { data.length = 3; data[0] = "x"; data[1] = "y"; data[2] = "z"; - return keccak256("b", keccak256(data), "a"); + return keccak256(abi.encodePacked("b", keccak256(data), "a")); } } - )"; + )ABC"; compileAndRun(sourceCode); ABI_CHECK(callContractFunction("foo()"), encodeArgs( u256(dev::keccak256(bytes{'b'} + dev::keccak256("xyz").asBytes() + bytes{'a'})) )); } -BOOST_AUTO_TEST_CASE(sha3_multiple_arguments) -{ - char const* sourceCode = R"( - contract c { - function foo(uint a, uint b, uint c) returns (bytes32 d) - { - d = sha3(a, b, c); - } - })"; - compileAndRun(sourceCode); - - ABI_CHECK(callContractFunction("foo(uint256,uint256,uint256)", 10, 12, 13), encodeArgs( - dev::keccak256( - toBigEndian(u256(10)) + - toBigEndian(u256(12)) + - toBigEndian(u256(13)) - ) - )); -} - BOOST_AUTO_TEST_CASE(generic_call) { char const* sourceCode = R"**( contract receiver { uint public received; - function receive(uint256 x) payable { received = x; } + function receive(uint256 x) public payable { received = x; } } contract sender { - function sender() payable {} - function doSend(address rec) returns (uint d) + constructor() public payable {} + function doSend(address rec) public returns (uint d) { bytes4 signature = bytes4(bytes32(keccak256("receive(uint256)"))); - rec.call.value(2)(signature, 23); + rec.call.value(2)(abi.encodeWithSelector(signature, 23)); return receiver(rec).received(); } } @@ -3976,38 +4402,6 @@ BOOST_AUTO_TEST_CASE(generic_call) BOOST_CHECK_EQUAL(balanceAt(m_contractAddress), 50 - 2); } -BOOST_AUTO_TEST_CASE(generic_callcode) -{ - char const* sourceCode = R"**( - contract Receiver { - uint public received; - function receive(uint256 x) payable { received = x; } - } - contract Sender { - uint public received; - function Sender() payable { } - function doSend(address rec) returns (uint d) - { - bytes4 signature = bytes4(bytes32(keccak256("receive(uint256)"))); - rec.callcode.value(2)(signature, 23); - return Receiver(rec).received(); - } - } - )**"; - compileAndRun(sourceCode, 0, "Receiver"); - u160 const c_receiverAddress = m_contractAddress; - compileAndRun(sourceCode, 50, "Sender"); - u160 const c_senderAddress = m_contractAddress; - ABI_CHECK(callContractFunction("doSend(address)", c_receiverAddress), encodeArgs(0)); - ABI_CHECK(callContractFunction("received()"), encodeArgs(23)); - m_contractAddress = c_receiverAddress; - ABI_CHECK(callContractFunction("received()"), encodeArgs(0)); - BOOST_CHECK(storageEmpty(c_receiverAddress)); - BOOST_CHECK(!storageEmpty(c_senderAddress)); - BOOST_CHECK_EQUAL(balanceAt(c_receiverAddress), 0); - BOOST_CHECK_EQUAL(balanceAt(c_senderAddress), 50); -} - BOOST_AUTO_TEST_CASE(generic_delegatecall) { char const* sourceCode = R"**( @@ -4015,18 +4409,19 @@ BOOST_AUTO_TEST_CASE(generic_delegatecall) uint public received; address public sender; uint public value; - function Receiver() payable {} - function receive(uint256 x) payable { received = x; sender = msg.sender; value = msg.value; } + constructor() public payable {} + function receive(uint256 x) public payable { received = x; sender = msg.sender; value = msg.value; } } contract Sender { uint public received; address public sender; uint public value; - function Sender() payable {} - function doSend(address rec) payable + constructor() public payable {} + function doSend(address rec) public payable { bytes4 signature = bytes4(bytes32(keccak256("receive(uint256)"))); - if (rec.delegatecall(signature, 23)) {} + (bool success,) = rec.delegatecall(abi.encodeWithSelector(signature, 23)); + success; } } )**"; @@ -4049,13 +4444,56 @@ BOOST_AUTO_TEST_CASE(generic_delegatecall) BOOST_CHECK_EQUAL(balanceAt(c_senderAddress), 50 + 11); } +BOOST_AUTO_TEST_CASE(generic_staticcall) +{ + if (dev::test::Options::get().evmVersion().hasStaticCall()) + { + char const* sourceCode = R"**( + contract A { + uint public x; + constructor() public { x = 42; } + function pureFunction(uint256 p) public pure returns (uint256) { return p; } + function viewFunction(uint256 p) public view returns (uint256) { return p + x; } + function nonpayableFunction(uint256 p) public returns (uint256) { x = p; return x; } + function assertFunction(uint256 p) public view returns (uint256) { assert(x == p); return x; } + } + contract C { + function f(address a) public view returns (bool, bytes memory) + { + return a.staticcall(abi.encodeWithSignature("pureFunction(uint256)", 23)); + } + function g(address a) public view returns (bool, bytes memory) + { + return a.staticcall(abi.encodeWithSignature("viewFunction(uint256)", 23)); + } + function h(address a) public view returns (bool, bytes memory) + { + return a.staticcall(abi.encodeWithSignature("nonpayableFunction(uint256)", 23)); + } + function i(address a, uint256 v) public view returns (bool, bytes memory) + { + return a.staticcall(abi.encodeWithSignature("assertFunction(uint256)", v)); + } + } + )**"; + compileAndRun(sourceCode, 0, "A"); + u160 const c_addressA = m_contractAddress; + compileAndRun(sourceCode, 0, "C"); + ABI_CHECK(callContractFunction("f(address)", c_addressA), encodeArgs(true, 0x40, 0x20, 23)); + ABI_CHECK(callContractFunction("g(address)", c_addressA), encodeArgs(true, 0x40, 0x20, 23 + 42)); + ABI_CHECK(callContractFunction("h(address)", c_addressA), encodeArgs(false, 0x40, 0x00)); + ABI_CHECK(callContractFunction("i(address,uint256)", c_addressA, 42), encodeArgs(true, 0x40, 0x20, 42)); + ABI_CHECK(callContractFunction("i(address,uint256)", c_addressA, 23), encodeArgs(false, 0x40, 0x00)); + } +} + BOOST_AUTO_TEST_CASE(library_call_in_homestead) { char const* sourceCode = R"( - library Lib { function m() returns (address) { return msg.sender; } } + library Lib { function m() public returns (address) { return msg.sender; } } contract Test { address public sender; - function f() { + function f() public { sender = Lib.m(); } } @@ -4099,12 +4537,37 @@ BOOST_AUTO_TEST_CASE(library_call_protection) ABI_CHECK(callContractFunction("pu()"), encodeArgs(2)); } + +BOOST_AUTO_TEST_CASE(library_staticcall_delegatecall) +{ + char const* sourceCode = R"( + library Lib { + function x() public view returns (uint) { + return 1; + } + } + contract Test { + uint t; + function f() public returns (uint) { + t = 2; + return this.g(); + } + function g() public view returns (uint) { + return Lib.x(); + } + } + )"; + compileAndRun(sourceCode, 0, "Lib"); + compileAndRun(sourceCode, 0, "Test", bytes(), map<string, Address>{{"Lib", m_contractAddress}}); + ABI_CHECK(callContractFunction("f()"), encodeArgs(1)); +} + BOOST_AUTO_TEST_CASE(store_bytes) { // this test just checks that the copy loop does not mess up the stack char const* sourceCode = R"( contract C { - function save() returns (uint r) { + function save() public returns (uint r) { r = 23; savedData = msg.data; r = 24; @@ -4122,14 +4585,15 @@ BOOST_AUTO_TEST_CASE(bytes_from_calldata_to_memory) { char const* sourceCode = R"( contract C { - function f() returns (bytes32) { - return keccak256("abc", msg.data); + function f() public returns (bytes32) { + return keccak256(abi.encodePacked("abc", msg.data)); } } )"; compileAndRun(sourceCode); bytes calldata1 = FixedHash<4>(dev::keccak256("f()")).asBytes() + bytes(61, 0x22) + bytes(12, 0x12); sendMessage(calldata1, false); + BOOST_CHECK(m_transactionSuccessful); BOOST_CHECK(m_output == encodeArgs(dev::keccak256(bytes{'a', 'b', 'c'} + calldata1))); } @@ -4138,15 +4602,15 @@ BOOST_AUTO_TEST_CASE(call_forward_bytes) char const* sourceCode = R"( contract receiver { uint public received; - function receive(uint x) { received += x + 1; } - function() { received = 0x80; } + function receive(uint x) public { received += x + 1; } + function() external { received = 0x80; } } contract sender { - function sender() { rec = new receiver(); } - function() { savedData = msg.data; } - function forward() returns (bool) { !rec.call(savedData); return true; } - function clear() returns (bool) { delete savedData; return true; } - function val() returns (uint) { return rec.received(); } + constructor() public { rec = new receiver(); } + function() external { savedData = msg.data; } + function forward() public returns (bool) { address(rec).call(savedData); return true; } + function clear() public returns (bool) { delete savedData; return true; } + function val() public returns (uint) { return rec.received(); } receiver rec; bytes savedData; } @@ -4167,24 +4631,27 @@ BOOST_AUTO_TEST_CASE(call_forward_bytes_length) char const* sourceCode = R"( contract receiver { uint public calledLength; - function() { calledLength = msg.data.length; } + function() external { calledLength = msg.data.length; } } contract sender { receiver rec; - constructor() { rec = new receiver(); } - function viaCalldata() returns (uint) { - require(rec.call(msg.data)); + constructor() public { rec = new receiver(); } + function viaCalldata() public returns (uint) { + (bool success,) = address(rec).call(msg.data); + require(success); return rec.calledLength(); } - function viaMemory() returns (uint) { + function viaMemory() public returns (uint) { bytes memory x = msg.data; - require(rec.call(x)); + (bool success,) = address(rec).call(x); + require(success); return rec.calledLength(); } bytes s; - function viaStorage() returns (uint) { + function viaStorage() public returns (uint) { s = msg.data; - require(rec.call(s)); + (bool success,) = address(rec).call(s); + require(success); return rec.calledLength(); } } @@ -4193,18 +4660,14 @@ BOOST_AUTO_TEST_CASE(call_forward_bytes_length) // No additional data, just function selector ABI_CHECK(callContractFunction("viaCalldata()"), encodeArgs(4)); - ABI_CHECK(callContractFunction("viaMemory()"), encodeArgs(0x20)); - // Should be this with 0.5.0: encodeArgs(4)); - ABI_CHECK(callContractFunction("viaStorage()"), encodeArgs(0x20)); - // Should be this with 0.5.0: encodeArgs(4)); + ABI_CHECK(callContractFunction("viaMemory()"), encodeArgs(4)); + ABI_CHECK(callContractFunction("viaStorage()"), encodeArgs(4)); // Some additional unpadded data bytes unpadded = asBytes(string("abc")); ABI_CHECK(callContractFunctionNoEncoding("viaCalldata()", unpadded), encodeArgs(7)); - ABI_CHECK(callContractFunctionNoEncoding("viaMemory()", unpadded), encodeArgs(0x20)); - // Should be this with 0.5.0: encodeArgs(7)); - ABI_CHECK(callContractFunctionNoEncoding("viaStorage()", unpadded), encodeArgs(0x20)); - // Should be this with 0.5.0: encodeArgs(7)); + ABI_CHECK(callContractFunctionNoEncoding("viaMemory()", unpadded), encodeArgs(7)); + ABI_CHECK(callContractFunctionNoEncoding("viaStorage()", unpadded), encodeArgs(7)); } BOOST_AUTO_TEST_CASE(copying_bytes_multiassign) @@ -4212,18 +4675,18 @@ BOOST_AUTO_TEST_CASE(copying_bytes_multiassign) char const* sourceCode = R"( contract receiver { uint public received; - function receive(uint x) { received += x + 1; } - function() { received = 0x80; } + function receive(uint x) public { received += x + 1; } + function() external { received = 0x80; } } contract sender { - function sender() { rec = new receiver(); } - function() { savedData1 = savedData2 = msg.data; } - function forward(bool selector) returns (bool) { - if (selector) { rec.call(savedData1); delete savedData1; } - else { rec.call(savedData2); delete savedData2; } + constructor() public { rec = new receiver(); } + function() external { savedData1 = savedData2 = msg.data; } + function forward(bool selector) public returns (bool) { + if (selector) { address(rec).call(savedData1); delete savedData1; } + else { address(rec).call(savedData2); delete savedData2; } return true; } - function val() returns (uint) { return rec.received(); } + function val() public returns (uint) { return rec.received(); } receiver rec; bytes savedData1; bytes savedData2; @@ -4244,8 +4707,8 @@ BOOST_AUTO_TEST_CASE(delete_removes_bytes_data) { char const* sourceCode = R"( contract c { - function() { data = msg.data; } - function del() returns (bool) { delete data; return true; } + function() external { data = msg.data; } + function del() public returns (bool) { delete data; return true; } bytes data; } )"; @@ -4260,8 +4723,8 @@ BOOST_AUTO_TEST_CASE(copy_from_calldata_removes_bytes_data) { char const* sourceCode = R"( contract c { - function set() returns (bool) { data = msg.data; return true; } - function() { data = msg.data; } + function set() public returns (bool) { data = msg.data; return true; } + function() external { data = msg.data; } bytes data; } )"; @@ -4269,7 +4732,8 @@ BOOST_AUTO_TEST_CASE(copy_from_calldata_removes_bytes_data) ABI_CHECK(callContractFunction("set()", 1, 2, 3, 4, 5), encodeArgs(true)); BOOST_CHECK(!storageEmpty(m_contractAddress)); sendMessage(bytes(), false); - BOOST_CHECK(m_output == bytes()); + BOOST_CHECK(m_transactionSuccessful); + BOOST_CHECK(m_output.empty()); BOOST_CHECK(storageEmpty(m_contractAddress)); } @@ -4277,8 +4741,8 @@ BOOST_AUTO_TEST_CASE(copy_removes_bytes_data) { char const* sourceCode = R"( contract c { - function set() returns (bool) { data1 = msg.data; return true; } - function reset() returns (bool) { data1 = data2; return true; } + function set() public returns (bool) { data1 = msg.data; return true; } + function reset() public returns (bool) { data1 = data2; return true; } bytes data1; bytes data2; } @@ -4294,8 +4758,8 @@ BOOST_AUTO_TEST_CASE(bytes_inside_mappings) { char const* sourceCode = R"( contract c { - function set(uint key) returns (bool) { data[key] = msg.data; return true; } - function copy(uint from, uint to) returns (bool) { data[to] = data[from]; return true; } + function set(uint key) public returns (bool) { data[key] = msg.data; return true; } + function copy(uint from, uint to) public returns (bool) { data[to] = data[from]; return true; } mapping(uint => bytes) data; } )"; @@ -4318,8 +4782,8 @@ BOOST_AUTO_TEST_CASE(bytes_length_member) { char const* sourceCode = R"( contract c { - function set() returns (bool) { data = msg.data; return true; } - function getLength() returns (uint) { return data.length; } + function set() public returns (bool) { data = msg.data; return true; } + function getLength() public returns (uint) { return data.length; } bytes data; } )"; @@ -4336,18 +4800,18 @@ BOOST_AUTO_TEST_CASE(struct_copy) struct Nested { uint x; uint y; } struct Struct { uint a; mapping(uint => Struct) b; Nested nested; uint c; } mapping(uint => Struct) data; - function set(uint k) returns (bool) { + function set(uint k) public returns (bool) { data[k].a = 1; data[k].nested.x = 3; data[k].nested.y = 4; data[k].c = 2; return true; } - function copy(uint from, uint to) returns (bool) { + function copy(uint from, uint to) public returns (bool) { data[to] = data[from]; return true; } - function retrieve(uint k) returns (uint a, uint x, uint y, uint c) + function retrieve(uint k) public returns (uint a, uint x, uint y, uint c) { a = data[k].a; x = data[k].nested.x; @@ -4376,17 +4840,17 @@ BOOST_AUTO_TEST_CASE(struct_containing_bytes_copy_and_delete) struct Struct { uint a; bytes data; uint b; } Struct data1; Struct data2; - function set(uint _a, bytes _data, uint _b) external returns (bool) { + function set(uint _a, bytes calldata _data, uint _b) external returns (bool) { data1.a = _a; data1.b = _b; data1.data = _data; return true; } - function copy() returns (bool) { + function copy() public returns (bool) { data1 = data2; return true; } - function del() returns (bool) { + function del() public returns (bool) { delete data1; return true; } @@ -4412,10 +4876,10 @@ BOOST_AUTO_TEST_CASE(struct_copy_via_local) struct Struct { uint a; uint b; } Struct data1; Struct data2; - function test() returns (bool) { + function test() public returns (bool) { data1.a = 1; data1.b = 2; - var x = data1; + Struct memory x = data1; data2 = x; return data2.a == data1.a && data2.b == data1.b; } @@ -4430,11 +4894,11 @@ BOOST_AUTO_TEST_CASE(using_enums) char const* sourceCode = R"( contract test { enum ActionChoices { GoLeft, GoRight, GoStraight, Sit } - function test() + constructor() public { choices = ActionChoices.GoStraight; } - function getChoice() returns (uint d) + function getChoice() public returns (uint d) { d = uint256(choices); } @@ -4450,20 +4914,20 @@ BOOST_AUTO_TEST_CASE(enum_explicit_overflow) char const* sourceCode = R"( contract test { enum ActionChoices { GoLeft, GoRight, GoStraight } - function test() + constructor() public { } - function getChoiceExp(uint x) returns (uint d) + function getChoiceExp(uint x) public returns (uint d) { choice = ActionChoices(x); d = uint256(choice); } - function getChoiceFromSigned(int x) returns (uint d) + function getChoiceFromSigned(int x) public returns (uint d) { choice = ActionChoices(x); d = uint256(choice); } - function getChoiceFromNegativeLiteral() returns (uint d) + function getChoiceFromNegativeLiteral() public returns (uint d) { choice = ActionChoices(-1); d = uint256(choice); @@ -4487,7 +4951,7 @@ BOOST_AUTO_TEST_CASE(storing_invalid_boolean) contract C { event Ev(bool); bool public perm; - function set() returns(uint) { + function set() public returns(uint) { bool tmp; assembly { tmp := 5 @@ -4495,19 +4959,19 @@ BOOST_AUTO_TEST_CASE(storing_invalid_boolean) perm = tmp; return 1; } - function ret() returns(bool) { + function ret() public returns(bool) { bool tmp; assembly { tmp := 5 } return tmp; } - function ev() returns(uint) { + function ev() public returns(uint) { bool tmp; assembly { tmp := 5 } - Ev(tmp); + emit Ev(tmp); return 1; } } @@ -4530,7 +4994,7 @@ BOOST_AUTO_TEST_CASE(using_contract_enums_with_explicit_contract_name) char const* sourceCode = R"( contract test { enum Choice { A, B, C } - function answer () returns (test.Choice _ret) + function answer () public returns (test.Choice _ret) { _ret = test.Choice.B; } @@ -4548,7 +5012,7 @@ BOOST_AUTO_TEST_CASE(using_inherited_enum) } contract test is base { - function answer () returns (Choice _ret) + function answer () public returns (Choice _ret) { _ret = Choice.B; } @@ -4566,7 +5030,7 @@ BOOST_AUTO_TEST_CASE(using_inherited_enum_excplicitly) } contract test is base { - function answer () returns (base.Choice _ret) + function answer () public returns (base.Choice _ret) { _ret = base.Choice.B; } @@ -4581,7 +5045,7 @@ BOOST_AUTO_TEST_CASE(constructing_enums_from_ints) char const* sourceCode = R"( contract c { enum Truth { False, True } - function test() returns (uint) + function test() public returns (uint) { return uint(Truth(uint8(0x701))); } @@ -4591,23 +5055,130 @@ BOOST_AUTO_TEST_CASE(constructing_enums_from_ints) ABI_CHECK(callContractFunction("test()"), encodeArgs(1)); } +BOOST_AUTO_TEST_CASE(struct_referencing) +{ + static char const* sourceCode = R"( + pragma experimental ABIEncoderV2; + interface I { + struct S { uint a; } + } + library L { + struct S { uint b; uint a; } + function f() public pure returns (S memory) { + S memory s; + s.a = 3; + return s; + } + function g() public pure returns (I.S memory) { + I.S memory s; + s.a = 4; + return s; + } + // argument-dependant lookup tests + function a(I.S memory) public pure returns (uint) { return 1; } + function a(S memory) public pure returns (uint) { return 2; } + } + contract C is I { + function f() public pure returns (S memory) { + S memory s; + s.a = 1; + return s; + } + function g() public pure returns (I.S memory) { + I.S memory s; + s.a = 2; + return s; + } + function h() public pure returns (L.S memory) { + L.S memory s; + s.a = 5; + return s; + } + function x() public pure returns (L.S memory) { + return L.f(); + } + function y() public pure returns (I.S memory) { + return L.g(); + } + function a1() public pure returns (uint) { S memory s; return L.a(s); } + function a2() public pure returns (uint) { L.S memory s; return L.a(s); } + } + )"; + compileAndRun(sourceCode, 0, "L"); + ABI_CHECK(callContractFunction("f()"), encodeArgs(0, 3)); + ABI_CHECK(callContractFunction("g()"), encodeArgs(4)); + compileAndRun(sourceCode, 0, "C", bytes(), map<string, Address>{ {"L", m_contractAddress}}); + ABI_CHECK(callContractFunction("f()"), encodeArgs(1)); + ABI_CHECK(callContractFunction("g()"), encodeArgs(2)); + ABI_CHECK(callContractFunction("h()"), encodeArgs(0, 5)); + ABI_CHECK(callContractFunction("x()"), encodeArgs(0, 3)); + ABI_CHECK(callContractFunction("y()"), encodeArgs(4)); + ABI_CHECK(callContractFunction("a1()"), encodeArgs(1)); + ABI_CHECK(callContractFunction("a2()"), encodeArgs(2)); +} + +BOOST_AUTO_TEST_CASE(enum_referencing) +{ + char const* sourceCode = R"( + interface I { + enum Direction { A, B, Left, Right } + } + library L { + enum Direction { Left, Right } + function f() public pure returns (Direction) { + return Direction.Right; + } + function g() public pure returns (I.Direction) { + return I.Direction.Right; + } + } + contract C is I { + function f() public pure returns (Direction) { + return Direction.Right; + } + function g() public pure returns (I.Direction) { + return I.Direction.Right; + } + function h() public pure returns (L.Direction) { + return L.Direction.Right; + } + function x() public pure returns (L.Direction) { + return L.f(); + } + function y() public pure returns (I.Direction) { + return L.g(); + } + } + )"; + compileAndRun(sourceCode, 0, "L"); + ABI_CHECK(callContractFunction("f()"), encodeArgs(1)); + ABI_CHECK(callContractFunction("g()"), encodeArgs(3)); + compileAndRun(sourceCode, 0, "C", bytes(), map<string, Address>{{"L", m_contractAddress}}); + ABI_CHECK(callContractFunction("f()"), encodeArgs(3)); + ABI_CHECK(callContractFunction("g()"), encodeArgs(3)); + ABI_CHECK(callContractFunction("h()"), encodeArgs(1)); + ABI_CHECK(callContractFunction("x()"), encodeArgs(1)); + ABI_CHECK(callContractFunction("y()"), encodeArgs(3)); +} + BOOST_AUTO_TEST_CASE(inline_member_init) { char const* sourceCode = R"( contract test { - function test(){ + constructor() public { m_b = 6; m_c = 8; } uint m_a = 5; uint m_b; uint m_c = 7; - function get() returns (uint a, uint b, uint c){ + function get() public returns (uint a, uint b, uint c){ a = m_a; b = m_b; c = m_c; } - })"; + } + )"; compileAndRun(sourceCode); ABI_CHECK(callContractFunction("get()"), encodeArgs(5, 6, 8)); } @@ -4616,15 +5187,16 @@ BOOST_AUTO_TEST_CASE(inline_member_init_inheritence) { char const* sourceCode = R"( contract Base { - function Base(){} + constructor() public {} uint m_base = 5; - function getBMember() returns (uint i) { return m_base; } + function getBMember() public returns (uint i) { return m_base; } } contract Derived is Base { - function Derived(){} + constructor() public {} uint m_derived = 6; - function getDMember() returns (uint i) { return m_derived; } - })"; + function getDMember() public returns (uint i) { return m_derived; } + } + )"; compileAndRun(sourceCode); ABI_CHECK(callContractFunction("getBMember()"), encodeArgs(5)); ABI_CHECK(callContractFunction("getDMember()"), encodeArgs(6)); @@ -4635,12 +5207,13 @@ BOOST_AUTO_TEST_CASE(inline_member_init_inheritence_without_constructor) char const* sourceCode = R"( contract Base { uint m_base = 5; - function getBMember() returns (uint i) { return m_base; } + function getBMember() public returns (uint i) { return m_base; } } contract Derived is Base { uint m_derived = 6; - function getDMember() returns (uint i) { return m_derived; } - })"; + function getDMember() public returns (uint i) { return m_derived; } + } + )"; compileAndRun(sourceCode); ABI_CHECK(callContractFunction("getBMember()"), encodeArgs(5)); ABI_CHECK(callContractFunction("getDMember()"), encodeArgs(6)); @@ -4650,7 +5223,7 @@ BOOST_AUTO_TEST_CASE(external_function) { char const* sourceCode = R"( contract c { - function f(uint a) returns (uint) { return a; } + function f(uint a) public returns (uint) { return a; } function test(uint a, uint b) external returns (uint r_a, uint r_b) { r_a = f(a + 7); r_b = b; @@ -4666,12 +5239,12 @@ BOOST_AUTO_TEST_CASE(bytes_in_arguments) char const* sourceCode = R"( contract c { uint result; - function f(uint a, uint b) { result += a + b; } - function g(uint a) { result *= a; } - function test(uint a, bytes data1, bytes data2, uint b) external returns (uint r_a, uint r, uint r_b, uint l) { + function f(uint a, uint b) public { result += a + b; } + function g(uint a) public { result *= a; } + function test(uint a, bytes calldata data1, bytes calldata data2, uint b) external returns (uint r_a, uint r, uint r_b, uint l) { r_a = a; - this.call(data1); - this.call(data2); + address(this).call(data1); + address(this).call(data2); r = result; r_b = b; l = data1.length; @@ -4699,12 +5272,12 @@ BOOST_AUTO_TEST_CASE(fixed_arrays_in_storage) struct Data { uint x; uint y; } Data[2**10] data; uint[2**10 + 3] ids; - function setIDStatic(uint id) { ids[2] = id; } - function setID(uint index, uint id) { ids[index] = id; } - function setData(uint index, uint x, uint y) { data[index].x = x; data[index].y = y; } - function getID(uint index) returns (uint) { return ids[index]; } - function getData(uint index) returns (uint x, uint y) { x = data[index].x; y = data[index].y; } - function getLengths() returns (uint l1, uint l2) { l1 = data.length; l2 = ids.length; } + function setIDStatic(uint id) public { ids[2] = id; } + function setID(uint index, uint id) public { ids[index] = id; } + function setData(uint index, uint x, uint y) public { data[index].x = x; data[index].y = y; } + function getID(uint index) public returns (uint) { return ids[index]; } + function getData(uint index) public returns (uint x, uint y) { x = data[index].x; y = data[index].y; } + function getLengths() public returns (uint l1, uint l2) { l1 = data.length; l2 = ids.length; } } )"; compileAndRun(sourceCode); @@ -4726,13 +5299,13 @@ BOOST_AUTO_TEST_CASE(dynamic_arrays_in_storage) struct Data { uint x; uint y; } Data[] data; uint[] ids; - function setIDStatic(uint id) { ids[2] = id; } - function setID(uint index, uint id) { ids[index] = id; } - function setData(uint index, uint x, uint y) { data[index].x = x; data[index].y = y; } - function getID(uint index) returns (uint) { return ids[index]; } - function getData(uint index) returns (uint x, uint y) { x = data[index].x; y = data[index].y; } - function getLengths() returns (uint l1, uint l2) { l1 = data.length; l2 = ids.length; } - function setLengths(uint l1, uint l2) { data.length = l1; ids.length = l2; } + function setIDStatic(uint id) public { ids[2] = id; } + function setID(uint index, uint id) public { ids[index] = id; } + function setData(uint index, uint x, uint y) public { data[index].x = x; data[index].y = y; } + function getID(uint index) public returns (uint) { return ids[index]; } + function getData(uint index) public returns (uint x, uint y) { x = data[index].x; y = data[index].y; } + function getLengths() public returns (uint l1, uint l2) { l1 = data.length; l2 = ids.length; } + function setLengths(uint l1, uint l2) public { data.length = l1; ids.length = l2; } } )"; compileAndRun(sourceCode); @@ -4754,9 +5327,9 @@ BOOST_AUTO_TEST_CASE(fixed_out_of_bounds_array_access) char const* sourceCode = R"( contract c { uint[4] data; - function set(uint index, uint value) returns (bool) { data[index] = value; return true; } - function get(uint index) returns (uint) { return data[index]; } - function length() returns (uint) { return data.length; } + function set(uint index, uint value) public returns (bool) { data[index] = value; return true; } + function get(uint index) public returns (uint) { return data[index]; } + function length() public returns (uint) { return data.length; } } )"; compileAndRun(sourceCode); @@ -4775,10 +5348,10 @@ BOOST_AUTO_TEST_CASE(dynamic_out_of_bounds_array_access) char const* sourceCode = R"( contract c { uint[] data; - function enlarge(uint amount) returns (uint) { return data.length += amount; } - function set(uint index, uint value) returns (bool) { data[index] = value; return true; } - function get(uint index) returns (uint) { return data[index]; } - function length() returns (uint) { return data.length; } + function enlarge(uint amount) public returns (uint) { return data.length += amount; } + function set(uint index, uint value) public returns (bool) { data[index] = value; return true; } + function get(uint index) public returns (uint) { return data[index]; } + function length() public returns (uint) { return data.length; } } )"; compileAndRun(sourceCode); @@ -4800,10 +5373,10 @@ BOOST_AUTO_TEST_CASE(fixed_array_cleanup) uint spacer1; uint spacer2; uint[20] data; - function fill() { + function fill() public { for (uint i = 0; i < data.length; ++i) data[i] = i+1; } - function clear() { delete data; } + function clear() public { delete data; } } )"; compileAndRun(sourceCode); @@ -4821,10 +5394,10 @@ BOOST_AUTO_TEST_CASE(short_fixed_array_cleanup) uint spacer1; uint spacer2; uint[3] data; - function fill() { + function fill() public { for (uint i = 0; i < data.length; ++i) data[i] = i+1; } - function clear() { delete data; } + function clear() public { delete data; } } )"; compileAndRun(sourceCode); @@ -4841,12 +5414,12 @@ BOOST_AUTO_TEST_CASE(dynamic_array_cleanup) contract c { uint[20] spacer; uint[] dynamic; - function fill() { + function fill() public { dynamic.length = 21; for (uint i = 0; i < dynamic.length; ++i) dynamic[i] = i+1; } - function halfClear() { dynamic.length = 5; } - function fullClear() { delete dynamic; } + function halfClear() public { dynamic.length = 5; } + function fullClear() public { delete dynamic; } } )"; compileAndRun(sourceCode); @@ -4865,14 +5438,14 @@ BOOST_AUTO_TEST_CASE(dynamic_multi_array_cleanup) contract c { struct s { uint[][] d; } s[] data; - function fill() returns (uint) { + function fill() public returns (uint) { data.length = 3; data[2].d.length = 4; data[2].d[3].length = 5; data[2].d[3][4] = 8; return data[2].d[3][4]; } - function clear() { delete data; } + function clear() public { delete data; } } )"; compileAndRun(sourceCode); @@ -4889,11 +5462,11 @@ BOOST_AUTO_TEST_CASE(array_copy_storage_storage_dyn_dyn) contract c { uint[] data1; uint[] data2; - function setData1(uint length, uint index, uint value) { + function setData1(uint length, uint index, uint value) public { data1.length = length; if (index < length) data1[index] = value; } - function copyStorageStorage() { data2 = data1; } - function getData2(uint index) returns (uint len, uint val) { + function copyStorageStorage() public { data2 = data1; } + function getData2(uint index) public returns (uint len, uint val) { len = data2.length; if (index < len) val = data2[index]; } } @@ -4914,7 +5487,7 @@ BOOST_AUTO_TEST_CASE(array_copy_storage_storage_static_static) contract c { uint[40] data1; uint[20] data2; - function test() returns (uint x, uint y){ + function test() public returns (uint x, uint y){ data1[30] = 4; data1[2] = 7; data1[3] = 9; @@ -4935,7 +5508,7 @@ BOOST_AUTO_TEST_CASE(array_copy_storage_storage_static_dynamic) contract c { uint[9] data1; uint[] data2; - function test() returns (uint x, uint y){ + function test() public returns (uint x, uint y){ data1[8] = 4; data2 = data1; x = data2.length; @@ -4953,10 +5526,10 @@ BOOST_AUTO_TEST_CASE(array_copy_different_packing) contract c { bytes8[] data1; // 4 per slot bytes10[] data2; // 3 per slot - function test() returns (bytes10 a, bytes10 b, bytes10 c, bytes10 d, bytes10 e) { + function test() public returns (bytes10 a, bytes10 b, bytes10 c, bytes10 d, bytes10 e) { data1.length = 9; for (uint i = 0; i < data1.length; ++i) - data1[i] = bytes8(i); + data1[i] = bytes8(uint64(i)); data2 = data1; a = data2[1]; b = data2[2]; @@ -4982,10 +5555,10 @@ BOOST_AUTO_TEST_CASE(array_copy_target_simple) contract c { bytes8[9] data1; // 4 per slot bytes17[10] data2; // 1 per slot, no offset counter - function test() returns (bytes17 a, bytes17 b, bytes17 c, bytes17 d, bytes17 e) { + function test() public returns (bytes17 a, bytes17 b, bytes17 c, bytes17 d, bytes17 e) { for (uint i = 0; i < data1.length; ++i) - data1[i] = bytes8(i); - data2[8] = data2[9] = 2; + data1[i] = bytes8(uint64(i)); + data2[8] = data2[9] = bytes8(uint64(2)); data2 = data1; a = data2[1]; b = data2[2]; @@ -5012,18 +5585,18 @@ BOOST_AUTO_TEST_CASE(array_copy_target_leftover) contract c { byte[10] data1; bytes2[32] data2; - function test() returns (uint check, uint res1, uint res2) { + function test() public returns (uint check, uint res1, uint res2) { uint i; for (i = 0; i < data2.length; ++i) data2[i] = 0xffff; - check = uint(data2[31]) * 0x10000 | uint(data2[14]); + check = uint(uint16(data2[31])) * 0x10000 | uint(uint16(data2[14])); for (i = 0; i < data1.length; ++i) data1[i] = byte(uint8(1 + i)); data2 = data1; for (i = 0; i < 16; ++i) - res1 |= uint(data2[i]) * 0x10000**i; + res1 |= uint(uint16(data2[i])) * 0x10000**i; for (i = 0; i < 16; ++i) - res2 |= uint(data2[16 + i]) * 0x10000**i; + res2 |= uint(uint16(data2[16 + i])) * 0x10000**i; } } )"; @@ -5043,13 +5616,13 @@ BOOST_AUTO_TEST_CASE(array_copy_target_leftover2) contract c { bytes8[4] data1; // fits into one slot bytes10[6] data2; // 4 elements need two slots - function test() returns (bytes10 r1, bytes10 r2, bytes10 r3) { - data1[0] = 1; - data1[1] = 2; - data1[2] = 3; - data1[3] = 4; + function test() public returns (bytes10 r1, bytes10 r2, bytes10 r3) { + data1[0] = bytes8(uint64(1)); + data1[1] = bytes8(uint64(2)); + data1[2] = bytes8(uint64(3)); + data1[3] = bytes8(uint64(4)); for (uint i = 0; i < data2.length; ++i) - data2[i] = bytes10(0xffff00 | (1 + i)); + data2[i] = bytes10(uint80(0xffff00 | (1 + i))); data2 = data1; r1 = data2[3]; r2 = data2[4]; @@ -5072,7 +5645,7 @@ BOOST_AUTO_TEST_CASE(array_copy_storage_storage_struct) struct Data { uint x; uint y; } Data[] data1; Data[] data2; - function test() returns (uint x, uint y) { + function test() public returns (uint x, uint y) { data1.length = 9; data1[8].x = 4; data1[8].y = 5; @@ -5100,22 +5673,22 @@ BOOST_AUTO_TEST_CASE(array_copy_storage_abi) uint16[] y; uint24[] z; uint24[][] w; - function test1() returns (uint8[]) { + function test1() public returns (uint8[] memory) { for (uint i = 0; i < 101; ++i) x.push(uint8(i)); return x; } - function test2() returns (uint16[]) { + function test2() public returns (uint16[] memory) { for (uint i = 0; i < 101; ++i) y.push(uint16(i)); return y; } - function test3() returns (uint24[]) { + function test3() public returns (uint24[] memory) { for (uint i = 0; i < 101; ++i) z.push(uint24(i)); return z; } - function test4() returns (uint24[][]) { + function test4() public returns (uint24[][] memory) { w.length = 5; for (uint i = 0; i < 5; ++i) for (uint j = 0; j < 101; ++j) @@ -5148,7 +5721,7 @@ BOOST_AUTO_TEST_CASE(array_copy_storage_abi_signed) char const* sourceCode = R"( contract c { int16[] x; - function test() returns (int16[]) { + function test() public returns (int16[] memory) { x.push(int16(-1)); x.push(int16(-1)); x.push(int16(8)); @@ -5180,7 +5753,7 @@ BOOST_AUTO_TEST_CASE(array_push) char const* sourceCode = R"( contract c { uint[] data; - function test() returns (uint x, uint y, uint z, uint l) { + function test() public returns (uint x, uint y, uint z, uint l) { data.push(5); x = data[0]; data.push(4); @@ -5200,7 +5773,7 @@ BOOST_AUTO_TEST_CASE(array_push_struct) contract c { struct S { uint16 a; uint16 b; uint16[3] c; uint16[] d; } S[] data; - function test() returns (uint16, uint16, uint16, uint16) { + function test() public returns (uint16, uint16, uint16, uint16) { S memory s; s.a = 2; s.b = 3; @@ -5221,7 +5794,7 @@ BOOST_AUTO_TEST_CASE(array_push_packed_array) char const* sourceCode = R"( contract c { uint80[] x; - function test() returns (uint80, uint80, uint80, uint80) { + function test() public returns (uint80, uint80, uint80, uint80) { x.push(1); x.push(2); x.push(3); @@ -5241,14 +5814,14 @@ BOOST_AUTO_TEST_CASE(byte_array_push) char const* sourceCode = R"( contract c { bytes data; - function test() returns (bool x) { - if (data.push(5) != 1) return true; - if (data[0] != 5) return true; - data.push(4); - if (data[1] != 4) return true; - uint l = data.push(3); - if (data[2] != 3) return true; - if (l != 3) return true; + function test() public returns (bool x) { + if (data.push(0x05) != 1) return true; + if (data[0] != 0x05) return true; + data.push(0x04); + if (data[1] != 0x04) return true; + uint l = data.push(0x03); + if (data[2] != 0x03) return true; + if (l != 0x03) return true; } } )"; @@ -5262,14 +5835,14 @@ BOOST_AUTO_TEST_CASE(byte_array_push_transition) char const* sourceCode = R"( contract c { bytes data; - function test() returns (uint) { - for (uint i = 1; i < 40; i++) + function test() public returns (uint) { + for (uint8 i = 1; i < 40; i++) { data.push(byte(i)); if (data.length != i) return 0x1000 + i; if (data[data.length - 1] != byte(i)) return i; } - for (i = 1; i < 40; i++) + for (uint8 i = 1; i < 40; i++) if (data[i - 1] != byte(i)) return 0x1000000 + i; return 0; } @@ -5279,11 +5852,331 @@ BOOST_AUTO_TEST_CASE(byte_array_push_transition) ABI_CHECK(callContractFunction("test()"), encodeArgs(0)); } +BOOST_AUTO_TEST_CASE(array_pop) +{ + char const* sourceCode = R"( + contract c { + uint[] data; + function test() public returns (uint x, uint l) { + data.push(7); + x = data.push(3); + data.pop(); + x = data.length; + data.pop(); + l = data.length; + } + } + )"; + compileAndRun(sourceCode); + ABI_CHECK(callContractFunction("test()"), encodeArgs(1, 0)); +} + +BOOST_AUTO_TEST_CASE(array_pop_uint16_transition) +{ + char const* sourceCode = R"( + contract c { + uint16[] data; + function test() public returns (uint16 x, uint16 y, uint16 z) { + for (uint i = 1; i <= 48; i++) + data.push(uint16(i)); + for (uint j = 1; j <= 10; j++) + data.pop(); + x = data[data.length - 1]; + for (uint k = 1; k <= 10; k++) + data.pop(); + y = data[data.length - 1]; + for (uint l = 1; l <= 10; l++) + data.pop(); + z = data[data.length - 1]; + for (uint m = 1; m <= 18; m++) + data.pop(); + } + } + )"; + compileAndRun(sourceCode); + ABI_CHECK(callContractFunction("test()"), encodeArgs(38, 28, 18)); + BOOST_CHECK(storageEmpty(m_contractAddress)); +} + +BOOST_AUTO_TEST_CASE(array_pop_uint24_transition) +{ + char const* sourceCode = R"( + contract c { + uint256 a; + uint256 b; + uint256 c; + uint24[] data; + function test() public returns (uint24 x, uint24 y) { + for (uint i = 1; i <= 30; i++) + data.push(uint24(i)); + for (uint j = 1; j <= 10; j++) + data.pop(); + x = data[data.length - 1]; + for (uint k = 1; k <= 10; k++) + data.pop(); + y = data[data.length - 1]; + for (uint l = 1; l <= 10; l++) + data.pop(); + } + } + )"; + compileAndRun(sourceCode); + ABI_CHECK(callContractFunction("test()"), encodeArgs(20, 10)); + BOOST_CHECK(storageEmpty(m_contractAddress)); +} + +BOOST_AUTO_TEST_CASE(array_pop_array_transition) +{ + char const* sourceCode = R"( + contract c { + uint256 a; + uint256 b; + uint256 c; + uint16[] inner = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]; + uint16[][] data; + function test() public returns (uint x, uint y, uint z) { + for (uint i = 1; i <= 48; i++) + data.push(inner); + for (uint j = 1; j <= 10; j++) + data.pop(); + x = data[data.length - 1][0]; + for (uint k = 1; k <= 10; k++) + data.pop(); + y = data[data.length - 1][1]; + for (uint l = 1; l <= 10; l++) + data.pop(); + z = data[data.length - 1][2]; + for (uint m = 1; m <= 18; m++) + data.pop(); + delete inner; + } + } + )"; + compileAndRun(sourceCode); + ABI_CHECK(callContractFunction("test()"), encodeArgs(1, 2, 3)); + BOOST_CHECK(storageEmpty(m_contractAddress)); +} + +BOOST_AUTO_TEST_CASE(array_pop_empty_exception) +{ + char const* sourceCode = R"( + contract c { + uint[] data; + function test() public returns (bool) { + data.pop(); + return true; + } + } + )"; + compileAndRun(sourceCode); + ABI_CHECK(callContractFunction("test()"), encodeArgs()); +} + +BOOST_AUTO_TEST_CASE(array_pop_storage_empty) +{ + char const* sourceCode = R"( + contract c { + uint[] data; + function test() public { + data.push(7); + data.pop(); + } + } + )"; + compileAndRun(sourceCode); + ABI_CHECK(callContractFunction("test()"), encodeArgs()); + BOOST_CHECK(storageEmpty(m_contractAddress)); +} + +BOOST_AUTO_TEST_CASE(byte_array_pop) +{ + char const* sourceCode = R"( + contract c { + bytes data; + function test() public returns (uint x, uint y, uint l) { + data.push(0x07); + x = data.push(0x03); + data.pop(); + data.pop(); + y = data.push(0x02); + l = data.length; + } + } + )"; + compileAndRun(sourceCode); + ABI_CHECK(callContractFunction("test()"), encodeArgs(2, 1, 1)); +} + +BOOST_AUTO_TEST_CASE(byte_array_pop_empty_exception) +{ + char const* sourceCode = R"( + contract c { + uint256 a; + uint256 b; + uint256 c; + bytes data; + function test() public returns (bool) { + data.pop(); + return true; + } + } + )"; + compileAndRun(sourceCode); + ABI_CHECK(callContractFunction("test()"), encodeArgs()); +} + +BOOST_AUTO_TEST_CASE(byte_array_pop_storage_empty) +{ + char const* sourceCode = R"( + contract c { + bytes data; + function test() public { + data.push(0x07); + data.push(0x05); + data.push(0x03); + data.pop(); + data.pop(); + data.pop(); + } + } + )"; + compileAndRun(sourceCode); + ABI_CHECK(callContractFunction("test()"), encodeArgs()); + BOOST_CHECK(storageEmpty(m_contractAddress)); +} + +BOOST_AUTO_TEST_CASE(byte_array_pop_long_storage_empty) +{ + char const* sourceCode = R"( + contract c { + uint256 a; + uint256 b; + uint256 c; + bytes data; + function test() public returns (bool) { + for (uint8 i = 0; i <= 40; i++) + data.push(byte(i+1)); + for (int8 j = 40; j >= 0; j--) { + require(data[uint8(j)] == byte(j+1)); + require(data.length == uint8(j+1)); + data.pop(); + } + return true; + } + } + )"; + compileAndRun(sourceCode); + ABI_CHECK(callContractFunction("test()"), encodeArgs(true)); + BOOST_CHECK(storageEmpty(m_contractAddress)); +} + +BOOST_AUTO_TEST_CASE(byte_array_pop_long_storage_empty_garbage_ref) +{ + char const* sourceCode = R"( + contract c { + uint256 a; + uint256 b; + bytes data; + function test() public { + for (uint8 i = 0; i <= 40; i++) + data.push(0x03); + for (uint8 j = 0; j <= 40; j++) { + assembly { + mstore(0, "garbage") + } + data.pop(); + } + } + } + )"; + compileAndRun(sourceCode); + ABI_CHECK(callContractFunction("test()"), encodeArgs()); + BOOST_CHECK(storageEmpty(m_contractAddress)); +} + +BOOST_AUTO_TEST_CASE(byte_array_pop_masking_long) +{ + char const* sourceCode = R"( + contract c { + bytes data; + function test() public returns (bytes memory) { + for (uint i = 0; i < 34; i++) + data.push(0x03); + data.pop(); + return data; + } + } + )"; + compileAndRun(sourceCode); + ABI_CHECK(callContractFunction("test()"), encodeArgs( + u256(0x20), + u256(33), + asString(fromHex("0303030303030303030303030303030303030303030303030303030303030303")), + asString(fromHex("03")) + )); +} + +BOOST_AUTO_TEST_CASE(byte_array_pop_copy_long) +{ + char const* sourceCode = R"( + contract c { + bytes data; + function test() public returns (bytes memory) { + for (uint i = 0; i < 33; i++) + data.push(0x03); + for (uint j = 0; j < 4; j++) + data.pop(); + return data; + } + } + )"; + compileAndRun(sourceCode); + ABI_CHECK(callContractFunction("test()"), encodeArgs( + u256(0x20), + u256(29), + asString(fromHex("0303030303030303030303030303030303030303030303030303030303")) + )); +} + +BOOST_AUTO_TEST_CASE(array_pop_isolated) +{ + char const* sourceCode = R"( + // This tests that the compiler knows the correct size of the function on the stack. + contract c { + uint[] data; + function test() public returns (uint x) { + x = 2; + data.pop; + x = 3; + } + } + )"; + compileAndRun(sourceCode); + ABI_CHECK(callContractFunction("test()"), encodeArgs(3)); +} + +BOOST_AUTO_TEST_CASE(byte_array_pop_isolated) +{ + char const* sourceCode = R"( + // This tests that the compiler knows the correct size of the function on the stack. + contract c { + bytes data; + function test() public returns (uint x) { + x = 2; + data.pop; + x = 3; + } + } + )"; + compileAndRun(sourceCode); + ABI_CHECK(callContractFunction("test()"), encodeArgs(3)); +} + BOOST_AUTO_TEST_CASE(external_array_args) { char const* sourceCode = R"( contract c { - function test(uint[8] a, uint[] b, uint[5] c, uint a_index, uint b_index, uint c_index) + function test(uint[8] calldata a, uint[] calldata b, uint[5] calldata c, uint a_index, uint b_index, uint c_index) external returns (uint av, uint bv, uint cv) { av = a[a_index]; bv = b[b_index]; @@ -5308,23 +6201,23 @@ BOOST_AUTO_TEST_CASE(bytes_index_access) char const* sourceCode = R"( contract c { bytes data; - function direct(bytes arg, uint index) external returns (uint) { - return uint(arg[index]); + function direct(bytes calldata arg, uint index) external returns (uint) { + return uint(uint8(arg[index])); } - function storageCopyRead(bytes arg, uint index) external returns (uint) { + function storageCopyRead(bytes calldata arg, uint index) external returns (uint) { data = arg; - return uint(data[index]); + return uint(uint8(data[index])); } function storageWrite() external returns (uint) { data.length = 35; data[31] = 0x77; data[32] = 0x14; - data[31] = 1; - data[31] |= 8; - data[30] = 1; - data[32] = 3; - return uint(data[30]) * 0x100 | uint(data[31]) * 0x10 | uint(data[32]); + data[31] = 0x01; + data[31] |= 0x08; + data[30] = 0x01; + data[32] = 0x03; + return uint(uint8(data[30])) * 0x100 | uint(uint8(data[31])) * 0x10 | uint(uint8(data[32])); } } )"; @@ -5347,11 +6240,11 @@ BOOST_AUTO_TEST_CASE(bytes_delete_element) function test1() external returns (bool) { data.length = 100; for (uint i = 0; i < data.length; i++) - data[i] = byte(i); + data[i] = byte(uint8(i)); delete data[94]; delete data[96]; delete data[98]; - return data[94] == 0 && data[95] == 95 && data[96] == 0 && data[97] == 97; + return data[94] == 0 && uint8(data[95]) == 95 && data[96] == 0 && uint8(data[97]) == 97; } } )"; @@ -5366,13 +6259,13 @@ BOOST_AUTO_TEST_CASE(array_copy_calldata_storage) uint[9] m_data; uint[] m_data_dyn; uint8[][] m_byte_data; - function store(uint[9] a, uint8[3][] b) external returns (uint8) { + function store(uint[9] calldata a, uint8[3][] calldata b) external returns (uint8) { m_data = a; m_data_dyn = a; m_byte_data = b; return b[3][1]; // note that access and declaration are reversed to each other } - function retrieve() returns (uint a, uint b, uint c, uint d, uint e, uint f, uint g) { + function retrieve() public returns (uint a, uint b, uint c, uint d, uint e, uint f, uint g) { a = m_data.length; b = m_data[7]; c = m_data_dyn.length; @@ -5405,7 +6298,7 @@ BOOST_AUTO_TEST_CASE(array_copy_nested_array) uint[4][] a; uint[10][] b; uint[][] c; - function test(uint[2][] d) external returns (uint) { + function test(uint[2][] calldata d) external returns (uint) { a = d; b = a; c = b; @@ -5428,7 +6321,7 @@ BOOST_AUTO_TEST_CASE(array_copy_including_mapping) contract c { mapping(uint=>uint)[90][] large; mapping(uint=>uint)[3][] small; - function test() returns (uint r) { + function test() public returns (uint r) { large.length = small.length = 7; large[3][2][0] = 2; large[1] = large[3]; @@ -5442,7 +6335,7 @@ BOOST_AUTO_TEST_CASE(array_copy_including_mapping) delete small; delete large; } - function clear() returns (uint r) { + function clear() public returns (uint r) { large.length = small.length = 7; small[3][2][0] = 0; large[3][2][0] = 0; @@ -5471,11 +6364,11 @@ BOOST_AUTO_TEST_CASE(swap_in_storage_overwrite) struct S { uint a; uint b; } S public x; S public y; - function set() { + function set() public { x.a = 1; x.b = 2; y.a = 3; y.b = 4; } - function swap() { + function swap() public { (x, y) = (y, x); } } @@ -5495,18 +6388,19 @@ BOOST_AUTO_TEST_CASE(pass_dynamic_arguments_to_the_base) { char const* sourceCode = R"( contract Base { - function Base(uint i) + constructor(uint i) public { m_i = i; } uint public m_i; } contract Derived is Base { - function Derived(uint i) Base(i) + constructor(uint i) Base(i) public {} } contract Final is Derived(4) { - })"; + } + )"; compileAndRun(sourceCode); ABI_CHECK(callContractFunction("m_i()"), encodeArgs(4)); } @@ -5515,21 +6409,22 @@ BOOST_AUTO_TEST_CASE(pass_dynamic_arguments_to_the_base_base) { char const* sourceCode = R"( contract Base { - function Base(uint j) + constructor(uint j) public { m_i = j; } uint public m_i; } contract Base1 is Base { - function Base1(uint k) Base(k*k) {} + constructor(uint k) Base(k) public {} } contract Derived is Base, Base1 { - function Derived(uint i) Base(i) Base1(i) + constructor(uint i) Base1(i) public {} } contract Final is Derived(4) { - })"; + } + )"; compileAndRun(sourceCode); ABI_CHECK(callContractFunction("m_i()"), encodeArgs(4)); } @@ -5538,18 +6433,21 @@ BOOST_AUTO_TEST_CASE(pass_dynamic_arguments_to_the_base_base_with_gap) { char const* sourceCode = R"( contract Base { - function Base(uint i) + constructor(uint i) public { m_i = i; } uint public m_i; } - contract Base1 is Base(3) {} + contract Base1 is Base { + constructor(uint k) public {} + } contract Derived is Base, Base1 { - function Derived(uint i) Base(i) {} + constructor(uint i) Base(i) Base1(7) public {} } contract Final is Derived(4) { - })"; + } + )"; compileAndRun(sourceCode); ABI_CHECK(callContractFunction("m_i()"), encodeArgs(4)); } @@ -5558,9 +6456,10 @@ BOOST_AUTO_TEST_CASE(simple_constant_variables_test) { char const* sourceCode = R"( contract Foo { - function getX() returns (uint r) { return x; } + function getX() public returns (uint r) { return x; } uint constant x = 56; - })"; + } + )"; compileAndRun(sourceCode); ABI_CHECK(callContractFunction("getX()"), encodeArgs(56)); } @@ -5573,7 +6472,8 @@ BOOST_AUTO_TEST_CASE(constant_variables) enum ActionChoices { GoLeft, GoRight, GoStraight, Sit } ActionChoices constant choices = ActionChoices.GoLeft; bytes32 constant st = "abc\x00\xff__"; - })"; + } + )"; compileAndRun(sourceCode); } @@ -5582,7 +6482,7 @@ BOOST_AUTO_TEST_CASE(assignment_to_const_var_involving_expression) char const* sourceCode = R"( contract C { uint constant x = 0x123 + 0x456; - function f() returns (uint) { return x + 1; } + function f() public returns (uint) { return x + 1; } } )"; compileAndRun(sourceCode); @@ -5594,7 +6494,7 @@ BOOST_AUTO_TEST_CASE(assignment_to_const_var_involving_keccak) char const* sourceCode = R"( contract C { bytes32 constant x = keccak256("abc"); - function f() returns (bytes32) { return x; } + function f() public returns (bytes32) { return x; } } )"; compileAndRun(sourceCode); @@ -5608,7 +6508,7 @@ BOOST_AUTO_TEST_CASE(assignment_to_const_var_involving_keccak) // contract C { // uint[3] constant x = [uint(1), 2, 3]; // uint constant y = x[0] + x[1] + x[2]; -// function f() returns (uint) { return y; } +// function f() public returns (uint) { return y; } // } // )"; // compileAndRun(sourceCode); @@ -5622,7 +6522,7 @@ BOOST_AUTO_TEST_CASE(assignment_to_const_var_involving_keccak) // contract C { // struct S { uint x; uint[] y; } // S constant x = S(5, new uint[](4)); -// function f() returns (uint) { return x.x; } +// function f() public returns (uint) { return x.x; } // } // )"; // compileAndRun(sourceCode); @@ -5635,7 +6535,7 @@ BOOST_AUTO_TEST_CASE(packed_storage_structs_uint) contract C { struct str { uint8 a; uint16 b; uint248 c; } str data; - function test() returns (uint) { + function test() public returns (uint) { data.a = 2; if (data.a != 2) return 2; data.b = 0xabcd; @@ -5667,7 +6567,7 @@ BOOST_AUTO_TEST_CASE(packed_storage_structs_enum) enum larger { A, B, C, D, E} struct str { small a; small b; larger c; larger d; } str data; - function test() returns (uint) { + function test() public returns (uint) { data.a = small.B; if (data.a != small.B) return 2; data.b = small.C; @@ -5700,20 +6600,20 @@ BOOST_AUTO_TEST_CASE(packed_storage_structs_bytes) byte x; s2 data; byte y; - function test() returns (bool) { - x = 1; - data.a = 2; - data.inner.a = 3; - data.inner.b = 4; + function test() public returns (bool) { + x = 0x01; + data.a = 0x02; + data.inner.a = 0x03; + data.inner.b = 0x04; data.inner.c = "1234567890"; data.inner.d = "123456789"; data.inner.e = "abcdefghij"; - data.b = 5; - data.c = 6; - y = 7; - return x == 1 && data.a == 2 && data.inner.a == 3 && data.inner.b == 4 && + data.b = 0x05; + data.c = byte(0x06); + y = 0x07; + return x == 0x01 && data.a == 0x02 && data.inner.a == 0x03 && data.inner.b == 0x04 && data.inner.c == "1234567890" && data.inner.d == "123456789" && - data.inner.e == "abcdefghij" && data.b == 5 && data.c == 6 && y == 7; + data.inner.e == "abcdefghij" && data.b == 0x05 && data.c == byte(0x06) && y == 0x07; } } )"; @@ -5729,7 +6629,7 @@ BOOST_AUTO_TEST_CASE(packed_storage_structs_delete) uint8 x; uint16 y; str data; - function test() returns (uint) { + function test() public returns (uint) { x = 1; y = 2; data.a = 2; @@ -5756,9 +6656,9 @@ BOOST_AUTO_TEST_CASE(overloaded_function_call_resolve_to_first) { char const* sourceCode = R"( contract test { - function f(uint k) returns(uint d) { return k; } - function f(uint a, uint b) returns(uint d) { return a + b; } - function g() returns(uint d) { return f(3); } + function f(uint k) public returns(uint d) { return k; } + function f(uint a, uint b) public returns(uint d) { return a + b; } + function g() public returns(uint d) { return f(3); } } )"; compileAndRun(sourceCode); @@ -5769,9 +6669,9 @@ BOOST_AUTO_TEST_CASE(overloaded_function_call_resolve_to_second) { char const* sourceCode = R"( contract test { - function f(uint a, uint b) returns(uint d) { return a + b; } - function f(uint k) returns(uint d) { return k; } - function g() returns(uint d) { return f(3, 7); } + function f(uint a, uint b) public returns(uint d) { return a + b; } + function f(uint k) public returns(uint d) { return k; } + function g() public returns(uint d) { return f(3, 7); } } )"; compileAndRun(sourceCode); @@ -5782,9 +6682,9 @@ BOOST_AUTO_TEST_CASE(overloaded_function_call_with_if_else) { char const* sourceCode = R"( contract test { - function f(uint a, uint b) returns(uint d) { return a + b; } - function f(uint k) returns(uint d) { return k; } - function g(bool flag) returns(uint d) { + function f(uint a, uint b) public returns(uint d) { return a + b; } + function f(uint k) public returns(uint d) { return k; } + function g(bool flag) public returns(uint d) { if (flag) return f(3); else @@ -5800,10 +6700,10 @@ BOOST_AUTO_TEST_CASE(overloaded_function_call_with_if_else) BOOST_AUTO_TEST_CASE(derived_overload_base_function_direct) { char const* sourceCode = R"( - contract B { function f() returns(uint) { return 10; } } + contract B { function f() public returns(uint) { return 10; } } contract C is B { - function f(uint i) returns(uint) { return 2 * i; } - function g() returns(uint) { return f(1); } + function f(uint i) public returns(uint) { return 2 * i; } + function g() public returns(uint) { return f(1); } } )"; compileAndRun(sourceCode, 0, "C"); @@ -5813,11 +6713,11 @@ BOOST_AUTO_TEST_CASE(derived_overload_base_function_direct) BOOST_AUTO_TEST_CASE(derived_overload_base_function_indirect) { char const* sourceCode = R"( - contract A { function f(uint a) returns(uint) { return 2 * a; } } - contract B { function f() returns(uint) { return 10; } } + contract A { function f(uint a) public returns(uint) { return 2 * a; } } + contract B { function f() public returns(uint) { return 10; } } contract C is A, B { - function g() returns(uint) { return f(); } - function h() returns(uint) { return f(1); } + function g() public returns(uint) { return f(); } + function h() public returns(uint) { return f(1); } } )"; compileAndRun(sourceCode, 0, "C"); @@ -5828,11 +6728,11 @@ BOOST_AUTO_TEST_CASE(derived_overload_base_function_indirect) BOOST_AUTO_TEST_CASE(super_overload) { char const* sourceCode = R"( - contract A { function f(uint a) returns(uint) { return 2 * a; } } - contract B { function f(bool b) returns(uint) { return 10; } } + contract A { function f(uint a) public returns(uint) { return 2 * a; } } + contract B { function f(bool b) public returns(uint) { return 10; } } contract C is A, B { - function g() returns(uint) { return super.f(true); } - function h() returns(uint) { return super.f(1); } + function g() public returns(uint) { return super.f(true); } + function h() public returns(uint) { return super.f(1); } } )"; compileAndRun(sourceCode, 0, "C"); @@ -5844,8 +6744,8 @@ BOOST_AUTO_TEST_CASE(gasleft_shadow_resolution) { char const* sourceCode = R"( contract C { - function gasleft() returns(uint256) { return 0; } - function f() returns(uint256) { return gasleft(); } + function gasleft() public returns(uint256) { return 0; } + function f() public returns(uint256) { return gasleft(); } } )"; compileAndRun(sourceCode, 0, "C"); @@ -5856,13 +6756,13 @@ BOOST_AUTO_TEST_CASE(bool_conversion) { char const* sourceCode = R"( contract C { - function f(bool _b) returns(uint) { + function f(bool _b) public returns(uint) { if (_b) return 1; else return 0; } - function g(bool _in) returns (bool _out) { + function g(bool _in) public returns (bool _out) { _out = _in; } } @@ -5888,7 +6788,7 @@ BOOST_AUTO_TEST_CASE(packed_storage_signed) uint8 b; int8 c; uint8 d; - function test() returns (uint x1, uint x2, uint x3, uint x4) { + function test() public returns (uint x1, uint x2, uint x3, uint x4) { a = -2; b = -uint8(a) * 2; c = a * int8(120) * int8(121); @@ -5906,15 +6806,15 @@ BOOST_AUTO_TEST_CASE(packed_storage_signed) BOOST_AUTO_TEST_CASE(external_types_in_calls) { char const* sourceCode = R"( - contract C1 { C1 public bla; function C1(C1 x) { bla = x; } } + contract C1 { C1 public bla; constructor(C1 x) public { bla = x; } } contract C { - function test() returns (C1 x, C1 y) { + function test() public returns (C1 x, C1 y) { C1 c = new C1(C1(9)); x = c.bla(); y = this.t1(C1(7)); } - function t1(C1 a) returns (C1) { return a; } - function t2() returns (C1) { return C1(9); } + function t1(C1 a) public returns (C1) { return a; } + function t2() public returns (C1) { return C1(9); } } )"; compileAndRun(sourceCode, 0, "C"); @@ -5928,18 +6828,18 @@ BOOST_AUTO_TEST_CASE(invalid_enum_compared) contract C { enum X { A, B } - function test_eq() returns (bool) { + function test_eq() public returns (bool) { X garbled; assembly { garbled := 5 } return garbled == garbled; } - function test_eq_ok() returns (bool) { + function test_eq_ok() public returns (bool) { X garbled = X.A; return garbled == garbled; } - function test_neq() returns (bool) { + function test_neq() public returns (bool) { X garbled; assembly { garbled := 5 @@ -5962,17 +6862,17 @@ BOOST_AUTO_TEST_CASE(invalid_enum_logged) enum X { A, B } event Log(X); - function test_log() returns (uint) { + function test_log() public returns (uint) { X garbled = X.A; assembly { garbled := 5 } - Log(garbled); + emit Log(garbled); return 1; } - function test_log_ok() returns (uint) { + function test_log_ok() public returns (uint) { X x = X.A; - Log(x); + emit Log(x); return 1; } } @@ -5996,7 +6896,7 @@ BOOST_AUTO_TEST_CASE(invalid_enum_stored) enum X { A, B } X public x; - function test_store() returns (uint) { + function test_store() public returns (uint) { X garbled = X.A; assembly { garbled := 5 @@ -6004,7 +6904,7 @@ BOOST_AUTO_TEST_CASE(invalid_enum_stored) x = garbled; return 1; } - function test_store_ok() returns (uint) { + function test_store_ok() public returns (uint) { x = X.A; return 1; } @@ -6024,19 +6924,19 @@ BOOST_AUTO_TEST_CASE(invalid_enum_as_external_ret) contract C { enum X { A, B } - function test_return() returns (X) { + function test_return() public returns (X) { X garbled; assembly { garbled := 5 } return garbled; } - function test_inline_assignment() returns (X _ret) { + function test_inline_assignment() public returns (X _ret) { assembly { _ret := 5 } } - function test_assignment() returns (X _ret) { + function test_assignment() public returns (X _ret) { X tmp; assembly { tmp := 5 @@ -6058,11 +6958,11 @@ BOOST_AUTO_TEST_CASE(invalid_enum_as_external_arg) contract C { enum X { A, B } - function tested (X x) returns (uint) { + function tested (X x) public returns (uint) { return 1; } - function test() returns (uint) { + function test() public returns (uint) { X garbled; assembly { @@ -6084,19 +6984,19 @@ BOOST_AUTO_TEST_CASE(proper_order_of_overwriting_of_attributes) // bug #1798 char const* sourceCode = R"( contract init { - function isOk() returns (bool) { return false; } + function isOk() public returns (bool) { return false; } bool public ok = false; } contract fix { - function isOk() returns (bool) { return true; } + function isOk() public returns (bool) { return true; } bool public ok = true; } contract init_fix is init, fix { - function checkOk() returns (bool) { return ok; } + function checkOk() public returns (bool) { return ok; } } contract fix_init is fix, init { - function checkOk() returns (bool) { return ok; } + function checkOk() public returns (bool) { return ok; } } )"; compileAndRun(sourceCode, 0, "init_fix"); @@ -6119,13 +7019,13 @@ BOOST_AUTO_TEST_CASE(struct_assign_reference_to_struct) testStruct data1; testStruct data2; testStruct data3; - function test() + constructor() public { data1.m_value = 2; } - function assign() returns (uint ret_local, uint ret_global, uint ret_global3, uint ret_global1) + function assign() public returns (uint ret_local, uint ret_global, uint ret_global3, uint ret_global1) { - testStruct x = data1; //x is a reference data1.m_value == 2 as well as x.m_value = 2 + testStruct storage x = data1; //x is a reference data1.m_value == 2 as well as x.m_value = 2 data2 = data1; // should copy data. data2.m_value == 2 ret_local = x.m_value; // = 2 @@ -6151,13 +7051,13 @@ BOOST_AUTO_TEST_CASE(struct_delete_member) uint m_value; } testStruct data1; - function test() + constructor() public { data1.m_value = 2; } - function deleteMember() returns (uint ret_value) + function deleteMember() public returns (uint ret_value) { - testStruct x = data1; //should not copy the data. data1.m_value == 2 but x.m_value = 0 + testStruct storage x = data1; //should not copy the data. data1.m_value == 2 but x.m_value = 0 x.m_value = 4; delete x.m_value; ret_value = data1.m_value; @@ -6178,11 +7078,11 @@ BOOST_AUTO_TEST_CASE(struct_delete_struct_in_mapping) } mapping (uint => testStruct) campaigns; - function test() + constructor() public { campaigns[0].m_value = 2; } - function deleteIt() returns (uint) + function deleteIt() public returns (uint) { delete campaigns[0]; return campaigns[0].m_value; @@ -6199,11 +7099,11 @@ BOOST_AUTO_TEST_CASE(evm_exceptions_out_of_band_access) contract A { uint[3] arr; bool public test = false; - function getElement(uint i) returns (uint) + function getElement(uint i) public returns (uint) { return arr[i]; } - function testIt() returns (bool) + function testIt() public returns (bool) { uint i = this.getElement(5); test = true; @@ -6221,14 +7121,14 @@ BOOST_AUTO_TEST_CASE(evm_exceptions_in_constructor_call_fail) { char const* sourceCode = R"( contract A { - function A() + constructor() public { - this.call("123"); + address(this).call("123"); } } contract B { uint public test = 1; - function testIt() + function testIt() public { A a = new A(); ++test; @@ -6247,7 +7147,7 @@ BOOST_AUTO_TEST_CASE(evm_exceptions_in_constructor_out_of_baund) contract A { uint public test = 1; uint[3] arr; - function A() + constructor() public { uint index = 5; test = arr[index]; @@ -6256,6 +7156,7 @@ BOOST_AUTO_TEST_CASE(evm_exceptions_in_constructor_out_of_baund) } )"; ABI_CHECK(compileAndRunWithoutCheck(sourceCode, 0, "A"), encodeArgs()); + BOOST_CHECK(!m_transactionSuccessful); } BOOST_AUTO_TEST_CASE(positive_integers_to_signed) @@ -6278,15 +7179,15 @@ BOOST_AUTO_TEST_CASE(failing_send) char const* sourceCode = R"( contract Helper { uint[] data; - function () { + function () external { data[9]; // trigger exception } } contract Main { - function Main() payable {} - function callHelper(address _a) returns (bool r, uint bal) { + constructor() public payable {} + function callHelper(address payable _a) public returns (bool r, uint bal) { r = !_a.send(5); - bal = this.balance; + bal = address(this).balance; } } )"; @@ -6302,14 +7203,14 @@ BOOST_AUTO_TEST_CASE(send_zero_ether) // (it previously did not because the gas stipend was not provided by the EVM) char const* sourceCode = R"( contract Receiver { - function () payable { + function () external payable { } } contract Main { - function Main() payable {} - function s() returns (bool) { - var r = new Receiver(); - return r.send(0); + constructor() public payable {} + function s() public returns (bool) { + Receiver r = new Receiver(); + return address(r).send(0); } } )"; @@ -6323,17 +7224,17 @@ BOOST_AUTO_TEST_CASE(reusing_memory) char const* sourceCode = R"( contract Helper { uint public flag; - function Helper(uint x) { + constructor(uint x) public { flag = x; } } contract Main { mapping(uint => uint) map; - function f(uint x) returns (uint) { + function f(uint x) public returns (uint) { map[x] = x; - return (new Helper(uint(keccak256(this.g(map[x]))))).flag(); + return (new Helper(uint(keccak256(abi.encodePacked(this.g(map[x])))))).flag(); } - function g(uint a) returns (uint) + function g(uint a) public returns (uint) { return map[a]; } @@ -6348,13 +7249,13 @@ BOOST_AUTO_TEST_CASE(return_string) char const* sourceCode = R"( contract Main { string public s; - function set(string _s) external { + function set(string calldata _s) external { s = _s; } - function get1() returns (string r) { + function get1() public returns (string memory r) { return s; } - function get2() returns (string r) { + function get2() public returns (string memory r) { r = s; } } @@ -6374,12 +7275,12 @@ BOOST_AUTO_TEST_CASE(return_multiple_strings_of_various_sizes) contract Main { string public s1; string public s2; - function set(string _s1, uint x, string _s2) external returns (uint) { + function set(string calldata _s1, uint x, string calldata _s2) external returns (uint) { s1 = _s1; s2 = _s2; return x; } - function get() returns (string r1, string r2) { + function get() public returns (string memory r1, string memory r2) { r1 = s1; r2 = s2; } @@ -6399,9 +7300,9 @@ BOOST_AUTO_TEST_CASE(return_multiple_strings_of_various_sizes) "ABCDEFGHIJKLMNOPQRSTUVXYZABCDEFGHIJKLMNOPQRSTUVXYZABCDEFGHIJKLMNOPQRSTUVXYZ" "ABCDEFGHIJKLMNOPQRSTUVXYZABCDEFGHIJKLMNOPQRSTUVXYZABCDEFGHIJKLMNOPQRSTUVXYZ" ); - vector<size_t> lengthes{0, 30, 32, 63, 64, 65, 210, 300}; - for (auto l1: lengthes) - for (auto l2: lengthes) + vector<size_t> lengths{0, 30, 32, 63, 64, 65, 210, 300}; + for (auto l1: lengths) + for (auto l2: lengths) { bytes dyn1 = encodeArgs(u256(l1), s1.substr(0, l1)); bytes dyn2 = encodeArgs(u256(l2), s2.substr(0, l2)); @@ -6423,7 +7324,7 @@ BOOST_AUTO_TEST_CASE(accessor_involving_strings) contract Main { struct stringData { string a; uint b; string c; } mapping(uint => stringData[]) public data; - function set(uint x, uint y, string a, uint b, string c) external returns (bool) { + function set(uint x, uint y, string calldata a, uint b, string calldata c) external returns (bool) { data[x].length = y + 1; data[x][y].a = a; data[x][y].b = b; @@ -6452,15 +7353,15 @@ BOOST_AUTO_TEST_CASE(bytes_in_function_calls) contract Main { string public s1; string public s2; - function set(string _s1, uint x, string _s2) returns (uint) { + function set(string memory _s1, uint x, string memory _s2) public returns (uint) { s1 = _s1; s2 = _s2; return x; } - function setIndirectFromMemory(string _s1, uint x, string _s2) returns (uint) { + function setIndirectFromMemory(string memory _s1, uint x, string memory _s2) public returns (uint) { return this.set(_s1, x, _s2); } - function setIndirectFromCalldata(string _s1, uint x, string _s2) external returns (uint) { + function setIndirectFromCalldata(string calldata _s1, uint x, string calldata _s2) external returns (uint) { return this.set(_s1, x, _s2); } } @@ -6468,9 +7369,9 @@ BOOST_AUTO_TEST_CASE(bytes_in_function_calls) compileAndRun(sourceCode, 0, "Main"); string s1("abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz"); string s2("ABCDEFGHIJKLMNOPQRSTUVXYZABCDEFGHIJKLMNOPQRSTUVXYZABCDEFGHIJKLMNOPQRSTUVXYZ"); - vector<size_t> lengthes{0, 31, 64, 65}; - for (auto l1: lengthes) - for (auto l2: lengthes) + vector<size_t> lengths{0, 31, 64, 65}; + for (auto l1: lengths) + for (auto l2: lengths) { bytes dyn1 = encodeArgs(u256(l1), s1.substr(0, l1)); bytes dyn2 = encodeArgs(u256(l2), s2.substr(0, l2)); @@ -6497,11 +7398,11 @@ BOOST_AUTO_TEST_CASE(return_bytes_internal) char const* sourceCode = R"( contract Main { bytes s1; - function doSet(bytes _s1) returns (bytes _r1) { + function doSet(bytes memory _s1) public returns (bytes memory _r1) { s1 = _s1; _r1 = s1; } - function set(bytes _s1) external returns (uint _r, bytes _r1) { + function set(bytes calldata _s1) external returns (uint _r, bytes memory _r1) { _r1 = doSet(_s1); _r = _r1.length; } @@ -6509,8 +7410,8 @@ BOOST_AUTO_TEST_CASE(return_bytes_internal) )"; compileAndRun(sourceCode, 0, "Main"); string s1("abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz"); - vector<size_t> lengthes{0, 31, 64, 65}; - for (auto l1: lengthes) + vector<size_t> lengths{0, 31, 64, 65}; + for (auto l1: lengths) { bytes dyn1 = encodeArgs(u256(l1), s1.substr(0, l1)); bytes args1 = encodeArgs(u256(0x20)) + dyn1; @@ -6525,15 +7426,15 @@ BOOST_AUTO_TEST_CASE(bytes_index_access_memory) { char const* sourceCode = R"( contract Main { - function f(bytes _s1, uint i1, uint i2, uint i3) returns (byte c1, byte c2, byte c3) { + function f(bytes memory _s1, uint i1, uint i2, uint i3) public returns (byte c1, byte c2, byte c3) { c1 = _s1[i1]; c2 = intern(_s1, i2); c3 = internIndirect(_s1)[i3]; } - function intern(bytes _s1, uint i) returns (byte c) { + function intern(bytes memory _s1, uint i) public returns (byte c) { return _s1[i]; } - function internIndirect(bytes _s1) returns (bytes) { + function internIndirect(bytes memory _s1) public returns (bytes memory) { return _s1; } } @@ -6554,7 +7455,7 @@ BOOST_AUTO_TEST_CASE(bytes_in_constructors_unpacker) contract Test { uint public m_x; bytes public m_s; - function Test(uint x, bytes s) { + constructor(uint x, bytes memory s) public { m_x = x; m_s = s; } @@ -6575,23 +7476,23 @@ BOOST_AUTO_TEST_CASE(bytes_in_constructors_packer) contract Base { uint public m_x; bytes m_s; - function Base(uint x, bytes s) { + constructor(uint x, bytes memory s) public { m_x = x; m_s = s; } - function part(uint i) returns (byte) { + function part(uint i) public returns (byte) { return m_s[i]; } } contract Main is Base { - function Main(bytes s, uint x) Base(x, f(s)) {} - function f(bytes s) returns (bytes) { + constructor(bytes memory s, uint x) Base(x, f(s)) public {} + function f(bytes memory s) public returns (bytes memory) { return s; } } contract Creator { - function f(uint x, bytes s) returns (uint r, byte ch) { - var c = new Main(s, x); + function f(uint x, bytes memory s) public returns (uint r, byte ch) { + Main c = new Main(s, x); r = c.m_x(); ch = c.part(x); } @@ -6614,23 +7515,23 @@ BOOST_AUTO_TEST_CASE(arrays_in_constructors) contract Base { uint public m_x; address[] m_s; - function Base(uint x, address[] s) { + constructor(uint x, address[] memory s) public { m_x = x; m_s = s; } - function part(uint i) returns (address) { + function part(uint i) public returns (address) { return m_s[i]; } } contract Main is Base { - function Main(address[] s, uint x) Base(x, f(s)) {} - function f(address[] s) returns (address[]) { + constructor(address[] memory s, uint x) Base(x, f(s)) public {} + function f(address[] memory s) public returns (address[] memory) { return s; } } contract Creator { - function f(uint x, address[] s) returns (uint r, address ch) { - var c = new Main(s, x); + function f(uint x, address[] memory s) public returns (uint r, address ch) { + Main c = new Main(s, x); r = c.m_x(); ch = c.part(x); } @@ -6653,7 +7554,7 @@ BOOST_AUTO_TEST_CASE(fixed_arrays_in_constructors) contract Creator { uint public r; address public ch; - function Creator(address[3] s, uint x) { + constructor(address[3] memory s, uint x) public { r = x; ch = s[2]; } @@ -6669,11 +7570,11 @@ BOOST_AUTO_TEST_CASE(arrays_from_and_to_storage) char const* sourceCode = R"( contract Test { uint24[] public data; - function set(uint24[] _data) returns (uint) { + function set(uint24[] memory _data) public returns (uint) { data = _data; return data.length; } - function get() returns (uint24[]) { + function get() public returns (uint24[] memory) { return data; } } @@ -6696,11 +7597,11 @@ BOOST_AUTO_TEST_CASE(arrays_complex_from_and_to_storage) char const* sourceCode = R"( contract Test { uint24[3][] public data; - function set(uint24[3][] _data) returns (uint) { + function set(uint24[3][] memory _data) public returns (uint) { data = _data; return data.length; } - function get() returns (uint24[3][]) { + function get() public returns (uint24[3][] memory) { return data; } } @@ -6722,7 +7623,7 @@ BOOST_AUTO_TEST_CASE(arrays_complex_memory_index_access) { char const* sourceCode = R"( contract Test { - function set(uint24[3][] _data, uint a, uint b) returns (uint l, uint e) { + function set(uint24[3][] memory _data, uint a, uint b) public returns (uint l, uint e) { l = _data.length; e = _data[a][b]; } @@ -6745,7 +7646,7 @@ BOOST_AUTO_TEST_CASE(bytes_memory_index_access) { char const* sourceCode = R"( contract Test { - function set(bytes _data, uint i) returns (uint l, byte c) { + function set(bytes memory _data, uint i) public returns (uint l, byte c) { l = _data.length; c = _data[i]; } @@ -6788,11 +7689,11 @@ BOOST_AUTO_TEST_CASE(storage_array_ref) contract Store is BinarySearch { uint[] data; - function add(uint v) { + function add(uint v) public { data.length++; data[data.length - 1] = v; } - function find(uint v) returns (uint) { + function find(uint v) public returns (uint) { return find(data, v); } } @@ -6821,13 +7722,13 @@ BOOST_AUTO_TEST_CASE(memory_types_initialisation) char const* sourceCode = R"( contract Test { mapping(uint=>uint) data; - function stat() returns (uint[5]) + function stat() public returns (uint[5] memory) { data[2] = 3; // make sure to use some memory } - function dyn() returns (uint[]) { stat(); } - function nested() returns (uint[3][]) { stat(); } - function nestedStat() returns (uint[3][7]) { stat(); } + function dyn() public returns (uint[] memory) { stat(); } + function nested() public returns (uint[3][] memory) { stat(); } + function nestedStat() public returns (uint[3][7] memory) { stat(); } } )"; compileAndRun(sourceCode, 0, "Test"); @@ -6842,7 +7743,7 @@ BOOST_AUTO_TEST_CASE(memory_arrays_delete) { char const* sourceCode = R"( contract Test { - function del() returns (uint24[3][4]) { + function del() public returns (uint24[3][4] memory) { uint24[3][4] memory x; for (uint24 i = 0; i < x.length; i ++) for (uint24 j = 0; j < x[i].length; j ++) @@ -6871,11 +7772,11 @@ BOOST_AUTO_TEST_CASE(memory_arrays_index_access_write) { char const* sourceCode = R"( contract Test { - function set(uint24[3][4] x) { + function set(uint24[3][4] memory x) public { x[2][2] = 1; x[3][2] = 7; } - function f() returns (uint24[3][4]){ + function f() public returns (uint24[3][4] memory){ uint24[3][4] memory data; set(data); return data; @@ -6895,12 +7796,12 @@ BOOST_AUTO_TEST_CASE(memory_arrays_dynamic_index_access_write) char const* sourceCode = R"( contract Test { uint24[3][][4] data; - function set(uint24[3][][4] x) internal returns (uint24[3][][4]) { + function set(uint24[3][][4] memory x) internal returns (uint24[3][][4] memory) { x[1][2][2] = 1; x[1][3][2] = 7; return x; } - function f() returns (uint24[3][]) { + function f() public returns (uint24[3][] memory) { data[1].length = 4; return set(data)[1]; } @@ -6920,7 +7821,7 @@ BOOST_AUTO_TEST_CASE(memory_structs_read_write) contract Test { struct S { uint8 x; uint16 y; uint z; uint8[2] a; } S[5] data; - function testInit() returns (uint8 x, uint16 y, uint z, uint8 a, bool flag) { + function testInit() public returns (uint8 x, uint16 y, uint z, uint8 a, bool flag) { S[2] memory d; x = d[0].x; y = d[0].y; @@ -6928,7 +7829,7 @@ BOOST_AUTO_TEST_CASE(memory_structs_read_write) a = d[0].a[1]; flag = true; } - function testCopyRead() returns (uint8 x, uint16 y, uint z, uint8 a) { + function testCopyRead() public returns (uint8 x, uint16 y, uint z, uint8 a) { data[2].x = 1; data[2].y = 2; data[2].z = 3; @@ -6939,7 +7840,7 @@ BOOST_AUTO_TEST_CASE(memory_structs_read_write) z = s.z; a = s.a[1]; } - function testAssign() returns (uint8 x, uint16 y, uint z, uint8 a) { + function testAssign() public returns (uint8 x, uint16 y, uint z, uint8 a) { S memory s; s.x = 1; s.y = 2; @@ -6964,18 +7865,18 @@ BOOST_AUTO_TEST_CASE(memory_structs_as_function_args) char const* sourceCode = R"( contract Test { struct S { uint8 x; uint16 y; uint z; } - function test() returns (uint x, uint y, uint z) { + function test() public returns (uint x, uint y, uint z) { S memory data = combine(1, 2, 3); x = extract(data, 0); y = extract(data, 1); z = extract(data, 2); } - function extract(S s, uint which) internal returns (uint x) { + function extract(S memory s, uint which) internal returns (uint x) { if (which == 0) return s.x; else if (which == 1) return s.y; else return s.z; } - function combine(uint8 x, uint16 y, uint z) internal returns (S s) { + function combine(uint8 x, uint16 y, uint z) internal returns (S memory s) { s.x = x; s.y = y; s.z = z; @@ -6993,20 +7894,20 @@ BOOST_AUTO_TEST_CASE(memory_structs_nested) contract Test { struct S { uint8 x; uint16 y; uint z; } struct X { uint8 x; S s; } - function test() returns (uint a, uint x, uint y, uint z) { + function test() public returns (uint a, uint x, uint y, uint z) { X memory d = combine(1, 2, 3, 4); a = extract(d, 0); x = extract(d, 1); y = extract(d, 2); z = extract(d, 3); } - function extract(X s, uint which) internal returns (uint x) { + function extract(X memory s, uint which) internal returns (uint x) { if (which == 0) return s.x; else if (which == 1) return s.s.x; else if (which == 2) return s.s.y; else return s.s.z; } - function combine(uint8 a, uint8 x, uint16 y, uint z) internal returns (X s) { + function combine(uint8 a, uint8 x, uint16 y, uint z) internal returns (X memory s) { s.x = a; s.s.x = x; s.s.y = y; @@ -7026,7 +7927,7 @@ BOOST_AUTO_TEST_CASE(memory_structs_nested_load) struct S { uint8 x; uint16 y; uint z; } struct X { uint8 x; S s; uint8[2] a; } X m_x; - function load() returns (uint a, uint x, uint y, uint z, uint a1, uint a2) { + function load() public returns (uint a, uint x, uint y, uint z, uint a1, uint a2) { m_x.x = 1; m_x.s.x = 2; m_x.s.y = 3; @@ -7041,7 +7942,7 @@ BOOST_AUTO_TEST_CASE(memory_structs_nested_load) a1 = d.a[0]; a2 = d.a[1]; } - function store() returns (uint a, uint x, uint y, uint z, uint a1, uint a2) { + function store() public returns (uint a, uint x, uint y, uint z, uint a1, uint a2) { X memory d; d.x = 1; d.s.x = 2; @@ -7073,12 +7974,12 @@ BOOST_AUTO_TEST_CASE(struct_constructor_nested) struct X { uint x1; uint x2; } struct S { uint s1; uint[3] s2; X s3; } S s; - function C() { + constructor() public { uint[3] memory s2; s2[1] = 9; s = S(1, s2, X(4, 5)); } - function get() returns (uint s1, uint[3] s2, uint x1, uint x2) + function get() public returns (uint s1, uint[3] memory s2, uint x1, uint x2) { s1 = s.s1; s2 = s.s2; @@ -7099,7 +8000,7 @@ BOOST_AUTO_TEST_CASE(struct_named_constructor) contract C { struct S { uint a; bool x; } S public s; - function C() { + constructor() public { s = S({a: 1, x: true}); } } @@ -7117,7 +8018,7 @@ BOOST_AUTO_TEST_CASE(literal_strings) string public medium; string public short; string public empty; - function f() returns (string) { + function f() public returns (string memory) { long = "0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789001234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678900123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789001234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890"; medium = "01234567890123456789012345678901234567890123456789012345678901234567890123456789"; short = "123"; @@ -7161,7 +8062,7 @@ BOOST_AUTO_TEST_CASE(memory_structs_with_mappings) contract Test { struct S { uint8 a; mapping(uint => uint) b; uint8 c; } S s; - function f() returns (uint) { + function f() public returns (uint) { S memory x; if (x.a != 0 || x.c != 0) return 1; x.a = 4; x.c = 5; @@ -7184,12 +8085,12 @@ BOOST_AUTO_TEST_CASE(string_bytes_conversion) contract Test { string s; bytes b; - function f(string _s, uint n) returns (byte) { + function f(string memory _s, uint n) public returns (byte) { b = bytes(_s); s = string(b); return bytes(s)[n]; } - function l() returns (uint) { return bytes(s).length; } + function l() public returns (uint) { return bytes(s).length; } } )"; compileAndRun(sourceCode, 0, "Test"); @@ -7208,8 +8109,8 @@ BOOST_AUTO_TEST_CASE(string_as_mapping_key) char const* sourceCode = R"( contract Test { mapping(string => uint) data; - function set(string _s, uint _v) { data[_s] = _v; } - function get(string _s) returns (uint) { return data[_s]; } + function set(string memory _s, uint _v) public { data[_s] = _v; } + function get(string memory _s) public returns (uint) { return data[_s]; } } )"; compileAndRun(sourceCode, 0, "Test"); @@ -7266,7 +8167,7 @@ BOOST_AUTO_TEST_CASE(state_variable_under_contract_name) contract Scope { uint stateVar = 42; - function getStateVar() view returns (uint stateVar) { + function getStateVar() public view returns (uint stateVar) { stateVar = Scope.stateVar; } } @@ -7281,7 +8182,7 @@ BOOST_AUTO_TEST_CASE(state_variable_local_variable_mixture) contract A { uint x = 1; uint y = 2; - function a() returns (uint x) { + function a() public returns (uint x) { x = A.y; } } @@ -7296,7 +8197,7 @@ BOOST_AUTO_TEST_CASE(inherited_function) { contract A { function f() internal returns (uint) { return 1; } } contract B is A { function f() internal returns (uint) { return 2; } - function g() returns (uint) { + function g() public returns (uint) { return A.f(); } } @@ -7311,7 +8212,7 @@ BOOST_AUTO_TEST_CASE(inherited_function_from_a_library) { library A { function f() internal returns (uint) { return 1; } } contract B { function f() internal returns (uint) { return 2; } - function g() returns (uint) { + function g() public returns (uint) { return A.f(); } } @@ -7328,7 +8229,7 @@ BOOST_AUTO_TEST_CASE(inherited_constant_state_var) uint constant x = 7; } contract B is A { - function f() returns (uint) { + function f() public returns (uint) { return A.x; } } @@ -7348,17 +8249,17 @@ BOOST_AUTO_TEST_CASE(multiple_inherited_state_vars) uint x = 9; } contract C is A, B { - function a() returns (uint) { + function a() public returns (uint) { return A.x; } - function b() returns (uint) { + function b() public returns (uint) { return B.x; } - function a_set(uint _x) returns (uint) { + function a_set(uint _x) public returns (uint) { A.x = _x; return 1; } - function b_set(uint _x) returns (uint) { + function b_set(uint _x) public returns (uint) { B.x = _x; return 1; } @@ -7381,14 +8282,14 @@ BOOST_AUTO_TEST_CASE(constant_string_literal) bytes32 constant public b = "abcdefghijklmnopq"; string constant public x = "abefghijklmnopqabcdefghijklmnopqabcdefghijklmnopqabca"; - function Test() { - var xx = x; - var bb = b; + constructor() public { + string memory xx = x; + bytes32 bb = b; } - function getB() returns (bytes32) { return b; } - function getX() returns (string) { return x; } - function getX2() returns (string r) { r = x; } - function unused() returns (uint) { + function getB() public returns (bytes32) { return b; } + function getX() public returns (string memory) { return x; } + function getX2() public returns (string memory r) { r = x; } + function unused() public returns (uint) { "unusedunusedunusedunusedunusedunusedunusedunusedunusedunusedunusedunused"; return 2; } @@ -7411,7 +8312,7 @@ BOOST_AUTO_TEST_CASE(storage_string_as_mapping_key_without_variable) char const* sourceCode = R"( contract Test { mapping(string => uint) data; - function f() returns (uint) { + function f() public returns (uint) { data["abc"] = 2; return data["abc"]; } @@ -7424,9 +8325,9 @@ BOOST_AUTO_TEST_CASE(storage_string_as_mapping_key_without_variable) BOOST_AUTO_TEST_CASE(library_call) { char const* sourceCode = R"( - library Lib { function m(uint x, uint y) returns (uint) { return x * y; } } + library Lib { function m(uint x, uint y) public returns (uint) { return x * y; } } contract Test { - function f(uint x) returns (uint) { + function f(uint x) public returns (uint) { return Lib.m(x, 9); } } @@ -7439,9 +8340,9 @@ BOOST_AUTO_TEST_CASE(library_call) BOOST_AUTO_TEST_CASE(library_function_external) { char const* sourceCode = R"( - library Lib { function m(bytes b) external pure returns (byte) { return b[2]; } } + library Lib { function m(bytes calldata b) external pure returns (byte) { return b[2]; } } contract Test { - function f(bytes b) public pure returns (byte) { + function f(bytes memory b) public pure returns (byte) { return Lib.m(b); } } @@ -7454,9 +8355,9 @@ BOOST_AUTO_TEST_CASE(library_function_external) BOOST_AUTO_TEST_CASE(library_stray_values) { char const* sourceCode = R"( - library Lib { function m(uint x, uint y) returns (uint) { return x * y; } } + library Lib { function m(uint x, uint y) public returns (uint) { return x * y; } } contract Test { - function f(uint x) returns (uint) { + function f(uint x) public returns (uint) { Lib; Lib.m; return x + 9; @@ -7473,8 +8374,8 @@ BOOST_AUTO_TEST_CASE(cross_contract_types) char const* sourceCode = R"( contract Lib { struct S {uint a; uint b; } } contract Test { - function f() returns (uint r) { - var x = Lib.S({a: 2, b: 3}); + function f() public returns (uint r) { + Lib.S memory x = Lib.S({a: 2, b: 3}); r = x.b; } } @@ -7487,11 +8388,11 @@ BOOST_AUTO_TEST_CASE(simple_throw) { char const* sourceCode = R"( contract Test { - function f(uint x) returns (uint) { + function f(uint x) public returns (uint) { if (x > 10) return x + 10; else - throw; + revert(); return 2; } } @@ -7514,22 +8415,22 @@ BOOST_AUTO_TEST_CASE(strings_in_struct) string last; } - function buggystruct(){ + constructor() public { bug = Buggy(10, 20, 30, "asdfghjkl"); } - function getFirst() returns (uint) + function getFirst() public returns (uint) { return bug.first; } - function getSecond() returns (uint) + function getSecond() public returns (uint) { return bug.second; } - function getThird() returns (uint) + function getThird() public returns (uint) { return bug.third; } - function getLast() returns (string) + function getLast() public returns (string memory) { return bug.last; } @@ -7547,7 +8448,7 @@ BOOST_AUTO_TEST_CASE(fixed_arrays_as_return_type) { char const* sourceCode = R"( contract A { - function f(uint16 input) pure returns (uint16[5] arr) + function f(uint16 input) public pure returns (uint16[5] memory arr) { arr[0] = input; arr[1] = ++input; @@ -7557,9 +8458,9 @@ BOOST_AUTO_TEST_CASE(fixed_arrays_as_return_type) } } contract B { - function f() returns (uint16[5] res, uint16[5] res2) + function f() public returns (uint16[5] memory res, uint16[5] memory res2) { - var a = new A(); + A a = new A(); res = a.f(2); res2 = a.f(1000); } @@ -7576,7 +8477,7 @@ BOOST_AUTO_TEST_CASE(internal_types_in_library) { char const* sourceCode = R"( library Lib { - function find(uint16[] storage _haystack, uint16 _needle) view returns (uint) + function find(uint16[] storage _haystack, uint16 _needle) public view returns (uint) { for (uint i = 0; i < _haystack.length; ++i) if (_haystack[i] == _needle) @@ -7586,7 +8487,7 @@ BOOST_AUTO_TEST_CASE(internal_types_in_library) } contract Test { mapping(string => uint16[]) data; - function f() returns (uint a, uint b) + function f() public returns (uint a, uint b) { data["abc"].length = 20; data["abc"][4] = 9; @@ -7601,12 +8502,168 @@ BOOST_AUTO_TEST_CASE(internal_types_in_library) ABI_CHECK(callContractFunction("f()"), encodeArgs(u256(4), u256(17))); } +BOOST_AUTO_TEST_CASE(mapping_arguments_in_library) +{ + char const* sourceCode = R"( + library Lib { + function set(mapping(uint => uint) storage m, uint key, uint value) internal + { + m[key] = value; + } + function get(mapping(uint => uint) storage m, uint key) internal view returns (uint) + { + return m[key]; + } + } + contract Test { + mapping(uint => uint) m; + function set(uint256 key, uint256 value) public returns (uint) + { + uint oldValue = Lib.get(m, key); + Lib.set(m, key, value); + return oldValue; + } + function get(uint256 key) public view returns (uint) { + return Lib.get(m, key); + } + } + )"; + compileAndRun(sourceCode, 0, "Lib"); + compileAndRun(sourceCode, 0, "Test", bytes(), map<string, Address>{{"Lib", m_contractAddress}}); + ABI_CHECK(callContractFunction("set(uint256,uint256)", u256(1), u256(42)), encodeArgs(u256(0))); + ABI_CHECK(callContractFunction("set(uint256,uint256)", u256(2), u256(84)), encodeArgs(u256(0))); + ABI_CHECK(callContractFunction("set(uint256,uint256)", u256(21), u256(7)), encodeArgs(u256(0))); + ABI_CHECK(callContractFunction("get(uint256)", u256(0)), encodeArgs(u256(0))); + ABI_CHECK(callContractFunction("get(uint256)", u256(1)), encodeArgs(u256(42))); + ABI_CHECK(callContractFunction("get(uint256)", u256(2)), encodeArgs(u256(84))); + ABI_CHECK(callContractFunction("get(uint256)", u256(21)), encodeArgs(u256(7))); + ABI_CHECK(callContractFunction("set(uint256,uint256)", u256(1), u256(21)), encodeArgs(u256(42))); + ABI_CHECK(callContractFunction("set(uint256,uint256)", u256(2), u256(42)), encodeArgs(u256(84))); + ABI_CHECK(callContractFunction("set(uint256,uint256)", u256(21), u256(14)), encodeArgs(u256(7))); + ABI_CHECK(callContractFunction("get(uint256)", u256(0)), encodeArgs(u256(0))); + ABI_CHECK(callContractFunction("get(uint256)", u256(1)), encodeArgs(u256(21))); + ABI_CHECK(callContractFunction("get(uint256)", u256(2)), encodeArgs(u256(42))); + ABI_CHECK(callContractFunction("get(uint256)", u256(21)), encodeArgs(u256(14))); +} + +BOOST_AUTO_TEST_CASE(mapping_returns_in_library) +{ + char const* sourceCode = R"( + library Lib { + function choose_mapping(mapping(uint => uint) storage a, mapping(uint => uint) storage b, bool c) internal pure returns(mapping(uint=>uint) storage) + { + return c ? a : b; + } + } + contract Test { + mapping(uint => uint) a; + mapping(uint => uint) b; + function set(bool choice, uint256 key, uint256 value) public returns (uint) + { + mapping(uint => uint) storage m = Lib.choose_mapping(a, b, choice); + uint oldValue = m[key]; + m[key] = value; + return oldValue; + } + function get(bool choice, uint256 key) public view returns (uint) { + return Lib.choose_mapping(a, b, choice)[key]; + } + function get_a(uint256 key) public view returns (uint) { + return a[key]; + } + function get_b(uint256 key) public view returns (uint) { + return b[key]; + } + } + )"; + compileAndRun(sourceCode, 0, "Lib"); + compileAndRun(sourceCode, 0, "Test", bytes(), map<string, Address>{{"Lib", m_contractAddress}}); + ABI_CHECK(callContractFunction("set(bool,uint256,uint256)", true, u256(1), u256(42)), encodeArgs(u256(0))); + ABI_CHECK(callContractFunction("set(bool,uint256,uint256)", true, u256(2), u256(84)), encodeArgs(u256(0))); + ABI_CHECK(callContractFunction("set(bool,uint256,uint256)", true, u256(21), u256(7)), encodeArgs(u256(0))); + ABI_CHECK(callContractFunction("set(bool,uint256,uint256)", false, u256(1), u256(10)), encodeArgs(u256(0))); + ABI_CHECK(callContractFunction("set(bool,uint256,uint256)", false, u256(2), u256(11)), encodeArgs(u256(0))); + ABI_CHECK(callContractFunction("set(bool,uint256,uint256)", false, u256(21), u256(12)), encodeArgs(u256(0))); + ABI_CHECK(callContractFunction("get(bool,uint256)", true, u256(0)), encodeArgs(u256(0))); + ABI_CHECK(callContractFunction("get(bool,uint256)", true, u256(1)), encodeArgs(u256(42))); + ABI_CHECK(callContractFunction("get(bool,uint256)", true, u256(2)), encodeArgs(u256(84))); + ABI_CHECK(callContractFunction("get(bool,uint256)", true, u256(21)), encodeArgs(u256(7))); + ABI_CHECK(callContractFunction("get_a(uint256)", u256(0)), encodeArgs(u256(0))); + ABI_CHECK(callContractFunction("get_a(uint256)", u256(1)), encodeArgs(u256(42))); + ABI_CHECK(callContractFunction("get_a(uint256)", u256(2)), encodeArgs(u256(84))); + ABI_CHECK(callContractFunction("get_a(uint256)", u256(21)), encodeArgs(u256(7))); + ABI_CHECK(callContractFunction("get(bool,uint256)", false, u256(0)), encodeArgs(u256(0))); + ABI_CHECK(callContractFunction("get(bool,uint256)", false, u256(1)), encodeArgs(u256(10))); + ABI_CHECK(callContractFunction("get(bool,uint256)", false, u256(2)), encodeArgs(u256(11))); + ABI_CHECK(callContractFunction("get(bool,uint256)", false, u256(21)), encodeArgs(u256(12))); + ABI_CHECK(callContractFunction("get_b(uint256)", u256(0)), encodeArgs(u256(0))); + ABI_CHECK(callContractFunction("get_b(uint256)", u256(1)), encodeArgs(u256(10))); + ABI_CHECK(callContractFunction("get_b(uint256)", u256(2)), encodeArgs(u256(11))); + ABI_CHECK(callContractFunction("get_b(uint256)", u256(21)), encodeArgs(u256(12))); + ABI_CHECK(callContractFunction("set(bool,uint256,uint256)", true, u256(1), u256(21)), encodeArgs(u256(42))); + ABI_CHECK(callContractFunction("set(bool,uint256,uint256)", true, u256(2), u256(42)), encodeArgs(u256(84))); + ABI_CHECK(callContractFunction("set(bool,uint256,uint256)", true, u256(21), u256(14)), encodeArgs(u256(7))); + ABI_CHECK(callContractFunction("set(bool,uint256,uint256)", false, u256(1), u256(30)), encodeArgs(u256(10))); + ABI_CHECK(callContractFunction("set(bool,uint256,uint256)", false, u256(2), u256(31)), encodeArgs(u256(11))); + ABI_CHECK(callContractFunction("set(bool,uint256,uint256)", false, u256(21), u256(32)), encodeArgs(u256(12))); + ABI_CHECK(callContractFunction("get_a(uint256)", u256(0)), encodeArgs(u256(0))); + ABI_CHECK(callContractFunction("get_a(uint256)", u256(1)), encodeArgs(u256(21))); + ABI_CHECK(callContractFunction("get_a(uint256)", u256(2)), encodeArgs(u256(42))); + ABI_CHECK(callContractFunction("get_a(uint256)", u256(21)), encodeArgs(u256(14))); + ABI_CHECK(callContractFunction("get(bool,uint256)", true, u256(0)), encodeArgs(u256(0))); + ABI_CHECK(callContractFunction("get(bool,uint256)", true, u256(1)), encodeArgs(u256(21))); + ABI_CHECK(callContractFunction("get(bool,uint256)", true, u256(2)), encodeArgs(u256(42))); + ABI_CHECK(callContractFunction("get(bool,uint256)", true, u256(21)), encodeArgs(u256(14))); + ABI_CHECK(callContractFunction("get_b(uint256)", u256(0)), encodeArgs(u256(0))); + ABI_CHECK(callContractFunction("get_b(uint256)", u256(1)), encodeArgs(u256(30))); + ABI_CHECK(callContractFunction("get_b(uint256)", u256(2)), encodeArgs(u256(31))); + ABI_CHECK(callContractFunction("get_b(uint256)", u256(21)), encodeArgs(u256(32))); + ABI_CHECK(callContractFunction("get(bool,uint256)", false, u256(0)), encodeArgs(u256(0))); + ABI_CHECK(callContractFunction("get(bool,uint256)", false, u256(1)), encodeArgs(u256(30))); + ABI_CHECK(callContractFunction("get(bool,uint256)", false, u256(2)), encodeArgs(u256(31))); + ABI_CHECK(callContractFunction("get(bool,uint256)", false, u256(21)), encodeArgs(u256(32))); +} + +BOOST_AUTO_TEST_CASE(mapping_returns_in_library_named) +{ + char const* sourceCode = R"( + library Lib { + function f(mapping(uint => uint) storage a, mapping(uint => uint) storage b) internal returns(mapping(uint=>uint) storage r) + { + r = a; + r[1] = 42; + r = b; + r[1] = 21; + } + } + contract Test { + mapping(uint => uint) a; + mapping(uint => uint) b; + function f() public returns (uint, uint, uint, uint, uint, uint) + { + Lib.f(a, b)[2] = 84; + return (a[0], a[1], a[2], b[0], b[1], b[2]); + } + function g() public returns (uint, uint, uint, uint, uint, uint) + { + mapping(uint => uint) storage m = Lib.f(a, b); + m[2] = 17; + return (a[0], a[1], a[2], b[0], b[1], b[2]); + } + } + )"; + compileAndRun(sourceCode, 0, "Lib"); + compileAndRun(sourceCode, 0, "Test", bytes(), map<string, Address>{{"Lib", m_contractAddress}}); + ABI_CHECK(callContractFunction("f()"), encodeArgs(u256(0), u256(42), u256(0), u256(0), u256(21), u256(84))); + ABI_CHECK(callContractFunction("g()"), encodeArgs(u256(0), u256(42), u256(0), u256(0), u256(21), u256(17))); +} + BOOST_AUTO_TEST_CASE(using_library_structs) { char const* sourceCode = R"( library Lib { struct Data { uint a; uint[] b; } - function set(Data storage _s) + function set(Data storage _s) public { _s.a = 7; _s.b.length = 20; @@ -7615,7 +8672,7 @@ BOOST_AUTO_TEST_CASE(using_library_structs) } contract Test { mapping(string => Lib.Data) data; - function f() returns (uint a, uint b) + function f() public returns (uint a, uint b) { Lib.set(data["abc"]); a = data["abc"].a; @@ -7639,7 +8696,7 @@ BOOST_AUTO_TEST_CASE(library_struct_as_an_expression) } contract Tsra { - function f() returns(uint) { + function f() public returns(uint) { Arst.Foo; return 1; } @@ -7660,7 +8717,7 @@ BOOST_AUTO_TEST_CASE(library_enum_as_an_expression) } contract Tsra { - function f() returns(uint) { + function f() public returns(uint) { Arst.Foo; return 1; } @@ -7678,7 +8735,7 @@ BOOST_AUTO_TEST_CASE(short_strings) contract A { bytes public data1 = "123"; bytes data2; - function lengthChange() returns (uint) + function lengthChange() public returns (uint) { // store constant in short and long string data1 = "123"; @@ -7701,18 +8758,18 @@ BOOST_AUTO_TEST_CASE(short_strings) if (data1[0] != "1") return 10; if (data1[4] != "4") return 11; for (uint i = 0; i < data1.length; i ++) - data1[i] = byte(i * 3); - if (data1[4] != 4 * 3) return 12; - if (data1[67] != 67 * 3) return 13; + data1[i] = byte(uint8(i * 3)); + if (uint8(data1[4]) != 4 * 3) return 12; + if (uint8(data1[67]) != 67 * 3) return 13; // change length: long -> short data1.length = 22; if (data1.length != 22) return 14; - if (data1[21] != byte(21 * 3)) return 15; - if (data1[2] != 2 * 3) return 16; + if (uint8(data1[21]) != 21 * 3) return 15; + if (uint8(data1[2]) != 2 * 3) return 16; // change length: short -> shorter data1.length = 19; if (data1.length != 19) return 17; - if (data1[7] != byte(7 * 3)) return 18; + if (uint8(data1[7]) != 7 * 3) return 18; // and now again to original size data1.length = 22; if (data1.length != 22) return 19; @@ -7720,7 +8777,7 @@ BOOST_AUTO_TEST_CASE(short_strings) data1.length = 0; data2.length = 0; } - function copy() returns (uint) { + function copy() public returns (uint) { bytes memory x = "123"; bytes memory y = "012345678901234567890123456789012345678901234567890123456789"; bytes memory z = "1234567"; @@ -7753,7 +8810,7 @@ BOOST_AUTO_TEST_CASE(short_strings) data1 = ""; data2 = ""; } - function deleteElements() returns (uint) { + function deleteElements() public returns (uint) { data1 = "01234"; delete data1[2]; if (data1[2] != 0) return 1; @@ -7789,7 +8846,7 @@ BOOST_AUTO_TEST_CASE(calldata_offset) { address[] _arr; string public last = "nd"; - function CB(address[] guardians) + constructor(address[] memory guardians) public { _arr = guardians; } @@ -7802,9 +8859,9 @@ BOOST_AUTO_TEST_CASE(calldata_offset) BOOST_AUTO_TEST_CASE(contract_binary_dependencies) { char const* sourceCode = R"( - contract A { function f() { new B(); } } - contract B { function f() { } } - contract C { function f() { new B(); } } + contract A { function f() public { new B(); } } + contract B { function f() public { } } + contract C { function f() public { new B(); } } )"; compileAndRun(sourceCode); } @@ -7814,11 +8871,11 @@ BOOST_AUTO_TEST_CASE(reject_ether_sent_to_library) char const* sourceCode = R"( library lib {} contract c { - function c() payable {} - function f(address x) returns (bool) { + constructor() public payable {} + function f(address payable x) public returns (bool) { return x.send(1); } - function () payable {} + function () external payable {} } )"; compileAndRun(sourceCode, 0, "lib"); @@ -7838,20 +8895,39 @@ BOOST_AUTO_TEST_CASE(multi_variable_declaration) { char const* sourceCode = R"( contract C { - function g() returns (uint a, uint b, uint c) { + function g() public returns (uint a, uint b, uint c) { a = 1; b = 2; c = 3; } - function f() returns (bool) { - var (x, y, z) = g(); + function h() public returns (uint a, uint b, uint c, uint d) { + a = 1; b = 2; c = 3; d = 4; + } + function f1() public returns (bool) { + (uint x, uint y, uint z) = g(); if (x != 1 || y != 2 || z != 3) return false; - var (, a,) = g(); + (, uint a,) = g(); if (a != 2) return false; - var (b,) = g(); + (uint b, , ) = g(); if (b != 1) return false; - var (,c) = g(); + (, , uint c) = g(); if (c != 3) return false; return true; } + function f2() public returns (bool) { + (uint a1, , uint a3, ) = h(); + if (a1 != 1 || a3 != 3) return false; + (uint b1, uint b2, , ) = h(); + if (b1 != 1 || b2 != 2) return false; + (, uint c2, uint c3, ) = h(); + if (c2 != 2 || c3 != 3) return false; + (, , uint d3, uint d4) = h(); + if (d3 != 3 || d4 != 4) return false; + (uint e1, , uint e3, uint e4) = h(); + if (e1 != 1 || e3 != 3 || e4 != 4) return false; + return true; + } + function f() public returns (bool) { + return f1() && f2(); + } } )"; compileAndRun(sourceCode); @@ -7868,7 +8944,7 @@ BOOST_AUTO_TEST_CASE(typed_multi_variable_declaration) s.x = 7; return (1, s, 2); } - function f() returns (bool) { + function f() public returns (bool) { (uint x1, S storage y1, uint z1) = g(); if (x1 != 1 || y1.x != 7 || z1 != 2) return false; (, S storage y2,) = g(); @@ -7890,24 +8966,25 @@ BOOST_AUTO_TEST_CASE(tuples) char const* sourceCode = R"( contract C { uint[] data; + uint[] m_c; function g() internal returns (uint a, uint b, uint[] storage c) { return (1, 2, data); } function h() external returns (uint a, uint b) { return (5, 6); } - function f() returns (uint) { + function f() public returns (uint) { data.length = 1; data[0] = 3; uint a; uint b; (a, b) = this.h(); if (a != 5 || b != 6) return 1; - uint[] storage c; + uint[] storage c = m_c; (a, b, c) = g(); if (a != 1 || b != 2 || c[0] != 3) return 2; (a, b) = (b, a); if (a != 2 || b != 1) return 3; - (a, , b, ) = (8, 9, 10, 11, 12); + (a, , b, , ) = (8, 9, 10, 11, 12); if (a != 8 || b != 10) return 4; } } @@ -7920,14 +8997,14 @@ BOOST_AUTO_TEST_CASE(string_tuples) { char const* sourceCode = R"( contract C { - function f() returns (string, uint) { + function f() public returns (string memory, uint) { return ("abc", 8); } - function g() returns (string, string) { + function g() public returns (string memory, string memory) { return (h(), "def"); } - function h() returns (string) { - return ("abc",); + function h() public returns (string memory) { + return ("abc"); } } )"; @@ -7940,7 +9017,7 @@ BOOST_AUTO_TEST_CASE(decayed_tuple) { char const* sourceCode = R"( contract C { - function f() returns (uint) { + function f() public returns (uint) { uint x = 1; (x) = 2; return x; @@ -7955,7 +9032,7 @@ BOOST_AUTO_TEST_CASE(inline_tuple_with_rational_numbers) { char const* sourceCode = R"( contract c { - function f() returns (int8) { + function f() public returns (int8) { int8[5] memory foo3 = [int8(1), -1, 0, 0, 0]; return foo3[0]; } @@ -7973,13 +9050,13 @@ BOOST_AUTO_TEST_CASE(destructuring_assignment) bytes data; uint[] y; uint[] arrayData; - function returnsArray() returns (uint[]) { + function returnsArray() public returns (uint[] memory) { arrayData.length = 9; arrayData[2] = 5; arrayData[7] = 4; return arrayData; } - function f(bytes s) returns (uint) { + function f(bytes memory s) public returns (uint) { uint loc; uint[] memory memArray; (loc, x, y, data, arrayData[3]) = (8, 4, returnsArray(), s, 2); @@ -7995,7 +9072,7 @@ BOOST_AUTO_TEST_CASE(destructuring_assignment) if (loc != 3) return 9; if (memArray.length != arrayData.length) return 10; bytes memory memBytes; - (x, memBytes, y[2], ) = (456, s, 789, 101112, 131415); + (x, memBytes, y[2], , ) = (456, s, 789, 101112, 131415); if (x != 456 || memBytes.length != s.length || y[2] != 789) return 11; } } @@ -8004,37 +9081,12 @@ BOOST_AUTO_TEST_CASE(destructuring_assignment) ABI_CHECK(callContractFunction("f(bytes)", u256(0x20), u256(5), string("abcde")), encodeArgs(u256(0))); } -BOOST_AUTO_TEST_CASE(destructuring_assignment_wildcard) -{ - char const* sourceCode = R"( - contract C { - function f() returns (uint) { - uint a; - uint b; - uint c; - (a,) = (1,); - if (a != 1) return 1; - (,b) = (2,3,4); - if (b != 4) return 2; - (, c,) = (5,6,7); - if (c != 6) return 3; - (a, b,) = (11, 12, 13); - if (a != 11 || b != 12) return 4; - (, a, b) = (11, 12, 13); - if (a != 12 || b != 13) return 5; - } - } - )"; - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("f()"), encodeArgs(u256(0))); -} - BOOST_AUTO_TEST_CASE(lone_struct_array_type) { char const* sourceCode = R"( contract C { struct s { uint a; uint b;} - function f() returns (uint) { + function f() public returns (uint) { s[7][]; // This is only the type, should not have any effect return 3; } @@ -8049,12 +9101,12 @@ BOOST_AUTO_TEST_CASE(create_memory_array) char const* sourceCode = R"( contract C { struct S { uint[2] a; bytes b; } - function f() returns (byte, uint, uint, byte) { - var x = new bytes(200); + function f() public returns (byte, uint, uint, byte) { + bytes memory x = new bytes(200); x[199] = 'A'; - var y = new uint[2][](300); + uint[2][] memory y = new uint[2][](300); y[203][1] = 8; - var z = new S[](180); + S[] memory z = new S[](180); z[170].a[1] = 4; z[170].b = new bytes(102); z[170].b[99] = 'B'; @@ -8072,7 +9124,7 @@ BOOST_AUTO_TEST_CASE(create_memory_array_allocation_size) // multiple of 32 char const* sourceCode = R"( contract C { - function f() pure returns (uint d1, uint d2, uint d3) { + function f() public pure returns (uint d1, uint d2, uint d3, uint memsize) { bytes memory b1 = new bytes(31); bytes memory b2 = new bytes(32); bytes memory b3 = new bytes(256); @@ -8081,12 +9133,13 @@ BOOST_AUTO_TEST_CASE(create_memory_array_allocation_size) d1 := sub(b2, b1) d2 := sub(b3, b2) d3 := sub(b4, b3) + memsize := msize() } } } )"; compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("f()"), encodeArgs(0x40, 0x40, 0x20 + 256)); + ABI_CHECK(callContractFunction("f()"), encodeArgs(0x40, 0x40, 0x20 + 256, 0x260)); } BOOST_AUTO_TEST_CASE(memory_arrays_of_various_sizes) @@ -8094,7 +9147,7 @@ BOOST_AUTO_TEST_CASE(memory_arrays_of_various_sizes) // Computes binomial coefficients the chinese way char const* sourceCode = R"( contract C { - function f(uint n, uint k) returns (uint) { + function f(uint n, uint k) public returns (uint) { uint[][] memory rows = new uint[][](n + 1); for (uint i = 1; i <= n; i++) { rows[i] = new uint[](i); @@ -8115,7 +9168,7 @@ BOOST_AUTO_TEST_CASE(create_multiple_dynamic_arrays) { char const* sourceCode = R"( contract C { - function f() returns (uint) { + function f() public returns (uint) { uint[][] memory x = new uint[][](42); assert(x[0].length == 0); x[0] = new uint[](1); @@ -8150,7 +9203,7 @@ BOOST_AUTO_TEST_CASE(memory_overwrite) { char const* sourceCode = R"( contract C { - function f() returns (bytes x) { + function f() public returns (bytes memory x) { x = "12345"; x[3] = 0x61; x[0] = 0x62; @@ -8165,7 +9218,7 @@ BOOST_AUTO_TEST_CASE(addmod_mulmod) { char const* sourceCode = R"( contract C { - function test() returns (uint) { + function test() public returns (uint) { // Note that this only works because computation on literals is done using // unbounded integers. if ((2**255 + 2**255) % 7 != addmod(2**255, 2**255, 7)) @@ -8184,15 +9237,15 @@ BOOST_AUTO_TEST_CASE(addmod_mulmod_zero) { char const* sourceCode = R"( contract C { - function f(uint d) pure returns (uint) { + function f(uint d) public pure returns (uint) { addmod(1, 2, d); return 2; } - function g(uint d) pure returns (uint) { + function g(uint d) public pure returns (uint) { mulmod(1, 2, d); return 2; } - function h() pure returns (uint) { + function h() public pure returns (uint) { mulmod(0, 1, 2); mulmod(1, 0, 2); addmod(0, 1, 2); @@ -8211,10 +9264,10 @@ BOOST_AUTO_TEST_CASE(divisiod_by_zero) { char const* sourceCode = R"( contract C { - function div(uint a, uint b) returns (uint) { + function div(uint a, uint b) public returns (uint) { return a / b; } - function mod(uint a, uint b) returns (uint) { + function mod(uint a, uint b) public returns (uint) { return a % b; } } @@ -8235,7 +9288,7 @@ BOOST_AUTO_TEST_CASE(string_allocation_bug) { struct s { uint16 x; uint16 y; string a; string b;} s[2] public p; - function Sample() { + constructor() public { s memory m; m.x = 0xbbbb; m.y = 0xcccc; @@ -8261,10 +9314,10 @@ BOOST_AUTO_TEST_CASE(string_allocation_bug) BOOST_AUTO_TEST_CASE(using_for_function_on_int) { char const* sourceCode = R"( - library D { function double(uint self) returns (uint) { return 2*self; } } + library D { function double(uint self) public returns (uint) { return 2*self; } } contract C { using D for uint; - function f(uint a) returns (uint) { + function f(uint a) public returns (uint) { return a.double(); } } @@ -8277,11 +9330,11 @@ BOOST_AUTO_TEST_CASE(using_for_function_on_int) BOOST_AUTO_TEST_CASE(using_for_function_on_struct) { char const* sourceCode = R"( - library D { struct s { uint a; } function mul(s storage self, uint x) returns (uint) { return self.a *= x; } } + library D { struct s { uint a; } function mul(s storage self, uint x) public returns (uint) { return self.a *= x; } } contract C { using D for D.s; D.s public x; - function f(uint a) returns (uint) { + function f(uint a) public returns (uint) { x.a = 3; return x.mul(a); } @@ -8298,13 +9351,13 @@ BOOST_AUTO_TEST_CASE(using_for_overload) char const* sourceCode = R"( library D { struct s { uint a; } - function mul(s storage self, uint x) returns (uint) { return self.a *= x; } - function mul(s storage self, bytes32 x) returns (bytes32) { } + function mul(s storage self, uint x) public returns (uint) { return self.a *= x; } + function mul(s storage self, bytes32 x) public returns (bytes32) { } } contract C { using D for D.s; D.s public x; - function f(uint a) returns (uint) { + function f(uint a) public returns (uint) { x.a = 6; return x.mul(a); } @@ -8319,11 +9372,11 @@ BOOST_AUTO_TEST_CASE(using_for_overload) BOOST_AUTO_TEST_CASE(using_for_by_name) { char const* sourceCode = R"( - library D { struct s { uint a; } function mul(s storage self, uint x) returns (uint) { return self.a *= x; } } + library D { struct s { uint a; } function mul(s storage self, uint x) public returns (uint) { return self.a *= x; } } contract C { using D for D.s; D.s public x; - function f(uint a) returns (uint) { + function f(uint a) public returns (uint) { x.a = 6; return x.mul({x: a}); } @@ -8338,14 +9391,13 @@ BOOST_AUTO_TEST_CASE(using_for_by_name) BOOST_AUTO_TEST_CASE(bound_function_in_var) { char const* sourceCode = R"( - library D { struct s { uint a; } function mul(s storage self, uint x) returns (uint) { return self.a *= x; } } + library D { struct s { uint a; } function mul(s storage self, uint x) public returns (uint) { return self.a *= x; } } contract C { using D for D.s; D.s public x; - function f(uint a) returns (uint) { + function f(uint a) public returns (uint) { x.a = 6; - var g = x.mul; - return g({x: a}); + return (x.mul)({x: a}); } } )"; @@ -8358,15 +9410,15 @@ BOOST_AUTO_TEST_CASE(bound_function_in_var) BOOST_AUTO_TEST_CASE(bound_function_to_string) { char const* sourceCode = R"( - library D { function length(string memory self) returns (uint) { return bytes(self).length; } } + library D { function length(string memory self) public returns (uint) { return bytes(self).length; } } contract C { using D for string; string x; - function f() returns (uint) { + function f() public returns (uint) { x = "abc"; return x.length(); } - function g() returns (uint) { + function g() public returns (uint) { string memory s = "abc"; return s.length(); } @@ -8383,7 +9435,7 @@ BOOST_AUTO_TEST_CASE(inline_array_storage_to_memory_conversion_strings) char const* sourceCode = R"( contract C { string s = "doh"; - function f() returns (string, string) { + function f() public returns (string memory, string memory) { string memory t = "ray"; string[3] memory x = [s, t, "mi"]; return (x[1], x[2]); @@ -8398,7 +9450,7 @@ BOOST_AUTO_TEST_CASE(inline_array_strings_from_document) { char const* sourceCode = R"( contract C { - function f(uint i) returns (string) { + function f(uint i) public returns (string memory) { string[4] memory x = ["This", "is", "an", "array"]; return (x[i]); } @@ -8415,7 +9467,7 @@ BOOST_AUTO_TEST_CASE(inline_array_storage_to_memory_conversion_ints) { char const* sourceCode = R"( contract C { - function f() returns (uint x, uint y) { + function f() public returns (uint x, uint y) { x = 3; y = 6; uint[2] memory z = [x, y]; @@ -8431,7 +9483,7 @@ BOOST_AUTO_TEST_CASE(inline_array_index_access_ints) { char const* sourceCode = R"( contract C { - function f() returns (uint) { + function f() public returns (uint) { return ([1, 2, 3, 4][2]); } } @@ -8445,10 +9497,10 @@ BOOST_AUTO_TEST_CASE(inline_array_index_access_strings) char const* sourceCode = R"( contract C { string public tester; - function f() returns (string) { + function f() public returns (string memory) { return (["abc", "def", "g"][0]); } - function test() { + function test() public { tester = f(); } } @@ -8462,15 +9514,15 @@ BOOST_AUTO_TEST_CASE(inline_array_return) { char const* sourceCode = R"( contract C { - uint8[] tester; - function f() returns (uint8[5]) { + uint8[] tester; + function f() public returns (uint8[5] memory) { return ([1,2,3,4,5]); } - function test() returns (uint8, uint8, uint8, uint8, uint8) { - tester = f(); + function test() public returns (uint8, uint8, uint8, uint8, uint8) { + tester = f(); return (tester[0], tester[1], tester[2], tester[3], tester[4]); } - + } )"; compileAndRun(sourceCode, 0, "C"); @@ -8482,7 +9534,7 @@ BOOST_AUTO_TEST_CASE(inline_array_singleton) // This caused a failure since the type was not converted to its mobile type. char const* sourceCode = R"( contract C { - function f() returns (uint) { + function f() public returns (uint) { return [4][0]; } } @@ -8494,13 +9546,13 @@ BOOST_AUTO_TEST_CASE(inline_array_singleton) BOOST_AUTO_TEST_CASE(inline_long_string_return) { char const* sourceCode = R"( - contract C { - function f() returns (string) { + contract C { + function f() public returns (string memory) { return (["somethingShort", "0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789001234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678900123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789001234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890"][1]); } } )"; - + string strLong = "0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789001234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678900123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789001234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890"; compileAndRun(sourceCode, 0, "C"); ABI_CHECK(callContractFunction("f()"), encodeDyn(strLong)); @@ -8511,13 +9563,13 @@ BOOST_AUTO_TEST_CASE(fixed_bytes_index_access) char const* sourceCode = R"( contract C { bytes16[] public data; - function f(bytes32 x) returns (byte) { + function f(bytes32 x) public returns (byte) { return x[2]; } - function g(bytes32 x) returns (uint) { + function g(bytes32 x) public returns (uint) { data = [x[0], x[1], x[2]]; data[0] = "12345"; - return uint(data[0][4]); + return uint(uint8(data[0][4])); } } )"; @@ -8532,8 +9584,8 @@ BOOST_AUTO_TEST_CASE(fixed_bytes_length_access) char const* sourceCode = R"( contract C { byte a; - function f(bytes32 x) returns (uint, uint, uint) { - return (x.length, bytes16(2).length, a.length + 7); + function f(bytes32 x) public returns (uint, uint, uint) { + return (x.length, bytes16(uint128(2)).length, a.length + 7); } } )"; @@ -8545,7 +9597,7 @@ BOOST_AUTO_TEST_CASE(inline_assembly_write_to_stack) { char const* sourceCode = R"( contract C { - function f() returns (uint r, bytes32 r2) { + function f() public returns (uint r, bytes32 r2) { assembly { r := 7 r2 := "abcdef" } } } @@ -8558,7 +9610,7 @@ BOOST_AUTO_TEST_CASE(inline_assembly_read_and_write_stack) { char const* sourceCode = R"( contract C { - function f() returns (uint r) { + function f() public returns (uint r) { for (uint x = 0; x < 10; ++x) assembly { r := add(r, x) } } @@ -8572,10 +9624,10 @@ BOOST_AUTO_TEST_CASE(inline_assembly_memory_access) { char const* sourceCode = R"( contract C { - function test() returns (bytes) { + function test() public returns (bytes memory) { bytes memory x = new bytes(5); for (uint i = 0; i < x.length; ++i) - x[i] = byte(i + 1); + x[i] = byte(uint8(i + 1)); assembly { mstore(add(x, 32), "12345678901234567890123456789012") } return x; } @@ -8592,7 +9644,7 @@ BOOST_AUTO_TEST_CASE(inline_assembly_storage_access) uint16 x; uint16 public y; uint public z; - function f() returns (bool) { + function f() public returns (bool) { uint off1; uint off2; assembly { @@ -8618,7 +9670,7 @@ BOOST_AUTO_TEST_CASE(inline_assembly_storage_access_inside_function) uint16 x; uint16 public y; uint public z; - function f() returns (bool) { + function f() public returns (bool) { uint off1; uint off2; assembly { @@ -8646,8 +9698,8 @@ BOOST_AUTO_TEST_CASE(inline_assembly_storage_access_via_pointer) uint public separator; Data public a; uint public separator2; - function f() returns (bool) { - Data x = a; + function f() public returns (bool) { + Data storage x = a; uint off; assembly { sstore(x_slot, 7) @@ -8665,57 +9717,11 @@ BOOST_AUTO_TEST_CASE(inline_assembly_storage_access_via_pointer) ABI_CHECK(callContractFunction("separator2()"), encodeArgs(u256(0))); } -BOOST_AUTO_TEST_CASE(inline_assembly_jumps) -{ - char const* sourceCode = R"( - contract C { - function f() { - assembly { - let n := calldataload(4) - let a := 1 - let b := a - loop: - jumpi(loopend, eq(n, 0)) - a add swap1 - n := sub(n, 1) - jump(loop) - loopend: - mstore(0, a) - return(0, 0x20) - } - } - } - )"; - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("f()", u256(5)), encodeArgs(u256(13))); - ABI_CHECK(callContractFunction("f()", u256(7)), encodeArgs(u256(34))); -} - -BOOST_AUTO_TEST_CASE(inline_assembly_function_access) -{ - char const* sourceCode = R"( - contract C { - uint public x; - function g(uint y) { x = 2 * y; assembly { stop } } - function f(uint _x) { - assembly { - _x - jump(g) - pop - } - } - } - )"; - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("f(uint256)", u256(5)), encodeArgs()); - ABI_CHECK(callContractFunction("x()"), encodeArgs(u256(10))); -} - BOOST_AUTO_TEST_CASE(inline_assembly_function_call) { char const* sourceCode = R"( contract C { - function f() { + function f() public { assembly { function asmfun(a, b, c) -> x, y, z { x := a @@ -8739,7 +9745,7 @@ BOOST_AUTO_TEST_CASE(inline_assembly_function_call_assignment) { char const* sourceCode = R"( contract C { - function f() { + function f() public { assembly { let a1, b1, c1 function asmfun(a, b, c) -> x, y, z { @@ -8764,7 +9770,7 @@ BOOST_AUTO_TEST_CASE(inline_assembly_function_call2) { char const* sourceCode = R"( contract C { - function f() { + function f() public { assembly { let d := 0x10 function asmfun(a, b, c) -> x, y, z { @@ -8790,7 +9796,7 @@ BOOST_AUTO_TEST_CASE(inline_assembly_embedded_function_call) { char const* sourceCode = R"( contract C { - function f() { + function f() public { assembly { let d := 0x10 function asmfun(a, b, c) -> x, y, z { @@ -8817,7 +9823,7 @@ BOOST_AUTO_TEST_CASE(inline_assembly_if) { char const* sourceCode = R"( contract C { - function f(uint a) returns (uint b) { + function f(uint a) public returns (uint b) { assembly { if gt(a, 1) { b := 2 } } @@ -8835,7 +9841,7 @@ BOOST_AUTO_TEST_CASE(inline_assembly_switch) { char const* sourceCode = R"( contract C { - function f(uint a) returns (uint b) { + function f(uint a) public returns (uint b) { assembly { switch a case 1 { b := 8 } @@ -8856,7 +9862,7 @@ BOOST_AUTO_TEST_CASE(inline_assembly_recursion) { char const* sourceCode = R"( contract C { - function f(uint a) returns (uint b) { + function f(uint a) public returns (uint b) { assembly { function fac(n) -> nf { switch n @@ -8881,7 +9887,7 @@ BOOST_AUTO_TEST_CASE(inline_assembly_for) { char const* sourceCode = R"( contract C { - function f(uint a) returns (uint b) { + function f(uint a) public returns (uint b) { assembly { function fac(n) -> nf { nf := 1 @@ -8907,7 +9913,7 @@ BOOST_AUTO_TEST_CASE(inline_assembly_for2) char const* sourceCode = R"( contract C { uint st; - function f(uint a) returns (uint b, uint c, uint d) { + function f(uint a) public returns (uint b, uint c, uint d) { st = 0; assembly { function sideeffect(r) -> x { sstore(0, add(sload(0), r)) x := 1} @@ -8931,7 +9937,7 @@ BOOST_AUTO_TEST_CASE(index_access_with_type_conversion) // Test for a bug where higher order bits cleanup was not done for array index access. char const* sourceCode = R"( contract C { - function f(uint x) returns (uint[256] r){ + function f(uint x) public returns (uint[256] memory r){ r[uint8(x)] = 2; } } @@ -8949,7 +9955,7 @@ BOOST_AUTO_TEST_CASE(delete_on_array_of_structs) contract C { struct S { uint x; uint[] y; } S[] data; - function f() returns (bool) { + function f() public returns (bool) { data.length = 2; data[0].x = 2**200; data[1].x = 2**200; @@ -8971,12 +9977,12 @@ BOOST_AUTO_TEST_CASE(internal_library_function) // and retain the same memory context (i.e. are pulled into the caller's code) char const* sourceCode = R"( library L { - function f(uint[] _data) internal { + function f(uint[] memory _data) internal { _data[3] = 2; } } contract C { - function f() returns (uint) { + function f() public returns (uint) { uint[] memory x = new uint[](7); x[3] = 8; L.f(x); @@ -8996,15 +10002,15 @@ BOOST_AUTO_TEST_CASE(internal_library_function_calling_private) // also has to be pulled into the caller's code) char const* sourceCode = R"( library L { - function g(uint[] _data) private { + function g(uint[] memory _data) private { _data[3] = 2; } - function f(uint[] _data) internal { + function f(uint[] memory _data) internal { g(_data); } } contract C { - function f() returns (uint) { + function f() public returns (uint) { uint[] memory x = new uint[](7); x[3] = 8; L.f(x); @@ -9022,13 +10028,13 @@ BOOST_AUTO_TEST_CASE(internal_library_function_bound) char const* sourceCode = R"( library L { struct S { uint[] data; } - function f(S _s) internal { + function f(S memory _s) internal { _s.data[3] = 2; } } contract C { using L for L.S; - function f() returns (uint) { + function f() public returns (uint) { L.S memory x; x.data = new uint[](7); x.data[3] = 8; @@ -9047,14 +10053,14 @@ BOOST_AUTO_TEST_CASE(internal_library_function_return_var_size) char const* sourceCode = R"( library L { struct S { uint[] data; } - function f(S _s) internal returns (uint[]) { + function f(S memory _s) internal returns (uint[] memory) { _s.data[3] = 2; return _s.data; } } contract C { using L for L.S; - function f() returns (uint) { + function f() public returns (uint) { L.S memory x; x.data = new uint[](7); x.data[3] = 8; @@ -9073,10 +10079,10 @@ BOOST_AUTO_TEST_CASE(iszero_bnot_correct) // "iszero" and "not". char const* sourceCode = R"( contract C { - function f() returns (bool) { - bytes32 x = 1; + function f() public returns (bool) { + bytes32 x = bytes32(uint256(1)); assembly { x := not(x) } - if (x != ~bytes32(1)) return false; + if (x != ~bytes32(uint256(1))) return false; assembly { x := iszero(x) } if (x != bytes32(0)) return false; return true; @@ -9092,10 +10098,10 @@ BOOST_AUTO_TEST_CASE(cleanup_bytes_types) // Checks that bytesXX types are properly cleaned before they are compared. char const* sourceCode = R"( contract C { - function f(bytes2 a, uint16 x) returns (uint) { + function f(bytes2 a, uint16 x) public returns (uint) { if (a != "ab") return 1; if (x != 0x0102) return 2; - if (bytes3(x) != 0x0102) return 3; + if (bytes3(uint24(x)) != 0x000102) return 3; return 0; } } @@ -9109,7 +10115,7 @@ BOOST_AUTO_TEST_CASE(cleanup_bytes_types_shortening) { char const* sourceCode = R"( contract C { - function f() pure returns (bytes32 r) { + function f() public pure returns (bytes32 r) { bytes4 x = 0xffffffff; bytes2 y = bytes2(x); assembly { r := y } @@ -9123,17 +10129,65 @@ BOOST_AUTO_TEST_CASE(cleanup_bytes_types_shortening) ABI_CHECK(callContractFunction("f()"), encodeArgs("\xff\xff\xff\xff")); } +BOOST_AUTO_TEST_CASE(cleanup_address_types) +{ + // Checks that address types are properly cleaned before they are compared. + char const* sourceCode = R"( + contract C { + function f(address a) public returns (uint) { + if (a != 0x1234567890123456789012345678901234567890) return 1; + return 0; + } + function g(address payable a) public returns (uint) { + if (a != 0x1234567890123456789012345678901234567890) return 1; + return 0; + } + } + )"; + compileAndRun(sourceCode, 0, "C"); + // We input longer data on purpose. + ABI_CHECK(callContractFunction("f(address)", u256("0xFFFF1234567890123456789012345678901234567890")), encodeArgs(0)); + ABI_CHECK(callContractFunction("g(address)", u256("0xFFFF1234567890123456789012345678901234567890")), encodeArgs(0)); +} + +BOOST_AUTO_TEST_CASE(cleanup_address_types_shortening) +{ + char const* sourceCode = R"( + contract C { + function f() public pure returns (address r) { + bytes21 x = 0x1122334455667788990011223344556677889900ff; + bytes20 y; + assembly { y := x } + address z = address(y); + assembly { r := z } + require(z == 0x1122334455667788990011223344556677889900); + } + function g() public pure returns (address payable r) { + bytes21 x = 0x1122334455667788990011223344556677889900ff; + bytes20 y; + assembly { y := x } + address payable z = address(y); + assembly { r := z } + require(z == 0x1122334455667788990011223344556677889900); + } + } + )"; + compileAndRun(sourceCode, 0, "C"); + ABI_CHECK(callContractFunction("f()"), encodeArgs(u256("0x1122334455667788990011223344556677889900"))); + ABI_CHECK(callContractFunction("g()"), encodeArgs(u256("0x1122334455667788990011223344556677889900"))); +} + BOOST_AUTO_TEST_CASE(skip_dynamic_types) { // The EVM cannot provide access to dynamically-sized return values, so we have to skip them. char const* sourceCode = R"( contract C { - function f() returns (uint, uint[], uint) { + function f() public returns (uint, uint[] memory, uint) { return (7, new uint[](2), 8); } - function g() returns (uint, uint) { + function g() public returns (uint, uint) { // Previous implementation "moved" b to the second place and did not skip. - var (a, _, b) = this.f(); + (uint a,, uint b) = this.f(); return (a, b); } } @@ -9154,12 +10208,12 @@ BOOST_AUTO_TEST_CASE(skip_dynamic_types_for_structs) uint y; } S public s; - function g() returns (uint, uint) { + function g() public returns (uint, uint) { s.x = 2; s.a = "abc"; s.b = [7, 8, 9]; s.y = 6; - var (x, a, y) = this.s(); + (uint x,, uint y) = this.s(); return (x, y); } } @@ -9171,19 +10225,19 @@ BOOST_AUTO_TEST_CASE(skip_dynamic_types_for_structs) BOOST_AUTO_TEST_CASE(failed_create) { char const* sourceCode = R"( - contract D { function D() payable {} } + contract D { constructor() public payable {} } contract C { uint public x; - function C() payable {} - function f(uint amount) returns (address) { + constructor() public payable {} + function f(uint amount) public returns (D) { x++; return (new D).value(amount)(); } - function stack(uint depth) returns (address) { + function stack(uint depth) public returns (address) { if (depth < 1024) return this.stack(depth - 1); else - return f(0); + return address(f(0)); } } )"; @@ -9200,8 +10254,8 @@ BOOST_AUTO_TEST_CASE(create_dynamic_array_with_zero_length) { char const* sourceCode = R"( contract C { - function f() returns (uint) { - var a = new uint[][](0); + function f() public returns (uint) { + uint[][] memory a = new uint[][](0); return 7; } } @@ -9217,7 +10271,7 @@ BOOST_AUTO_TEST_CASE(correctly_initialize_memory_array_in_constructor) char const* sourceCode = R"( contract C { bool public success; - function C() public { + constructor() public { // Make memory dirty. assembly { for { let i := 0 } lt(i, 64) { i := add(i, 1) } { @@ -9245,7 +10299,7 @@ BOOST_AUTO_TEST_CASE(return_does_not_skip_modifier) _; x = 9; } - function f() setsx returns (uint) { + function f() setsx public returns (uint) { return 2; } } @@ -9267,8 +10321,10 @@ BOOST_AUTO_TEST_CASE(break_in_modifier) break; } } - function f() run { - x++; + function f() run public { + uint k = x; + uint t = k + 1; + x = t; } } )"; @@ -9278,6 +10334,54 @@ BOOST_AUTO_TEST_CASE(break_in_modifier) ABI_CHECK(callContractFunction("x()"), encodeArgs(u256(1))); } +BOOST_AUTO_TEST_CASE(continue_in_modifier) +{ + char const* sourceCode = R"( + contract C { + uint public x; + modifier run() { + for (uint i = 0; i < 10; i++) { + if (i % 2 == 1) continue; + _; + } + } + function f() run public { + uint k = x; + uint t = k + 1; + x = t; + } + } + )"; + compileAndRun(sourceCode, 0, "C"); + ABI_CHECK(callContractFunction("x()"), encodeArgs(u256(0))); + ABI_CHECK(callContractFunction("f()"), encodeArgs()); + ABI_CHECK(callContractFunction("x()"), encodeArgs(u256(5))); +} + +BOOST_AUTO_TEST_CASE(return_in_modifier) +{ + char const* sourceCode = R"( + contract C { + uint public x; + modifier run() { + for (uint i = 1; i < 10; i++) { + if (i == 5) return; + _; + } + } + function f() run public { + uint k = x; + uint t = k + 1; + x = t; + } + } + )"; + compileAndRun(sourceCode, 0, "C"); + ABI_CHECK(callContractFunction("x()"), encodeArgs(u256(0))); + ABI_CHECK(callContractFunction("f()"), encodeArgs()); + ABI_CHECK(callContractFunction("x()"), encodeArgs(u256(4))); +} + BOOST_AUTO_TEST_CASE(stacked_return_with_modifiers) { char const* sourceCode = R"( @@ -9289,8 +10393,10 @@ BOOST_AUTO_TEST_CASE(stacked_return_with_modifiers) break; } } - function f() run { - x++; + function f() run public { + uint k = x; + uint t = k + 1; + x = t; } } )"; @@ -9306,7 +10412,7 @@ BOOST_AUTO_TEST_CASE(mutex) contract mutexed { bool locked; modifier protected { - if (locked) throw; + if (locked) revert(); locked = true; _; locked = false; @@ -9314,20 +10420,22 @@ BOOST_AUTO_TEST_CASE(mutex) } contract Fund is mutexed { uint shares; - function Fund() payable { shares = msg.value; } - function withdraw(uint amount) protected returns (uint) { + constructor() public payable { shares = msg.value; } + function withdraw(uint amount) public protected returns (uint) { // NOTE: It is very bad practice to write this function this way. // Please refer to the documentation of how to do this properly. - if (amount > shares) throw; - if (!msg.sender.call.value(amount)()) throw; + if (amount > shares) revert(); + (bool success,) = msg.sender.call.value(amount)(""); + require(success); shares -= amount; return shares; } - function withdrawUnprotected(uint amount) returns (uint) { + function withdrawUnprotected(uint amount) public returns (uint) { // NOTE: It is very bad practice to write this function this way. // Please refer to the documentation of how to do this properly. - if (amount > shares) throw; - if (!msg.sender.call.value(amount)()) throw; + if (amount > shares) revert(); + (bool success,) = msg.sender.call.value(amount)(""); + require(success); shares -= amount; return shares; } @@ -9336,9 +10444,9 @@ BOOST_AUTO_TEST_CASE(mutex) Fund public fund; uint callDepth; bool protected; - function setProtected(bool _protected) { protected = _protected; } - function Attacker(Fund _fund) { fund = _fund; } - function attack() returns (uint) { + function setProtected(bool _protected) public { protected = _protected; } + constructor(Fund _fund) public { fund = _fund; } + function attack() public returns (uint) { callDepth = 0; return attackInternal(); } @@ -9348,7 +10456,7 @@ BOOST_AUTO_TEST_CASE(mutex) else return fund.withdrawUnprotected(10); } - function() payable { + function() external payable { callDepth++; if (callDepth < 4) attackInternal(); @@ -9374,8 +10482,8 @@ BOOST_AUTO_TEST_CASE(failing_ecrecover_invalid_input) // Note that the precompile does not return zero but returns nothing. char const* sourceCode = R"( contract C { - function f() returns (address) { - return ecrecover(bytes32(uint(-1)), 1, 2, 3); + function f() public returns (address) { + return ecrecover(bytes32(uint(-1)), 1, bytes32(uint(2)), bytes32(uint(3))); } } )"; @@ -9387,20 +10495,20 @@ BOOST_AUTO_TEST_CASE(failing_ecrecover_invalid_input_proper) { char const* sourceCode = R"( contract C { - function f() returns (address) { + function f() public returns (address) { return recover( 0x77e5189111eb6557e8a637b27ef8fbb15bc61d61c2f00cc48878f3a296e5e0ca, 0, // invalid v value 0x6944c77849b18048f6abe0db8084b0d0d0689cdddb53d2671c36967b58691ad4, 0xef4f06ba4f78319baafd0424365777241af4dfd3da840471b4b4b087b7750d0d, - 0xca35b7d915458ef540ade6068dfe2f44e8fa733c, - 0xca35b7d915458ef540ade6068dfe2f44e8fa733c + 0x000000000000000000000000ca35b7d915458ef540ade6068dfe2f44e8fa733c, + 0x000000000000000000000000ca35b7d915458ef540ade6068dfe2f44e8fa733c ); } function recover(bytes32 hash, uint8 v, bytes32 r, bytes32 s, uint blockExpired, bytes32 salt) - returns (address) + public returns (address) { - require(hash == keccak256(blockExpired, salt)); + require(hash == keccak256(abi.encodePacked(blockExpired, salt))); return ecrecover(hash, v, r, s); } } @@ -9413,7 +10521,7 @@ BOOST_AUTO_TEST_CASE(failing_ecrecover_invalid_input_asm) { char const* sourceCode = R"( contract C { - function f() returns (address) { + function f() public returns (address) { assembly { mstore(mload(0x40), 0xca35b7d915458ef540ade6068dfe2f44e8fa733c) } @@ -9432,24 +10540,24 @@ BOOST_AUTO_TEST_CASE(failing_ecrecover_invalid_input_asm) BOOST_AUTO_TEST_CASE(calling_nonexisting_contract_throws) { - char const* sourceCode = R"( - contract D { function g(); } + char const* sourceCode = R"YY( + contract D { function g() public; } contract C { D d = D(0x1212); - function f() returns (uint) { + function f() public returns (uint) { d.g(); return 7; } - function g() returns (uint) { + function g() public returns (uint) { d.g.gas(200)(); return 7; } - function h() returns (uint) { - d.call(); // this does not throw (low-level) + function h() public returns (uint) { + address(d).call(""); // this does not throw (low-level) return 7; } } - )"; + )YY"; compileAndRun(sourceCode, 0, "C"); ABI_CHECK(callContractFunction("f()"), encodeArgs()); ABI_CHECK(callContractFunction("g()"), encodeArgs()); @@ -9460,7 +10568,7 @@ BOOST_AUTO_TEST_CASE(payable_constructor) { char const* sourceCode = R"( contract C { - function C() payable { } + constructor() public payable { } } )"; compileAndRun(sourceCode, 27, "C"); @@ -9471,10 +10579,10 @@ BOOST_AUTO_TEST_CASE(payable_function) char const* sourceCode = R"( contract C { uint public a; - function f() payable returns (uint) { + function f() payable public returns (uint) { return msg.value; } - function() payable { + function() external payable { a = msg.value + 1; } } @@ -9492,10 +10600,10 @@ BOOST_AUTO_TEST_CASE(payable_function_calls_library) { char const* sourceCode = R"( library L { - function f() returns (uint) { return 7; } + function f() public returns (uint) { return 7; } } contract C { - function f() payable returns (uint) { + function f() public payable returns (uint) { return L.f(); } } @@ -9510,12 +10618,19 @@ BOOST_AUTO_TEST_CASE(non_payable_throw) char const* sourceCode = R"( contract C { uint public a; - function f() returns (uint) { + function f() public returns (uint) { + return msgvalue(); + } + function msgvalue() internal returns (uint) { return msg.value; } - function() { + function() external { + update(); + } + function update() internal { a = msg.value + 1; } + } )"; compileAndRun(sourceCode, 0, "C"); @@ -9537,7 +10652,10 @@ BOOST_AUTO_TEST_CASE(no_nonpayable_circumvention_by_modifier) modifier tryCircumvent { if (false) _; // avoid the function, we should still not accept ether } - function f() tryCircumvent returns (uint) { + function f() tryCircumvent public returns (uint) { + return msgvalue(); + } + function msgvalue() internal returns (uint) { return msg.value; } } @@ -9555,10 +10673,10 @@ BOOST_AUTO_TEST_CASE(mem_resize_is_not_paid_at_call) // Tests that this also survives the optimizer. char const* sourceCode = R"( contract C { - function f() returns (uint[200]) {} + function f() public returns (uint[200] memory) {} } contract D { - function f(C c) returns (uint) { c.f(); return 7; } + function f(C c) public returns (uint) { c.f(); return 7; } } )"; @@ -9572,12 +10690,12 @@ BOOST_AUTO_TEST_CASE(calling_uninitialized_function) { char const* sourceCode = R"( contract C { - function intern() returns (uint) { + function intern() public returns (uint) { function (uint) internal returns (uint) x; x(2); return 7; } - function extern() returns (uint) { + function extern() public returns (uint) { function (uint) external returns (uint) x; x(2); return 7; @@ -9597,7 +10715,7 @@ BOOST_AUTO_TEST_CASE(calling_uninitialized_function_in_detail) contract C { function() internal returns (uint) x; int mutex; - function t() returns (uint) { + function t() public returns (uint) { if (mutex > 0) { assembly { mstore(0, 7) return(0, 0x20) } } mutex = 1; @@ -9617,12 +10735,12 @@ BOOST_AUTO_TEST_CASE(calling_uninitialized_function_through_array) char const* sourceCode = R"( contract C { int mutex; - function t() returns (uint) { + function t() public returns (uint) { if (mutex > 0) { assembly { mstore(0, 7) return(0, 0x20) } } mutex = 1; // Avoid re-executing this function if we jump somewhere. - function() internal returns (uint)[200] x; + function() internal returns (uint)[200] memory x; x[0](); return 2; } @@ -9637,13 +10755,13 @@ BOOST_AUTO_TEST_CASE(pass_function_types_internally) { char const* sourceCode = R"( contract C { - function f(uint x) returns (uint) { + function f(uint x) public returns (uint) { return eval(g, x); } - function eval(function(uint) returns (uint) x, uint a) internal returns (uint) { + function eval(function(uint) internal returns (uint) x, uint a) internal returns (uint) { return x(a); } - function g(uint x) returns (uint) { return x + 1; } + function g(uint x) public returns (uint) { return x + 1; } } )"; @@ -9655,16 +10773,16 @@ BOOST_AUTO_TEST_CASE(pass_function_types_externally) { char const* sourceCode = R"( contract C { - function f(uint x) returns (uint) { + function f(uint x) public returns (uint) { return this.eval(this.g, x); } - function f2(uint x) returns (uint) { + function f2(uint x) public returns (uint) { return eval(this.g, x); } - function eval(function(uint) external returns (uint) x, uint a) returns (uint) { + function eval(function(uint) external returns (uint) x, uint a) public returns (uint) { return x(a); } - function g(uint x) returns (uint) { return x + 1; } + function g(uint x) public returns (uint) { return x + 1; } } )"; @@ -9677,8 +10795,8 @@ BOOST_AUTO_TEST_CASE(receive_external_function_type) { char const* sourceCode = R"( contract C { - function g() returns (uint) { return 7; } - function f(function() external returns (uint) g) returns (uint) { + function g() public returns (uint) { return 7; } + function f(function() external returns (uint) g) public returns (uint) { return g(); } } @@ -9695,8 +10813,8 @@ BOOST_AUTO_TEST_CASE(return_external_function_type) { char const* sourceCode = R"( contract C { - function g() {} - function f() returns (function() external) { + function g() public {} + function f() public returns (function() external) { return this.g; } } @@ -9713,18 +10831,18 @@ BOOST_AUTO_TEST_CASE(store_function) { char const* sourceCode = R"( contract Other { - function addTwo(uint x) returns (uint) { return x + 2; } + function addTwo(uint x) public returns (uint) { return x + 2; } } contract C { - function (function (uint) external returns (uint)) returns (uint) ev; + function (function (uint) external returns (uint)) internal returns (uint) ev; function (uint) external returns (uint) x; - function store(function(uint) external returns (uint) y) { + function store(function(uint) external returns (uint) y) public { x = y; } - function eval(function(uint) external returns (uint) y) returns (uint) { + function eval(function(uint) external returns (uint) y) public returns (uint) { return y(7); } - function t() returns (uint) { + function t() public returns (uint) { ev = eval; this.store((new Other()).addTwo); return ev(x); @@ -9742,14 +10860,14 @@ BOOST_AUTO_TEST_CASE(store_function_in_constructor) contract C { uint public result_in_constructor; function (uint) internal returns (uint) x; - function C () { + constructor() public { x = double; result_in_constructor = use(2); } - function double(uint _arg) returns (uint _ret) { + function double(uint _arg) public returns (uint _ret) { _ret = _arg * 2; } - function use(uint _arg) returns (uint) { + function use(uint _arg) public returns (uint) { return x(_arg); } } @@ -9767,13 +10885,13 @@ BOOST_AUTO_TEST_CASE(store_internal_unused_function_in_constructor) char const* sourceCode = R"( contract C { function () internal returns (uint) x; - function C () { + constructor() public { x = unused; } function unused() internal returns (uint) { return 7; } - function t() returns (uint) { + function t() public returns (uint) { return x(); } } @@ -9789,10 +10907,10 @@ BOOST_AUTO_TEST_CASE(store_internal_unused_library_function_in_constructor) library L { function x() internal returns (uint) { return 7; } } contract C { function () internal returns (uint) x; - function C () { + constructor() public { x = L.x; } - function t() returns (uint) { + function t() public returns (uint) { return x(); } } @@ -9807,13 +10925,13 @@ BOOST_AUTO_TEST_CASE(same_function_in_construction_and_runtime) char const* sourceCode = R"( contract C { uint public initial; - function C() { + constructor() public { initial = double(2); } - function double(uint _arg) returns (uint _ret) { + function double(uint _arg) public returns (uint _ret) { _ret = _arg * 2; } - function runtime(uint _arg) returns (uint) { + function runtime(uint _arg) public returns (uint) { return double(_arg); } } @@ -9829,13 +10947,13 @@ BOOST_AUTO_TEST_CASE(same_function_in_construction_and_runtime_equality_check) char const* sourceCode = R"( contract C { function (uint) internal returns (uint) x; - function C() { + constructor() public { x = double; } - function test() returns (bool) { + function test() public returns (bool) { return x == double; } - function double(uint _arg) returns (uint _ret) { + function double(uint _arg) public returns (uint _ret) { _ret = _arg * 2; } } @@ -9849,7 +10967,7 @@ BOOST_AUTO_TEST_CASE(function_type_library_internal) { char const* sourceCode = R"( library Utils { - function reduce(uint[] memory array, function(uint, uint) returns (uint) f, uint init) internal returns (uint) { + function reduce(uint[] memory array, function(uint, uint) internal returns (uint) f, uint init) internal returns (uint) { for (uint i = 0; i < array.length; i++) { init = f(array[i], init); } @@ -9860,7 +10978,7 @@ BOOST_AUTO_TEST_CASE(function_type_library_internal) } } contract C { - function f(uint[] x) returns (uint) { + function f(uint[] memory x) public returns (uint) { return Utils.reduce(x, Utils.sum, 0); } } @@ -9875,21 +10993,21 @@ BOOST_AUTO_TEST_CASE(call_function_returning_function) { char const* sourceCode = R"( contract test { - function f0() returns (uint) { + function f0() public returns (uint) { return 2; } - function f1() internal returns (function() returns (uint)) { + function f1() internal returns (function() internal returns (uint)) { return f0; } - function f2() internal returns (function() returns (function () returns (uint))) { + function f2() internal returns (function() internal returns (function () internal returns (uint))) { return f1; } - function f3() internal returns (function() returns (function () returns (function () returns (uint)))) + function f3() internal returns (function() internal returns (function () internal returns (function () internal returns (uint)))) { return f2; } - function f() returns (uint) { - function() returns(function() returns(function() returns(function() returns(uint)))) x; + function f() public returns (uint) { + function() internal returns(function() internal returns(function() internal returns(function() internal returns(uint)))) x; x = f3; return x()()()(); } @@ -9920,11 +11038,11 @@ BOOST_AUTO_TEST_CASE(mapping_of_functions) success = true; } - function Flow() { + constructor() public { stages[msg.sender] = stage0; } - function f() returns (uint) { + function f() public returns (uint) { stages[msg.sender](); return 7; } @@ -9945,35 +11063,35 @@ BOOST_AUTO_TEST_CASE(packed_functions) char const* sourceCode = R"( contract C { // these should take the same slot - function() returns (uint) a; + function() internal returns (uint) a; function() external returns (uint) b; function() external returns (uint) c; - function() returns (uint) d; + function() internal returns (uint) d; uint8 public x; - function set() { + function set() public { x = 2; d = g; c = this.h; b = this.h; a = g; } - function t1() returns (uint) { + function t1() public returns (uint) { return a(); } - function t2() returns (uint) { + function t2() public returns (uint) { return b(); } - function t3() returns (uint) { + function t3() public returns (uint) { return a(); } - function t4() returns (uint) { + function t4() public returns (uint) { return b(); } - function g() returns (uint) { + function g() public returns (uint) { return 7; } - function h() returns (uint) { + function h() public returns (uint) { return 8; } } @@ -9992,12 +11110,12 @@ BOOST_AUTO_TEST_CASE(function_memory_array) { char const* sourceCode = R"( contract C { - function a(uint x) returns (uint) { return x + 1; } - function b(uint x) returns (uint) { return x + 2; } - function c(uint x) returns (uint) { return x + 3; } - function d(uint x) returns (uint) { return x + 5; } - function e(uint x) returns (uint) { return x + 8; } - function test(uint x, uint i) returns (uint) { + function a(uint x) public returns (uint) { return x + 1; } + function b(uint x) public returns (uint) { return x + 2; } + function c(uint x) public returns (uint) { return x + 3; } + function d(uint x) public returns (uint) { return x + 5; } + function e(uint x) public returns (uint) { return x + 8; } + function test(uint x, uint i) public returns (uint) { function(uint) internal returns (uint)[] memory arr = new function(uint) internal returns (uint)[](10); arr[0] = a; @@ -10023,17 +11141,17 @@ BOOST_AUTO_TEST_CASE(function_delete_storage) { char const* sourceCode = R"( contract C { - function a() returns (uint) { return 7; } + function a() public returns (uint) { return 7; } function() internal returns (uint) y; - function set() returns (uint) { + function set() public returns (uint) { y = a; return y(); } - function d() returns (uint) { + function d() public returns (uint) { delete y; return 1; } - function ca() returns (uint) { + function ca() public returns (uint) { return y(); } } @@ -10050,9 +11168,9 @@ BOOST_AUTO_TEST_CASE(function_delete_stack) { char const* sourceCode = R"( contract C { - function a() returns (uint) { return 7; } - function test() returns (uint) { - var y = a; + function a() public returns (uint) { return 7; } + function test() public returns (uint) { + function () returns (uint) y = a; delete y; y(); } @@ -10069,13 +11187,13 @@ BOOST_AUTO_TEST_CASE(copy_function_storage_array) contract C { function() internal returns (uint)[] x; function() internal returns (uint)[] y; - function test() returns (uint) { + function test() public returns (uint) { x.length = 10; x[9] = a; y = x; return y[9](); } - function a() returns (uint) { + function a() public returns (uint) { return 7; } } @@ -10089,8 +11207,8 @@ BOOST_AUTO_TEST_CASE(function_array_cross_calls) { char const* sourceCode = R"( contract D { - function f(function() external returns (function() external returns (uint))[] x) - returns (function() external returns (uint)[3] r) + function f(function() external returns (function() external returns (uint))[] memory x) + public returns (function() external returns (uint)[3] memory r) { r[0] = x[0](); r[1] = x[1](); @@ -10098,23 +11216,23 @@ BOOST_AUTO_TEST_CASE(function_array_cross_calls) } } contract C { - function test() returns (uint, uint, uint) { + function test() public returns (uint, uint, uint) { function() external returns (function() external returns (uint))[] memory x = new function() external returns (function() external returns (uint))[](10); for (uint i = 0; i < x.length; i ++) x[i] = this.h; x[0] = this.htwo; - var y = (new D()).f(x); + function() external returns (uint)[3] memory y = (new D()).f(x); return (y[0](), y[1](), y[2]()); } - function e() returns (uint) { return 5; } - function f() returns (uint) { return 6; } - function g() returns (uint) { return 7; } + function e() public returns (uint) { return 5; } + function f() public returns (uint) { return 6; } + function g() public returns (uint) { return 7; } uint counter; - function h() returns (function() external returns (uint)) { + function h() public returns (function() external returns (uint)) { return counter++ == 0 ? this.f : this.g; } - function htwo() returns (function() external returns (uint)) { + function htwo() public returns (function() external returns (uint)) { return this.e; } } @@ -10128,10 +11246,10 @@ BOOST_AUTO_TEST_CASE(external_function_to_address) { char const* sourceCode = R"( contract C { - function f() returns (bool) { + function f() public returns (bool) { return address(this.f) == address(this); } - function g(function() external cb) returns (address) { + function g(function() external cb) public returns (address) { return address(cb); } } @@ -10149,12 +11267,12 @@ BOOST_AUTO_TEST_CASE(copy_internal_function_array_to_storage) contract C { function() internal returns (uint)[20] x; int mutex; - function one() returns (uint) { - function() internal returns (uint)[20] xmem; + function one() public returns (uint) { + function() internal returns (uint)[20] memory xmem; x = xmem; return 3; } - function two() returns (uint) { + function two() public returns (uint) { if (mutex > 0) return 7; mutex = 1; @@ -10218,7 +11336,7 @@ BOOST_AUTO_TEST_CASE(shift_left) { char const* sourceCode = R"( contract C { - function f(uint a, uint b) returns (uint) { + function f(uint a, uint b) public returns (uint) { return a << b; } } @@ -10236,7 +11354,7 @@ BOOST_AUTO_TEST_CASE(shift_left_uint32) { char const* sourceCode = R"( contract C { - function f(uint32 a, uint32 b) returns (uint) { + function f(uint32 a, uint32 b) public returns (uint) { return a << b; } } @@ -10253,7 +11371,7 @@ BOOST_AUTO_TEST_CASE(shift_left_uint8) { char const* sourceCode = R"( contract C { - function f(uint8 a, uint8 b) returns (uint) { + function f(uint8 a, uint8 b) public returns (uint) { return a << b; } } @@ -10268,7 +11386,7 @@ BOOST_AUTO_TEST_CASE(shift_left_larger_type) // This basically tests proper cleanup and conversion. It should not convert x to int8. char const* sourceCode = R"( contract C { - function f() returns (int8) { + function f() public returns (int8) { uint8 x = 254; int8 y = 1; return y << x; @@ -10283,7 +11401,7 @@ BOOST_AUTO_TEST_CASE(shift_left_assignment) { char const* sourceCode = R"( contract C { - function f(uint a, uint b) returns (uint) { + function f(uint a, uint b) public returns (uint) { a <<= b; return a; } @@ -10302,7 +11420,7 @@ BOOST_AUTO_TEST_CASE(shift_left_assignment_different_type) { char const* sourceCode = R"( contract C { - function f(uint a, uint8 b) returns (uint) { + function f(uint a, uint8 b) public returns (uint) { a <<= b; return a; } @@ -10320,7 +11438,7 @@ BOOST_AUTO_TEST_CASE(shift_right) { char const* sourceCode = R"( contract C { - function f(uint a, uint b) returns (uint) { + function f(uint a, uint b) public returns (uint) { return a >> b; } } @@ -10330,13 +11448,14 @@ BOOST_AUTO_TEST_CASE(shift_right) ABI_CHECK(callContractFunction("f(uint256,uint256)", u256(0x4266), u256(8)), encodeArgs(u256(0x42))); ABI_CHECK(callContractFunction("f(uint256,uint256)", u256(0x4266), u256(16)), encodeArgs(u256(0))); ABI_CHECK(callContractFunction("f(uint256,uint256)", u256(0x4266), u256(17)), encodeArgs(u256(0))); + ABI_CHECK(callContractFunction("f(uint256,uint256)", u256(1)<<255, u256(5)), encodeArgs(u256(1)<<250)); } BOOST_AUTO_TEST_CASE(shift_right_garbled) { char const* sourceCode = R"( contract C { - function f(uint8 a, uint8 b) returns (uint) { + function f(uint8 a, uint8 b) public returns (uint) { assembly { a := 0xffffffff } @@ -10350,11 +11469,44 @@ BOOST_AUTO_TEST_CASE(shift_right_garbled) ABI_CHECK(callContractFunction("f(uint8,uint8)", u256(0x0), u256(0x1004)), encodeArgs(u256(0xf))); } +BOOST_AUTO_TEST_CASE(shift_right_garbled_signed) +{ + char const* sourceCode = R"( + contract C { + function f(int8 a, uint8 b) public returns (int) { + assembly { + a := 0xfffffff0 + } + // Higher bits should be signextended before the shift + return a >> b; + } + function g(int8 a, uint8 b) public returns (int) { + assembly { + a := 0xf0 + } + // Higher bits should be signextended before the shift + return a >> b; + } + } + )"; + compileAndRun(sourceCode, 0, "C"); + ABI_CHECK(callContractFunction("f(int8,uint8)", u256(0x0), u256(3)), encodeArgs(u256(-2))); + ABI_CHECK(callContractFunction("f(int8,uint8)", u256(0x0), u256(4)), encodeArgs(u256(-1))); + ABI_CHECK(callContractFunction("f(int8,uint8)", u256(0x0), u256(0xFF)), encodeArgs(u256(-1))); + ABI_CHECK(callContractFunction("f(int8,uint8)", u256(0x0), u256(0x1003)), encodeArgs(u256(-2))); + ABI_CHECK(callContractFunction("f(int8,uint8)", u256(0x0), u256(0x1004)), encodeArgs(u256(-1))); + ABI_CHECK(callContractFunction("g(int8,uint8)", u256(0x0), u256(3)), encodeArgs(u256(-2))); + ABI_CHECK(callContractFunction("g(int8,uint8)", u256(0x0), u256(4)), encodeArgs(u256(-1))); + ABI_CHECK(callContractFunction("g(int8,uint8)", u256(0x0), u256(0xFF)), encodeArgs(u256(-1))); + ABI_CHECK(callContractFunction("g(int8,uint8)", u256(0x0), u256(0x1003)), encodeArgs(u256(-2))); + ABI_CHECK(callContractFunction("g(int8,uint8)", u256(0x0), u256(0x1004)), encodeArgs(u256(-1))); +} + BOOST_AUTO_TEST_CASE(shift_right_uint32) { char const* sourceCode = R"( contract C { - function f(uint32 a, uint32 b) returns (uint) { + function f(uint32 a, uint32 b) public returns (uint) { return a >> b; } } @@ -10370,7 +11522,7 @@ BOOST_AUTO_TEST_CASE(shift_right_uint8) { char const* sourceCode = R"( contract C { - function f(uint8 a, uint8 b) returns (uint) { + function f(uint8 a, uint8 b) public returns (uint) { return a >> b; } } @@ -10384,7 +11536,7 @@ BOOST_AUTO_TEST_CASE(shift_right_assignment) { char const* sourceCode = R"( contract C { - function f(uint a, uint b) returns (uint) { + function f(uint a, uint b) public returns (uint) { a >>= b; return a; } @@ -10401,7 +11553,7 @@ BOOST_AUTO_TEST_CASE(shift_right_assignment_signed) { char const* sourceCode = R"( contract C { - function f(int a, int b) returns (int) { + function f(int a, int b) public returns (int) { a >>= b; return a; } @@ -10418,7 +11570,7 @@ BOOST_AUTO_TEST_CASE(shift_right_negative_lvalue) { char const* sourceCode = R"( contract C { - function f(int a, int b) returns (int) { + function f(int a, int b) public returns (int) { return a >> b; } } @@ -10426,23 +11578,80 @@ BOOST_AUTO_TEST_CASE(shift_right_negative_lvalue) compileAndRun(sourceCode, 0, "C"); ABI_CHECK(callContractFunction("f(int256,int256)", u256(-4266), u256(0)), encodeArgs(u256(-4266))); ABI_CHECK(callContractFunction("f(int256,int256)", u256(-4266), u256(1)), encodeArgs(u256(-2133))); - ABI_CHECK(callContractFunction("f(int256,int256)", u256(-4266), u256(4)), encodeArgs(u256(-266))); - ABI_CHECK(callContractFunction("f(int256,int256)", u256(-4266), u256(8)), encodeArgs(u256(-16))); - ABI_CHECK(callContractFunction("f(int256,int256)", u256(-4266), u256(16)), encodeArgs(u256(0))); - ABI_CHECK(callContractFunction("f(int256,int256)", u256(-4266), u256(17)), encodeArgs(u256(0))); + ABI_CHECK(callContractFunction("f(int256,int256)", u256(-4266), u256(4)), encodeArgs(u256(-267))); + ABI_CHECK(callContractFunction("f(int256,int256)", u256(-4266), u256(8)), encodeArgs(u256(-17))); + ABI_CHECK(callContractFunction("f(int256,int256)", u256(-4266), u256(16)), encodeArgs(u256(-1))); + ABI_CHECK(callContractFunction("f(int256,int256)", u256(-4266), u256(17)), encodeArgs(u256(-1))); ABI_CHECK(callContractFunction("f(int256,int256)", u256(-4267), u256(0)), encodeArgs(u256(-4267))); - ABI_CHECK(callContractFunction("f(int256,int256)", u256(-4267), u256(1)), encodeArgs(u256(-2133))); - ABI_CHECK(callContractFunction("f(int256,int256)", u256(-4267), u256(4)), encodeArgs(u256(-266))); - ABI_CHECK(callContractFunction("f(int256,int256)", u256(-4267), u256(8)), encodeArgs(u256(-16))); - ABI_CHECK(callContractFunction("f(int256,int256)", u256(-4267), u256(16)), encodeArgs(u256(0))); - ABI_CHECK(callContractFunction("f(int256,int256)", u256(-4267), u256(17)), encodeArgs(u256(0))); + ABI_CHECK(callContractFunction("f(int256,int256)", u256(-4267), u256(1)), encodeArgs(u256(-2134))); + ABI_CHECK(callContractFunction("f(int256,int256)", u256(-4267), u256(4)), encodeArgs(u256(-267))); + ABI_CHECK(callContractFunction("f(int256,int256)", u256(-4267), u256(8)), encodeArgs(u256(-17))); + ABI_CHECK(callContractFunction("f(int256,int256)", u256(-4267), u256(16)), encodeArgs(u256(-1))); + ABI_CHECK(callContractFunction("f(int256,int256)", u256(-4267), u256(17)), encodeArgs(u256(-1))); +} + +BOOST_AUTO_TEST_CASE(shift_right_negative_literal) +{ + char const* sourceCode = R"( + contract C { + function f1() public pure returns (bool) { + return (-4266 >> 0) == -4266; + } + function f2() public pure returns (bool) { + return (-4266 >> 1) == -2133; + } + function f3() public pure returns (bool) { + return (-4266 >> 4) == -267; + } + function f4() public pure returns (bool) { + return (-4266 >> 8) == -17; + } + function f5() public pure returns (bool) { + return (-4266 >> 16) == -1; + } + function f6() public pure returns (bool) { + return (-4266 >> 17) == -1; + } + function g1() public pure returns (bool) { + return (-4267 >> 0) == -4267; + } + function g2() public pure returns (bool) { + return (-4267 >> 1) == -2134; + } + function g3() public pure returns (bool) { + return (-4267 >> 4) == -267; + } + function g4() public pure returns (bool) { + return (-4267 >> 8) == -17; + } + function g5() public pure returns (bool) { + return (-4267 >> 16) == -1; + } + function g6() public pure returns (bool) { + return (-4267 >> 17) == -1; + } + } + )"; + compileAndRun(sourceCode, 0, "C"); + ABI_CHECK(callContractFunction("f1()"), encodeArgs(true)); + ABI_CHECK(callContractFunction("f2()"), encodeArgs(true)); + ABI_CHECK(callContractFunction("f3()"), encodeArgs(true)); + ABI_CHECK(callContractFunction("f4()"), encodeArgs(true)); + ABI_CHECK(callContractFunction("f5()"), encodeArgs(true)); + ABI_CHECK(callContractFunction("f6()"), encodeArgs(true)); + ABI_CHECK(callContractFunction("g1()"), encodeArgs(true)); + ABI_CHECK(callContractFunction("g2()"), encodeArgs(true)); + ABI_CHECK(callContractFunction("g3()"), encodeArgs(true)); + ABI_CHECK(callContractFunction("g4()"), encodeArgs(true)); + ABI_CHECK(callContractFunction("g5()"), encodeArgs(true)); + ABI_CHECK(callContractFunction("g6()"), encodeArgs(true)); } BOOST_AUTO_TEST_CASE(shift_right_negative_lvalue_int8) { char const* sourceCode = R"( contract C { - function f(int8 a, int8 b) returns (int) { + function f(int8 a, int8 b) public returns (int) { return a >> b; } } @@ -10450,67 +11659,67 @@ BOOST_AUTO_TEST_CASE(shift_right_negative_lvalue_int8) compileAndRun(sourceCode, 0, "C"); ABI_CHECK(callContractFunction("f(int8,int8)", u256(-66), u256(0)), encodeArgs(u256(-66))); ABI_CHECK(callContractFunction("f(int8,int8)", u256(-66), u256(1)), encodeArgs(u256(-33))); - ABI_CHECK(callContractFunction("f(int8,int8)", u256(-66), u256(4)), encodeArgs(u256(-4))); - ABI_CHECK(callContractFunction("f(int8,int8)", u256(-66), u256(8)), encodeArgs(u256(0))); - ABI_CHECK(callContractFunction("f(int8,int8)", u256(-66), u256(16)), encodeArgs(u256(0))); - ABI_CHECK(callContractFunction("f(int8,int8)", u256(-66), u256(17)), encodeArgs(u256(0))); + ABI_CHECK(callContractFunction("f(int8,int8)", u256(-66), u256(4)), encodeArgs(u256(-5))); + ABI_CHECK(callContractFunction("f(int8,int8)", u256(-66), u256(8)), encodeArgs(u256(-1))); + ABI_CHECK(callContractFunction("f(int8,int8)", u256(-66), u256(16)), encodeArgs(u256(-1))); + ABI_CHECK(callContractFunction("f(int8,int8)", u256(-66), u256(17)), encodeArgs(u256(-1))); ABI_CHECK(callContractFunction("f(int8,int8)", u256(-67), u256(0)), encodeArgs(u256(-67))); - ABI_CHECK(callContractFunction("f(int8,int8)", u256(-67), u256(1)), encodeArgs(u256(-33))); - ABI_CHECK(callContractFunction("f(int8,int8)", u256(-67), u256(4)), encodeArgs(u256(-4))); - ABI_CHECK(callContractFunction("f(int8,int8)", u256(-67), u256(8)), encodeArgs(u256(0))); - ABI_CHECK(callContractFunction("f(int8,int8)", u256(-67), u256(16)), encodeArgs(u256(0))); - ABI_CHECK(callContractFunction("f(int8,int8)", u256(-67), u256(17)), encodeArgs(u256(0))); + ABI_CHECK(callContractFunction("f(int8,int8)", u256(-67), u256(1)), encodeArgs(u256(-34))); + ABI_CHECK(callContractFunction("f(int8,int8)", u256(-67), u256(4)), encodeArgs(u256(-5))); + ABI_CHECK(callContractFunction("f(int8,int8)", u256(-67), u256(8)), encodeArgs(u256(-1))); + ABI_CHECK(callContractFunction("f(int8,int8)", u256(-67), u256(16)), encodeArgs(u256(-1))); + ABI_CHECK(callContractFunction("f(int8,int8)", u256(-67), u256(17)), encodeArgs(u256(-1))); } BOOST_AUTO_TEST_CASE(shift_right_negative_lvalue_signextend_int8) { char const* sourceCode = R"( contract C { - function f(int8 a, int8 b) returns (int8) { + function f(int8 a, int8 b) public returns (int8) { return a >> b; } } )"; compileAndRun(sourceCode, 0, "C"); ABI_CHECK(callContractFunction("f(int8,int8)", u256(0x99u), u256(0)), encodeArgs(u256(-103))); - ABI_CHECK(callContractFunction("f(int8,int8)", u256(0x99u), u256(1)), encodeArgs(u256(-51))); - ABI_CHECK(callContractFunction("f(int8,int8)", u256(0x99u), u256(2)), encodeArgs(u256(-25))); - ABI_CHECK(callContractFunction("f(int8,int8)", u256(0x99u), u256(4)), encodeArgs(u256(-6))); - ABI_CHECK(callContractFunction("f(int8,int8)", u256(0x99u), u256(8)), encodeArgs(u256(0))); + ABI_CHECK(callContractFunction("f(int8,int8)", u256(0x99u), u256(1)), encodeArgs(u256(-52))); + ABI_CHECK(callContractFunction("f(int8,int8)", u256(0x99u), u256(2)), encodeArgs(u256(-26))); + ABI_CHECK(callContractFunction("f(int8,int8)", u256(0x99u), u256(4)), encodeArgs(u256(-7))); + ABI_CHECK(callContractFunction("f(int8,int8)", u256(0x99u), u256(8)), encodeArgs(u256(-1))); } BOOST_AUTO_TEST_CASE(shift_right_negative_lvalue_signextend_int16) { char const* sourceCode = R"( contract C { - function f(int16 a, int16 b) returns (int16) { + function f(int16 a, int16 b) public returns (int16) { return a >> b; } } )"; compileAndRun(sourceCode, 0, "C"); ABI_CHECK(callContractFunction("f(int16,int16)", u256(0xFF99u), u256(0)), encodeArgs(u256(-103))); - ABI_CHECK(callContractFunction("f(int16,int16)", u256(0xFF99u), u256(1)), encodeArgs(u256(-51))); - ABI_CHECK(callContractFunction("f(int16,int16)", u256(0xFF99u), u256(2)), encodeArgs(u256(-25))); - ABI_CHECK(callContractFunction("f(int16,int16)", u256(0xFF99u), u256(4)), encodeArgs(u256(-6))); - ABI_CHECK(callContractFunction("f(int16,int16)", u256(0xFF99u), u256(8)), encodeArgs(u256(0))); + ABI_CHECK(callContractFunction("f(int16,int16)", u256(0xFF99u), u256(1)), encodeArgs(u256(-52))); + ABI_CHECK(callContractFunction("f(int16,int16)", u256(0xFF99u), u256(2)), encodeArgs(u256(-26))); + ABI_CHECK(callContractFunction("f(int16,int16)", u256(0xFF99u), u256(4)), encodeArgs(u256(-7))); + ABI_CHECK(callContractFunction("f(int16,int16)", u256(0xFF99u), u256(8)), encodeArgs(u256(-1))); } BOOST_AUTO_TEST_CASE(shift_right_negative_lvalue_signextend_int32) { char const* sourceCode = R"( contract C { - function f(int32 a, int32 b) returns (int32) { + function f(int32 a, int32 b) public returns (int32) { return a >> b; } } )"; compileAndRun(sourceCode, 0, "C"); ABI_CHECK(callContractFunction("f(int32,int32)", u256(0xFFFFFF99u), u256(0)), encodeArgs(u256(-103))); - ABI_CHECK(callContractFunction("f(int32,int32)", u256(0xFFFFFF99u), u256(1)), encodeArgs(u256(-51))); - ABI_CHECK(callContractFunction("f(int32,int32)", u256(0xFFFFFF99u), u256(2)), encodeArgs(u256(-25))); - ABI_CHECK(callContractFunction("f(int32,int32)", u256(0xFFFFFF99u), u256(4)), encodeArgs(u256(-6))); - ABI_CHECK(callContractFunction("f(int32,int32)", u256(0xFFFFFF99u), u256(8)), encodeArgs(u256(0))); + ABI_CHECK(callContractFunction("f(int32,int32)", u256(0xFFFFFF99u), u256(1)), encodeArgs(u256(-52))); + ABI_CHECK(callContractFunction("f(int32,int32)", u256(0xFFFFFF99u), u256(2)), encodeArgs(u256(-26))); + ABI_CHECK(callContractFunction("f(int32,int32)", u256(0xFFFFFF99u), u256(4)), encodeArgs(u256(-7))); + ABI_CHECK(callContractFunction("f(int32,int32)", u256(0xFFFFFF99u), u256(8)), encodeArgs(u256(-1))); } @@ -10518,7 +11727,7 @@ BOOST_AUTO_TEST_CASE(shift_right_negative_lvalue_int16) { char const* sourceCode = R"( contract C { - function f(int16 a, int16 b) returns (int) { + function f(int16 a, int16 b) public returns (int) { return a >> b; } } @@ -10526,23 +11735,23 @@ BOOST_AUTO_TEST_CASE(shift_right_negative_lvalue_int16) compileAndRun(sourceCode, 0, "C"); ABI_CHECK(callContractFunction("f(int16,int16)", u256(-4266), u256(0)), encodeArgs(u256(-4266))); ABI_CHECK(callContractFunction("f(int16,int16)", u256(-4266), u256(1)), encodeArgs(u256(-2133))); - ABI_CHECK(callContractFunction("f(int16,int16)", u256(-4266), u256(4)), encodeArgs(u256(-266))); - ABI_CHECK(callContractFunction("f(int16,int16)", u256(-4266), u256(8)), encodeArgs(u256(-16))); - ABI_CHECK(callContractFunction("f(int16,int16)", u256(-4266), u256(16)), encodeArgs(u256(0))); - ABI_CHECK(callContractFunction("f(int16,int16)", u256(-4266), u256(17)), encodeArgs(u256(0))); + ABI_CHECK(callContractFunction("f(int16,int16)", u256(-4266), u256(4)), encodeArgs(u256(-267))); + ABI_CHECK(callContractFunction("f(int16,int16)", u256(-4266), u256(8)), encodeArgs(u256(-17))); + ABI_CHECK(callContractFunction("f(int16,int16)", u256(-4266), u256(16)), encodeArgs(u256(-1))); + ABI_CHECK(callContractFunction("f(int16,int16)", u256(-4266), u256(17)), encodeArgs(u256(-1))); ABI_CHECK(callContractFunction("f(int16,int16)", u256(-4267), u256(0)), encodeArgs(u256(-4267))); - ABI_CHECK(callContractFunction("f(int16,int16)", u256(-4267), u256(1)), encodeArgs(u256(-2133))); - ABI_CHECK(callContractFunction("f(int16,int16)", u256(-4267), u256(4)), encodeArgs(u256(-266))); - ABI_CHECK(callContractFunction("f(int16,int16)", u256(-4267), u256(8)), encodeArgs(u256(-16))); - ABI_CHECK(callContractFunction("f(int16,int16)", u256(-4267), u256(16)), encodeArgs(u256(0))); - ABI_CHECK(callContractFunction("f(int16,int16)", u256(-4267), u256(17)), encodeArgs(u256(0))); + ABI_CHECK(callContractFunction("f(int16,int16)", u256(-4267), u256(1)), encodeArgs(u256(-2134))); + ABI_CHECK(callContractFunction("f(int16,int16)", u256(-4267), u256(4)), encodeArgs(u256(-267))); + ABI_CHECK(callContractFunction("f(int16,int16)", u256(-4267), u256(8)), encodeArgs(u256(-17))); + ABI_CHECK(callContractFunction("f(int16,int16)", u256(-4267), u256(16)), encodeArgs(u256(-1))); + ABI_CHECK(callContractFunction("f(int16,int16)", u256(-4267), u256(17)), encodeArgs(u256(-1))); } BOOST_AUTO_TEST_CASE(shift_right_negative_lvalue_int32) { char const* sourceCode = R"( contract C { - function f(int32 a, int32 b) returns (int) { + function f(int32 a, int32 b) public returns (int) { return a >> b; } } @@ -10550,23 +11759,23 @@ BOOST_AUTO_TEST_CASE(shift_right_negative_lvalue_int32) compileAndRun(sourceCode, 0, "C"); ABI_CHECK(callContractFunction("f(int32,int32)", u256(-4266), u256(0)), encodeArgs(u256(-4266))); ABI_CHECK(callContractFunction("f(int32,int32)", u256(-4266), u256(1)), encodeArgs(u256(-2133))); - ABI_CHECK(callContractFunction("f(int32,int32)", u256(-4266), u256(4)), encodeArgs(u256(-266))); - ABI_CHECK(callContractFunction("f(int32,int32)", u256(-4266), u256(8)), encodeArgs(u256(-16))); - ABI_CHECK(callContractFunction("f(int32,int32)", u256(-4266), u256(16)), encodeArgs(u256(0))); - ABI_CHECK(callContractFunction("f(int32,int32)", u256(-4266), u256(17)), encodeArgs(u256(0))); + ABI_CHECK(callContractFunction("f(int32,int32)", u256(-4266), u256(4)), encodeArgs(u256(-267))); + ABI_CHECK(callContractFunction("f(int32,int32)", u256(-4266), u256(8)), encodeArgs(u256(-17))); + ABI_CHECK(callContractFunction("f(int32,int32)", u256(-4266), u256(16)), encodeArgs(u256(-1))); + ABI_CHECK(callContractFunction("f(int32,int32)", u256(-4266), u256(17)), encodeArgs(u256(-1))); ABI_CHECK(callContractFunction("f(int32,int32)", u256(-4267), u256(0)), encodeArgs(u256(-4267))); - ABI_CHECK(callContractFunction("f(int32,int32)", u256(-4267), u256(1)), encodeArgs(u256(-2133))); - ABI_CHECK(callContractFunction("f(int32,int32)", u256(-4267), u256(4)), encodeArgs(u256(-266))); - ABI_CHECK(callContractFunction("f(int32,int32)", u256(-4267), u256(8)), encodeArgs(u256(-16))); - ABI_CHECK(callContractFunction("f(int32,int32)", u256(-4267), u256(16)), encodeArgs(u256(0))); - ABI_CHECK(callContractFunction("f(int32,int32)", u256(-4267), u256(17)), encodeArgs(u256(0))); + ABI_CHECK(callContractFunction("f(int32,int32)", u256(-4267), u256(1)), encodeArgs(u256(-2134))); + ABI_CHECK(callContractFunction("f(int32,int32)", u256(-4267), u256(4)), encodeArgs(u256(-267))); + ABI_CHECK(callContractFunction("f(int32,int32)", u256(-4267), u256(8)), encodeArgs(u256(-17))); + ABI_CHECK(callContractFunction("f(int32,int32)", u256(-4267), u256(16)), encodeArgs(u256(-1))); + ABI_CHECK(callContractFunction("f(int32,int32)", u256(-4267), u256(17)), encodeArgs(u256(-1))); } BOOST_AUTO_TEST_CASE(shift_right_negative_lvalue_assignment) { char const* sourceCode = R"( contract C { - function f(int a, int b) returns (int) { + function f(int a, int b) public returns (int) { a >>= b; return a; } @@ -10575,26 +11784,26 @@ BOOST_AUTO_TEST_CASE(shift_right_negative_lvalue_assignment) compileAndRun(sourceCode, 0, "C"); ABI_CHECK(callContractFunction("f(int256,int256)", u256(-4266), u256(0)), encodeArgs(u256(-4266))); ABI_CHECK(callContractFunction("f(int256,int256)", u256(-4266), u256(1)), encodeArgs(u256(-2133))); - ABI_CHECK(callContractFunction("f(int256,int256)", u256(-4266), u256(4)), encodeArgs(u256(-266))); - ABI_CHECK(callContractFunction("f(int256,int256)", u256(-4266), u256(8)), encodeArgs(u256(-16))); - ABI_CHECK(callContractFunction("f(int256,int256)", u256(-4266), u256(16)), encodeArgs(u256(0))); - ABI_CHECK(callContractFunction("f(int256,int256)", u256(-4266), u256(17)), encodeArgs(u256(0))); + ABI_CHECK(callContractFunction("f(int256,int256)", u256(-4266), u256(4)), encodeArgs(u256(-267))); + ABI_CHECK(callContractFunction("f(int256,int256)", u256(-4266), u256(8)), encodeArgs(u256(-17))); + ABI_CHECK(callContractFunction("f(int256,int256)", u256(-4266), u256(16)), encodeArgs(u256(-1))); + ABI_CHECK(callContractFunction("f(int256,int256)", u256(-4266), u256(17)), encodeArgs(u256(-1))); ABI_CHECK(callContractFunction("f(int256,int256)", u256(-4267), u256(0)), encodeArgs(u256(-4267))); - ABI_CHECK(callContractFunction("f(int256,int256)", u256(-4267), u256(1)), encodeArgs(u256(-2133))); - ABI_CHECK(callContractFunction("f(int256,int256)", u256(-4267), u256(4)), encodeArgs(u256(-266))); - ABI_CHECK(callContractFunction("f(int256,int256)", u256(-4267), u256(8)), encodeArgs(u256(-16))); - ABI_CHECK(callContractFunction("f(int256,int256)", u256(-4267), u256(16)), encodeArgs(u256(0))); - ABI_CHECK(callContractFunction("f(int256,int256)", u256(-4267), u256(17)), encodeArgs(u256(0))); + ABI_CHECK(callContractFunction("f(int256,int256)", u256(-4267), u256(1)), encodeArgs(u256(-2134))); + ABI_CHECK(callContractFunction("f(int256,int256)", u256(-4267), u256(4)), encodeArgs(u256(-267))); + ABI_CHECK(callContractFunction("f(int256,int256)", u256(-4267), u256(8)), encodeArgs(u256(-17))); + ABI_CHECK(callContractFunction("f(int256,int256)", u256(-4267), u256(16)), encodeArgs(u256(-1))); + ABI_CHECK(callContractFunction("f(int256,int256)", u256(-4267), u256(17)), encodeArgs(u256(-1))); } BOOST_AUTO_TEST_CASE(shift_negative_rvalue) { char const* sourceCode = R"( contract C { - function f(int a, int b) returns (int) { + function f(int a, int b) public returns (int) { return a << b; } - function g(int a, int b) returns (int) { + function g(int a, int b) public returns (int) { return a >> b; } } @@ -10608,11 +11817,11 @@ BOOST_AUTO_TEST_CASE(shift_negative_rvalue_assignment) { char const* sourceCode = R"( contract C { - function f(int a, int b) returns (int) { + function f(int a, int b) public returns (int) { a <<= b; return a; } - function g(int a, int b) returns (int) { + function g(int a, int b) public returns (int) { a >>= b; return a; } @@ -10627,7 +11836,7 @@ BOOST_AUTO_TEST_CASE(shift_constant_left_assignment) { char const* sourceCode = R"( contract C { - function f() returns (uint a) { + function f() public returns (uint a) { a = 0x42; a <<= 8; } @@ -10641,7 +11850,7 @@ BOOST_AUTO_TEST_CASE(shift_constant_right_assignment) { char const* sourceCode = R"( contract C { - function f() returns (uint a) { + function f() public returns (uint a) { a = 0x4200; a >>= 8; } @@ -10655,7 +11864,7 @@ BOOST_AUTO_TEST_CASE(shift_cleanup) { char const* sourceCode = R"( contract C { - function f() returns (uint16 x) { + function f() public returns (uint16 x) { x = 0xffff; x += 32; x <<= 8; @@ -10671,7 +11880,7 @@ BOOST_AUTO_TEST_CASE(shift_cleanup_garbled) { char const* sourceCode = R"( contract C { - function f() returns (uint8 x) { + function f() public returns (uint8 x) { assembly { x := 0xffff } @@ -10687,10 +11896,10 @@ BOOST_AUTO_TEST_CASE(shift_overflow) { char const* sourceCode = R"( contract C { - function leftU(uint8 x, uint8 y) returns (uint8) { + function leftU(uint8 x, uint8 y) public returns (uint8) { return x << y; } - function leftS(int8 x, int8 y) returns (int8) { + function leftS(int8 x, int8 y) public returns (int8) { return x << y; } } @@ -10709,10 +11918,10 @@ BOOST_AUTO_TEST_CASE(shift_bytes) { char const* sourceCode = R"( contract C { - function left(bytes20 x, uint8 y) returns (bytes20) { + function left(bytes20 x, uint8 y) public returns (bytes20) { return x << y; } - function right(bytes20 x, uint8 y) returns (bytes20) { + function right(bytes20 x, uint8 y) public returns (bytes20) { return x >> y; } } @@ -10726,12 +11935,12 @@ BOOST_AUTO_TEST_CASE(shift_bytes_cleanup) { char const* sourceCode = R"( contract C { - function left(uint8 y) returns (bytes20) { + function left(uint8 y) public returns (bytes20) { bytes20 x; assembly { x := "12345678901234567890abcde" } return x << y; } - function right(uint8 y) returns (bytes20) { + function right(uint8 y) public returns (bytes20) { bytes20 x; assembly { x := "12345678901234567890abcde" } return x >> y; @@ -10787,7 +11996,7 @@ BOOST_AUTO_TEST_CASE(cleanup_in_compound_assign) { char const* sourceCode = R"( contract C { - function test() returns (uint, uint) { + function test() public returns (uint, uint) { uint32 a = 0xffffffff; uint16 x = uint16(a); uint16 y = x; @@ -10811,10 +12020,10 @@ BOOST_AUTO_TEST_CASE(inline_assembly_in_modifiers) a := 2 } if (a != 2) - throw; + revert(); _; } - function f() m returns (bool) { + function f() m public returns (bool) { return true; } } @@ -10830,7 +12039,7 @@ BOOST_AUTO_TEST_CASE(packed_storage_overflow) uint16 x = 0x1234; uint16 a = 0xffff; uint16 b; - function f() returns (uint, uint, uint, uint) { + function f() public returns (uint, uint, uint, uint) { a++; uint c = b; delete b; @@ -10862,18 +12071,18 @@ BOOST_AUTO_TEST_CASE(include_creation_bytecode_only_once) contract D { bytes a = hex"1237651237125387136581271652831736512837126583171583712358126123765123712538713658127165283173651283712658317158371235812612376512371253871365812716528317365128371265831715837123581261237651237125387136581271652831736512837126583171583712358126"; bytes b = hex"1237651237125327136581271252831736512837126583171383712358126123765125712538713658127165253173651283712658357158371235812612376512371a5387136581271652a317365128371265a317158371235812612a765123712538a13658127165a83173651283712a58317158371235a126"; - function D(uint) {} + constructor(uint) public {} } contract Double { - function f() { + function f() public { new D(2); } - function g() { + function g() public { new D(3); } } contract Single { - function f() { + function f() public { new D(2); } } @@ -10893,7 +12102,7 @@ BOOST_AUTO_TEST_CASE(recursive_structs) S[] x; } S sstorage; - function f() returns (uint) { + function f() public returns (uint) { S memory s; s.x = new S[](10); delete s; @@ -10911,9 +12120,9 @@ BOOST_AUTO_TEST_CASE(invalid_instruction) { char const* sourceCode = R"( contract C { - function f() { + function f() public { assembly { - invalid + invalid() } } } @@ -10926,14 +12135,14 @@ BOOST_AUTO_TEST_CASE(assert_require) { char const* sourceCode = R"( contract C { - function f() { + function f() public { assert(false); } - function g(bool val) returns (bool) { + function g(bool val) public returns (bool) { assert(val == true); return true; } - function h(bool val) returns (bool) { + function h(bool val) public returns (bool) { require(val); return true; } @@ -10952,11 +12161,11 @@ BOOST_AUTO_TEST_CASE(revert) char const* sourceCode = R"( contract C { uint public a = 42; - function f() { + function f() public { a = 1; revert(); } - function g() { + function g() public { a = 1; assembly { revert(0, 0) @@ -10996,7 +12205,7 @@ BOOST_AUTO_TEST_CASE(revert_with_cause) } contract C { D d = new D(); - function forward(address target, bytes data) internal returns (bool success, bytes retval) { + function forward(address target, bytes memory data) internal returns (bool success, bytes memory retval) { uint retsize; assembly { success := call(not(0), target, 0, add(data, 0x20), mload(data), 0, 0) @@ -11007,19 +12216,19 @@ BOOST_AUTO_TEST_CASE(revert_with_cause) returndatacopy(add(retval, 0x20), 0, returndatasize()) } } - function f() public returns (bool, bytes) { + function f() public returns (bool, bytes memory) { return forward(address(d), msg.data); } - function g() public returns (bool, bytes) { + function g() public returns (bool, bytes memory) { return forward(address(d), msg.data); } - function h() public returns (bool, bytes) { + function h() public returns (bool, bytes memory) { return forward(address(d), msg.data); } - function i() public returns (bool, bytes) { + function i() public returns (bool, bytes memory) { return forward(address(d), msg.data); } - function j() public returns (bool, bytes) { + function j() public returns (bool, bytes memory) { return forward(address(d), msg.data); } } @@ -11051,7 +12260,7 @@ BOOST_AUTO_TEST_CASE(require_with_message) bool flagCopy = flag; require(flagCopy == false, internalFun()); } - function internalFun() returns (string) { + function internalFun() public returns (string memory) { flag = true; return "only on second run"; } @@ -11068,7 +12277,7 @@ BOOST_AUTO_TEST_CASE(require_with_message) } contract C { D d = new D(); - function forward(address target, bytes data) internal returns (bool success, bytes retval) { + function forward(address target, bytes memory data) internal returns (bool success, bytes memory retval) { uint retsize; assembly { success := call(not(0), target, 0, add(data, 0x20), mload(data), 0, 0) @@ -11079,19 +12288,19 @@ BOOST_AUTO_TEST_CASE(require_with_message) returndatacopy(add(retval, 0x20), 0, returndatasize()) } } - function f(uint x) public returns (bool, bytes) { + function f(uint x) public returns (bool, bytes memory) { return forward(address(d), msg.data); } - function g() public returns (bool, bytes) { + function g() public returns (bool, bytes memory) { return forward(address(d), msg.data); } - function h() public returns (bool, bytes) { + function h() public returns (bool, bytes memory) { return forward(address(d), msg.data); } - function i() public returns (bool, bytes) { + function i() public returns (bool, bytes memory) { return forward(address(d), msg.data); } - function j() public returns (bool, bytes) { + function j() public returns (bool, bytes memory) { return forward(address(d), msg.data); } } @@ -11121,7 +12330,7 @@ BOOST_AUTO_TEST_CASE(bubble_up_error_messages) } contract C { D d = new D(); - function forward(address target, bytes data) internal returns (bool success, bytes retval) { + function forward(address target, bytes memory data) internal returns (bool success, bytes memory retval) { uint retsize; assembly { success := call(not(0), target, 0, add(data, 0x20), mload(data), 0, 0) @@ -11132,10 +12341,10 @@ BOOST_AUTO_TEST_CASE(bubble_up_error_messages) returndatacopy(add(retval, 0x20), 0, returndatasize()) } } - function f() public returns (bool, bytes) { + function f() public returns (bool, bytes memory) { return forward(address(d), msg.data); } - function g() public returns (bool, bytes) { + function g() public returns (bool, bytes memory) { return forward(address(d), msg.data); } } @@ -11151,16 +12360,16 @@ BOOST_AUTO_TEST_CASE(bubble_up_error_messages_through_transfer) { char const* sourceCode = R"( contract D { - function() public payable { + function() external payable { revert("message"); } function f() public { - this.transfer(0); + address(this).transfer(0); } } contract C { D d = new D(); - function forward(address target, bytes data) internal returns (bool success, bytes retval) { + function forward(address target, bytes memory data) internal returns (bool success, bytes memory retval) { uint retsize; assembly { success := call(not(0), target, 0, add(data, 0x20), mload(data), 0, 0) @@ -11171,7 +12380,7 @@ BOOST_AUTO_TEST_CASE(bubble_up_error_messages_through_transfer) returndatacopy(add(retval, 0x20), 0, returndatasize()) } } - function f() public returns (bool, bytes) { + function f() public returns (bool, bytes memory) { return forward(address(d), msg.data); } } @@ -11186,18 +12395,18 @@ BOOST_AUTO_TEST_CASE(bubble_up_error_messages_through_create) { char const* sourceCode = R"( contract E { - function E() { + constructor() public { revert("message"); } } contract D { function f() public { - var x = new E(); + E x = new E(); } } contract C { D d = new D(); - function forward(address target, bytes data) internal returns (bool success, bytes retval) { + function forward(address target, bytes memory data) internal returns (bool success, bytes memory retval) { uint retsize; assembly { success := call(not(0), target, 0, add(data, 0x20), mload(data), 0, 0) @@ -11208,7 +12417,7 @@ BOOST_AUTO_TEST_CASE(bubble_up_error_messages_through_create) returndatacopy(add(retval, 0x20), 0, returndatasize()) } } - function f() public returns (bool, bytes) { + function f() public returns (bool, bytes memory) { return forward(address(d), msg.data); } } @@ -11232,9 +12441,9 @@ BOOST_AUTO_TEST_CASE(negative_stack_height) bool Aboolc; bool exists; } - function nredit(uint startindex) public pure returns(uint[500] CIDs, uint[500] dates, uint[500] RIDs, bool[500] Cboolas, uint[500] amounts){} - function return500InvoicesByDates(uint begindate, uint enddate, uint startindex) public view returns(uint[500] AIDs, bool[500] Aboolas, uint[500] dates, bytes32[3][500] Abytesas, bytes32[3][500] bytesbs, bytes32[2][500] bytescs, uint[500] amounts, bool[500] Aboolbs, bool[500] Aboolcs){} - function return500PaymentsByDates(uint begindate, uint enddate, uint startindex) public view returns(uint[500] BIDs, uint[500] dates, uint[500] RIDs, bool[500] Bboolas, bytes32[3][500] bytesbs,bytes32[2][500] bytescs, uint[500] amounts, bool[500] Bboolbs){} + function nredit(uint startindex) public pure returns(uint[500] memory CIDs, uint[500] memory dates, uint[500] memory RIDs, bool[500] memory Cboolas, uint[500] memory amounts){} + function return500InvoicesByDates(uint begindate, uint enddate, uint startindex) public view returns(uint[500] memory AIDs, bool[500] memory Aboolas, uint[500] memory dates, bytes32[3][500] memory Abytesas, bytes32[3][500] memory bytesbs, bytes32[2][500] memory bytescs, uint[500] memory amounts, bool[500] memory Aboolbs, bool[500] memory Aboolcs){} + function return500PaymentsByDates(uint begindate, uint enddate, uint startindex) public view returns(uint[500] memory BIDs, uint[500] memory dates, uint[500] memory RIDs, bool[500] memory Bboolas, bytes32[3][500] memory bytesbs,bytes32[2][500] memory bytescs, uint[500] memory amounts, bool[500] memory Bboolbs){} } )"; compileAndRun(sourceCode, 0, "C"); @@ -11246,11 +12455,11 @@ BOOST_AUTO_TEST_CASE(literal_empty_string) contract C { bytes32 public x; uint public a; - function f(bytes32 _x, uint _a) { + function f(bytes32 _x, uint _a) public { x = _x; a = _a; } - function g() { + function g() public { this.f("", 2); } } @@ -11267,22 +12476,22 @@ BOOST_AUTO_TEST_CASE(scientific_notation) { char const* sourceCode = R"( contract C { - function f() returns (uint) { + function f() public returns (uint) { return 2e10 wei; } - function g() returns (uint) { + function g() public returns (uint) { return 200e-2 wei; } - function h() returns (uint) { + function h() public returns (uint) { return 2.5e1; } - function i() returns (int) { + function i() public returns (int) { return -2e10; } - function j() returns (int) { + function j() public returns (int) { return -200e-2; } - function k() returns (int) { + function k() public returns (int) { return -2.5e1; } } @@ -11301,25 +12510,25 @@ BOOST_AUTO_TEST_CASE(interface_contract) char const* sourceCode = R"( interface I { event A(); - function f() returns (bool); - function() payable; + function f() external returns (bool); + function() external payable; } contract A is I { - function f() returns (bool) { + function f() public returns (bool) { return g(); } - function g() returns (bool) { + function g() public returns (bool) { return true; } - function() payable { + function() external payable { } } contract C { - function f(address _interfaceAddress) returns (bool) { + function f(address payable _interfaceAddress) public returns (bool) { I i = I(_interfaceAddress); return i.f(); } @@ -11335,39 +12544,15 @@ BOOST_AUTO_TEST_CASE(keccak256_assembly) { char const* sourceCode = R"( contract C { - function f() pure returns (bytes32 ret) { + function f() public pure returns (bytes32 ret) { assembly { ret := keccak256(0, 0) } } - function g() pure returns (bytes32 ret) { - assembly { - 0 - 0 - keccak256 - =: ret - } - } - function h() pure returns (bytes32 ret) { - assembly { - ret := sha3(0, 0) - } - } - function i() pure returns (bytes32 ret) { - assembly { - 0 - 0 - sha3 - =: ret - } - } } )"; compileAndRun(sourceCode, 0, "C"); ABI_CHECK(callContractFunction("f()"), fromHex("0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470")); - ABI_CHECK(callContractFunction("g()"), fromHex("0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470")); - ABI_CHECK(callContractFunction("h()"), fromHex("0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470")); - ABI_CHECK(callContractFunction("i()"), fromHex("0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470")); } BOOST_AUTO_TEST_CASE(multi_modifiers) @@ -11382,10 +12567,10 @@ BOOST_AUTO_TEST_CASE(multi_modifiers) x++; _; } - function f1() m1() { + function f1() m1() public { x += 7; } - function f2() m1() { + function f2() m1() public { x += 3; } } @@ -11401,7 +12586,7 @@ BOOST_AUTO_TEST_CASE(inlineasm_empty_let) { char const* sourceCode = R"( contract C { - function f() pure returns (uint a, uint b) { + function f() public pure returns (uint a, uint b) { assembly { let x let y, z @@ -11417,60 +12602,238 @@ BOOST_AUTO_TEST_CASE(inlineasm_empty_let) BOOST_AUTO_TEST_CASE(bare_call_invalid_address) { - char const* sourceCode = R"( + char const* sourceCode = R"YY( contract C { /// Calling into non-existant account is successful (creates the account) function f() external returns (bool) { - return address(0x4242).call(); - } - function g() external returns (bool) { - return address(0x4242).callcode(); + (bool success,) = address(0x4242).call(""); + return success; } function h() external returns (bool) { - return address(0x4242).delegatecall(); + (bool success,) = address(0x4242).delegatecall(""); + return success; } } - )"; + )YY"; compileAndRun(sourceCode, 0, "C"); ABI_CHECK(callContractFunction("f()"), encodeArgs(u256(1))); - ABI_CHECK(callContractFunction("g()"), encodeArgs(u256(1))); ABI_CHECK(callContractFunction("h()"), encodeArgs(u256(1))); + + if (dev::test::Options::get().evmVersion().hasStaticCall()) + { + char const* sourceCode = R"YY( + contract C { + function f() external returns (bool, bytes memory) { + return address(0x4242).staticcall(""); + } + } + )YY"; + compileAndRun(sourceCode, 0, "C"); + ABI_CHECK(callContractFunction("f()"), encodeArgs(u256(1), 0x40, 0x00)); + } +} + +BOOST_AUTO_TEST_CASE(bare_call_return_data) +{ + if (dev::test::Options::get().evmVersion().supportsReturndata()) + { + vector<string> calltypes = {"call", "delegatecall"}; + if (dev::test::Options::get().evmVersion().hasStaticCall()) + calltypes.emplace_back("staticcall"); + for (string const& calltype: calltypes) + { + string sourceCode = R"DELIMITER( + contract A { + constructor() public { + } + function return_bool() public pure returns(bool) { + return true; + } + function return_int32() public pure returns(int32) { + return -32; + } + function return_uint32() public pure returns(uint32) { + return 0x3232; + } + function return_int256() public pure returns(int256) { + return -256; + } + function return_uint256() public pure returns(uint256) { + return 0x256256; + } + function return_bytes4() public pure returns(bytes4) { + return 0xabcd0012; + } + function return_multi() public pure returns(bool, uint32, bytes4) { + return (false, 0x3232, 0xabcd0012); + } + function return_bytes() public pure returns(bytes memory b) { + b = new bytes(2); + b[0] = 0x42; + b[1] = 0x21; + } + } + contract C { + A addr; + constructor() public { + addr = new A(); + } + function f(string memory signature) public returns (bool, bytes memory) { + return address(addr).)DELIMITER" + calltype + R"DELIMITER((abi.encodeWithSignature(signature)); + } + function check_bool() external returns (bool) { + (bool success, bytes memory data) = f("return_bool()"); + assert(success); + bool a = abi.decode(data, (bool)); + assert(a); + return true; + } + function check_int32() external returns (bool) { + (bool success, bytes memory data) = f("return_int32()"); + assert(success); + int32 a = abi.decode(data, (int32)); + assert(a == -32); + return true; + } + function check_uint32() external returns (bool) { + (bool success, bytes memory data) = f("return_uint32()"); + assert(success); + uint32 a = abi.decode(data, (uint32)); + assert(a == 0x3232); + return true; + } + function check_int256() external returns (bool) { + (bool success, bytes memory data) = f("return_int256()"); + assert(success); + int256 a = abi.decode(data, (int256)); + assert(a == -256); + return true; + } + function check_uint256() external returns (bool) { + (bool success, bytes memory data) = f("return_uint256()"); + assert(success); + uint256 a = abi.decode(data, (uint256)); + assert(a == 0x256256); + return true; + } + function check_bytes4() external returns (bool) { + (bool success, bytes memory data) = f("return_bytes4()"); + assert(success); + bytes4 a = abi.decode(data, (bytes4)); + assert(a == 0xabcd0012); + return true; + } + function check_multi() external returns (bool) { + (bool success, bytes memory data) = f("return_multi()"); + assert(success); + (bool a, uint32 b, bytes4 c) = abi.decode(data, (bool, uint32, bytes4)); + assert(a == false && b == 0x3232 && c == 0xabcd0012); + return true; + } + function check_bytes() external returns (bool) { + (bool success, bytes memory data) = f("return_bytes()"); + assert(success); + (bytes memory d) = abi.decode(data, (bytes)); + assert(d.length == 2 && d[0] == 0x42 && d[1] == 0x21); + return true; + } + } + )DELIMITER"; + compileAndRun(sourceCode, 0, "C"); + ABI_CHECK(callContractFunction("f(string)", encodeDyn(string("return_bool()"))), encodeArgs(true, 0x40, 0x20, true)); + ABI_CHECK(callContractFunction("f(string)", encodeDyn(string("return_int32()"))), encodeArgs(true, 0x40, 0x20, u256(-32))); + ABI_CHECK(callContractFunction("f(string)", encodeDyn(string("return_uint32()"))), encodeArgs(true, 0x40, 0x20, u256(0x3232))); + ABI_CHECK(callContractFunction("f(string)", encodeDyn(string("return_int256()"))), encodeArgs(true, 0x40, 0x20, u256(-256))); + ABI_CHECK(callContractFunction("f(string)", encodeDyn(string("return_uint256()"))), encodeArgs(true, 0x40, 0x20, u256(0x256256))); + ABI_CHECK(callContractFunction("f(string)", encodeDyn(string("return_bytes4()"))), encodeArgs(true, 0x40, 0x20, u256(0xabcd0012) << (28*8))); + ABI_CHECK(callContractFunction("f(string)", encodeDyn(string("return_multi()"))), encodeArgs(true, 0x40, 0x60, false, u256(0x3232), u256(0xabcd0012) << (28*8))); + ABI_CHECK(callContractFunction("f(string)", encodeDyn(string("return_bytes()"))), encodeArgs(true, 0x40, 0x60, 0x20, 0x02, encode(bytes{0x42,0x21}, false))); + ABI_CHECK(callContractFunction("check_bool()"), encodeArgs(true)); + ABI_CHECK(callContractFunction("check_int32()"), encodeArgs(true)); + ABI_CHECK(callContractFunction("check_uint32()"), encodeArgs(true)); + ABI_CHECK(callContractFunction("check_int256()"), encodeArgs(true)); + ABI_CHECK(callContractFunction("check_uint256()"), encodeArgs(true)); + ABI_CHECK(callContractFunction("check_bytes4()"), encodeArgs(true)); + ABI_CHECK(callContractFunction("check_multi()"), encodeArgs(true)); + ABI_CHECK(callContractFunction("check_bytes()"), encodeArgs(true)); + } + } } BOOST_AUTO_TEST_CASE(delegatecall_return_value) { - char const* sourceCode = R"DELIMITER( - contract C { - uint value; - function set(uint _value) external { - value = _value; - } - function get() external view returns (uint) { - return value; - } - function get_delegated() external returns (bool) { - return this.delegatecall(bytes4(sha3("get()"))); - } - function assert0() external view { - assert(value == 0); + if (dev::test::Options::get().evmVersion().supportsReturndata()) + { + char const* sourceCode = R"DELIMITER( + contract C { + uint value; + function set(uint _value) external { + value = _value; + } + function get() external view returns (uint) { + return value; + } + function get_delegated() external returns (bool, bytes memory) { + return address(this).delegatecall(abi.encodeWithSignature("get()")); + } + function assert0() external view { + assert(value == 0); + } + function assert0_delegated() external returns (bool, bytes memory) { + return address(this).delegatecall(abi.encodeWithSignature("assert0()")); + } } - function assert0_delegated() external returns (bool) { - return this.delegatecall(bytes4(sha3("assert0()"))); + )DELIMITER"; + compileAndRun(sourceCode, 0, "C"); + ABI_CHECK(callContractFunction("get()"), encodeArgs(u256(0))); + ABI_CHECK(callContractFunction("assert0_delegated()"), encodeArgs(u256(1), 0x40, 0x00)); + ABI_CHECK(callContractFunction("get_delegated()"), encodeArgs(u256(1), 0x40, 0x20, 0x00)); + ABI_CHECK(callContractFunction("set(uint256)", u256(1)), encodeArgs()); + ABI_CHECK(callContractFunction("get()"), encodeArgs(u256(1))); + ABI_CHECK(callContractFunction("assert0_delegated()"), encodeArgs(u256(0), 0x40, 0x00)); + ABI_CHECK(callContractFunction("get_delegated()"), encodeArgs(u256(1), 0x40, 0x20, 1)); + ABI_CHECK(callContractFunction("set(uint256)", u256(42)), encodeArgs()); + ABI_CHECK(callContractFunction("get()"), encodeArgs(u256(42))); + ABI_CHECK(callContractFunction("assert0_delegated()"), encodeArgs(u256(0), 0x40, 0x00)); + ABI_CHECK(callContractFunction("get_delegated()"), encodeArgs(u256(1), 0x40, 0x20, 42)); + } + else + { + char const* sourceCode = R"DELIMITER( + contract C { + uint value; + function set(uint _value) external { + value = _value; + } + function get() external view returns (uint) { + return value; + } + function get_delegated() external returns (bool) { + (bool success,) = address(this).delegatecall(abi.encodeWithSignature("get()")); + return success; + } + function assert0() external view { + assert(value == 0); + } + function assert0_delegated() external returns (bool) { + (bool success,) = address(this).delegatecall(abi.encodeWithSignature("assert0()")); + return success; + } } - } - )DELIMITER"; - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("get()"), encodeArgs(u256(0))); - ABI_CHECK(callContractFunction("assert0_delegated()"), encodeArgs(u256(1))); - ABI_CHECK(callContractFunction("get_delegated()"), encodeArgs(u256(1))); - ABI_CHECK(callContractFunction("set(uint256)", u256(1)), encodeArgs()); - ABI_CHECK(callContractFunction("get()"), encodeArgs(u256(1))); - ABI_CHECK(callContractFunction("assert0_delegated()"), encodeArgs(u256(0))); - ABI_CHECK(callContractFunction("get_delegated()"), encodeArgs(u256(1))); - ABI_CHECK(callContractFunction("set(uint256)", u256(42)), encodeArgs()); - ABI_CHECK(callContractFunction("get()"), encodeArgs(u256(42))); - ABI_CHECK(callContractFunction("assert0_delegated()"), encodeArgs(u256(0))); - ABI_CHECK(callContractFunction("get_delegated()"), encodeArgs(u256(1))); + )DELIMITER"; + compileAndRun(sourceCode, 0, "C"); + ABI_CHECK(callContractFunction("get()"), encodeArgs(u256(0))); + ABI_CHECK(callContractFunction("assert0_delegated()"), encodeArgs(u256(1))); + ABI_CHECK(callContractFunction("get_delegated()"), encodeArgs(u256(1))); + ABI_CHECK(callContractFunction("set(uint256)", u256(1)), encodeArgs()); + ABI_CHECK(callContractFunction("get()"), encodeArgs(u256(1))); + ABI_CHECK(callContractFunction("assert0_delegated()"), encodeArgs(u256(0))); + ABI_CHECK(callContractFunction("get_delegated()"), encodeArgs(u256(1))); + ABI_CHECK(callContractFunction("set(uint256)", u256(42)), encodeArgs()); + ABI_CHECK(callContractFunction("get()"), encodeArgs(u256(42))); + ABI_CHECK(callContractFunction("assert0_delegated()"), encodeArgs(u256(0))); + ABI_CHECK(callContractFunction("get_delegated()"), encodeArgs(u256(1))); + } } BOOST_AUTO_TEST_CASE(function_types_sig) @@ -11478,19 +12841,18 @@ BOOST_AUTO_TEST_CASE(function_types_sig) char const* sourceCode = R"( contract C { uint public x; - function f() pure returns (bytes4) { + function f() public pure returns (bytes4) { return this.f.selector; } - function g() returns (bytes4) { + function g() public returns (bytes4) { function () pure external returns (bytes4) fun = this.f; return fun.selector; } - function h() returns (bytes4) { + function h() public returns (bytes4) { function () pure external returns (bytes4) fun = this.f; - var funvar = fun; - return funvar.selector; + return fun.selector; } - function i() pure returns (bytes4) { + function i() public pure returns (bytes4) { return this.x.selector; } } @@ -11509,13 +12871,13 @@ BOOST_AUTO_TEST_CASE(constant_string) bytes constant a = "\x03\x01\x02"; bytes constant b = hex"030102"; string constant c = "hello"; - function f() returns (bytes) { + function f() public returns (bytes memory) { return a; } - function g() returns (bytes) { + function g() public returns (bytes memory) { return b; } - function h() returns (bytes) { + function h() public returns (bytes memory) { return bytes(c); } } @@ -11530,18 +12892,18 @@ BOOST_AUTO_TEST_CASE(address_overload_resolution) { char const* sourceCode = R"( contract C { - function balance() returns (uint) { + function balance() public returns (uint) { return 1; } - function transfer(uint amount) returns (uint) { + function transfer(uint amount) public returns (uint) { return amount; } } contract D { - function f() returns (uint) { + function f() public returns (uint) { return (new C()).balance(); } - function g() returns (uint) { + function g() public returns (uint) { return (new C()).transfer(5); } } @@ -11566,12 +12928,12 @@ BOOST_AUTO_TEST_CASE(snark) } /// @return the generator of G1 - function P1() internal returns (G1Point) { + function P1() internal returns (G1Point memory) { return G1Point(1, 2); } /// @return the generator of G2 - function P2() internal returns (G2Point) { + function P2() internal returns (G2Point memory) { return G2Point( [11559732032986387107991004021392285783925812861821192530917403151452391805634, 10857046999023057135944570762232829481370756359578518086990519993285655852781], @@ -11581,7 +12943,7 @@ BOOST_AUTO_TEST_CASE(snark) } /// @return the negation of p, i.e. p.add(p.negate()) should be zero. - function negate(G1Point p) internal returns (G1Point) { + function negate(G1Point memory p) internal returns (G1Point memory) { // The prime q in the base field F_q for G1 uint q = 21888242871839275222246405745257275088696311157297823662689037894645226208583; if (p.X == 0 && p.Y == 0) @@ -11590,7 +12952,7 @@ BOOST_AUTO_TEST_CASE(snark) } /// @return the sum of two points of G1 - function add(G1Point p1, G1Point p2) internal returns (G1Point r) { + function add(G1Point memory p1, G1Point memory p2) internal returns (G1Point memory r) { uint[4] memory input; input[0] = p1.X; input[1] = p1.Y; @@ -11607,7 +12969,7 @@ BOOST_AUTO_TEST_CASE(snark) /// @return the product of a point on G1 and a scalar, i.e. /// p == p.mul(1) and p.add(p) == p.mul(2) for all points p. - function mul(G1Point p, uint s) internal returns (G1Point r) { + function mul(G1Point memory p, uint s) internal returns (G1Point memory r) { uint[3] memory input; input[0] = p.X; input[1] = p.Y; @@ -11625,7 +12987,7 @@ BOOST_AUTO_TEST_CASE(snark) /// e(p1[0], p2[0]) * .... * e(p1[n], p2[n]) == 1 /// For example pairing([P1(), P1().negate()], [P2(), P2()]) should /// return true. - function pairing(G1Point[] p1, G2Point[] p2) internal returns (bool) { + function pairing(G1Point[] memory p1, G2Point[] memory p2) internal returns (bool) { require(p1.length == p2.length); uint elements = p1.length; uint inputSize = p1.length * 6; @@ -11649,7 +13011,7 @@ BOOST_AUTO_TEST_CASE(snark) require(success); return out[0] != 0; } - function pairingProd2(G1Point a1, G2Point a2, G1Point b1, G2Point b2) internal returns (bool) { + function pairingProd2(G1Point memory a1, G2Point memory a2, G1Point memory b1, G2Point memory b2) internal returns (bool) { G1Point[] memory p1 = new G1Point[](2); G2Point[] memory p2 = new G2Point[](2); p1[0] = a1; @@ -11659,9 +13021,9 @@ BOOST_AUTO_TEST_CASE(snark) return pairing(p1, p2); } function pairingProd3( - G1Point a1, G2Point a2, - G1Point b1, G2Point b2, - G1Point c1, G2Point c2 + G1Point memory a1, G2Point memory a2, + G1Point memory b1, G2Point memory b2, + G1Point memory c1, G2Point memory c2 ) internal returns (bool) { G1Point[] memory p1 = new G1Point[](3); G2Point[] memory p2 = new G2Point[](3); @@ -11674,10 +13036,10 @@ BOOST_AUTO_TEST_CASE(snark) return pairing(p1, p2); } function pairingProd4( - G1Point a1, G2Point a2, - G1Point b1, G2Point b2, - G1Point c1, G2Point c2, - G1Point d1, G2Point d2 + G1Point memory a1, G2Point memory a2, + G1Point memory b1, G2Point memory b2, + G1Point memory c1, G2Point memory c2, + G1Point memory d1, G2Point memory d2 ) internal returns (bool) { G1Point[] memory p1 = new G1Point[](4); G2Point[] memory p2 = new G2Point[](4); @@ -11715,22 +13077,22 @@ BOOST_AUTO_TEST_CASE(snark) Pairing.G1Point K; Pairing.G1Point H; } - function f() returns (bool) { + function f() public returns (bool) { Pairing.G1Point memory p1; Pairing.G1Point memory p2; p1.X = 1; p1.Y = 2; p2.X = 1; p2.Y = 2; - var explict_sum = Pairing.add(p1, p2); - var scalar_prod = Pairing.mul(p1, 2); + Pairing.G1Point memory explict_sum = Pairing.add(p1, p2); + Pairing.G1Point memory scalar_prod = Pairing.mul(p1, 2); return (explict_sum.X == scalar_prod.X && explict_sum.Y == scalar_prod.Y); } - function g() returns (bool) { + function g() public returns (bool) { Pairing.G1Point memory x = Pairing.add(Pairing.P1(), Pairing.negate(Pairing.P1())); // should be zero return (x.X == 0 && x.Y == 0); } - function testMul() returns (bool) { + function testMul() public returns (bool) { Pairing.G1Point memory p; // @TODO The points here are reported to be not well-formed p.X = 14125296762497065001182820090155008161146766663259912659363835465243039841726; @@ -11740,7 +13102,7 @@ BOOST_AUTO_TEST_CASE(snark) p.X == 18256332256630856740336504687838346961237861778318632856900758565550522381207 && p.Y == 6976682127058094634733239494758371323697222088503263230319702770853579280803; } - function pair() returns (bool) { + function pair() public returns (bool) { Pairing.G2Point memory fiveTimesP2 = Pairing.G2Point( [4540444681147253467785307942530223364530218361853237193970751657229138047649, 20954117799226682825035885491234530437475518021362091509513177301640194298072], [11631839690097995216017572651900167465857396346217730511548857041925508482915, 21508930868448350162258892668132814424284302804699005394342512102884055673846] @@ -11766,7 +13128,7 @@ BOOST_AUTO_TEST_CASE(snark) return false; return true; } - function verifyingKey() internal returns (VerifyingKey vk) { + function verifyingKey() internal returns (VerifyingKey memory vk) { vk.A = Pairing.G2Point([0x209dd15ebff5d46c4bd888e51a93cf99a7329636c63514396b4a452003a35bf7, 0x04bf11ca01483bfa8b34b43561848d28905960114c8ac04049af4b6315a41678], [0x2bb8324af6cfc93537a2ad1a445cfd0ca2a71acd7ac41fadbf933c2a51be344d, 0x120a2a4cf30c1bf9845f20c6fe39e07ea2cce61f0c9bb048165fe5e4de877550]); vk.B = Pairing.G1Point(0x2eca0c7238bf16e83e7a1e6c5d49540685ff51380f309842a98561558019fc02, 0x03d3260361bb8451de5ff5ecd17f010ff22f5c31cdf184e9020b06fa5997db84); vk.C = Pairing.G2Point([0x2e89718ad33c8bed92e210e81d1853435399a271913a6520736a4729cf0d51eb, 0x01a9e2ffa2e92599b68e44de5bcf354fa2642bd4f26b259daa6f7ce3ed57aeb3], [0x14a9a87b789a58af499b314e13c3d65bede56c07ea2d418d6874857b70763713, 0x178fb49a2d6cd347dc58973ff49613a20757d0fcc22079f9abd10c3baee24590]); @@ -11786,7 +13148,7 @@ BOOST_AUTO_TEST_CASE(snark) vk.IC[8] = Pairing.G1Point(0x0a6de0e2240aa253f46ce0da883b61976e3588146e01c9d8976548c145fe6e4a, 0x04fbaa3a4aed4bb77f30ebb07a3ec1c7d77a7f2edd75636babfeff97b1ea686e); vk.IC[9] = Pairing.G1Point(0x111e2e2a5f8828f80ddad08f9f74db56dac1cc16c1cb278036f79a84cf7a116f, 0x1d7d62e192b219b9808faa906c5ced871788f6339e8d91b83ac1343e20a16b30); } - function verify(uint[] input, Proof proof) internal returns (uint) { + function verify(uint[] memory input, Proof memory proof) internal returns (uint) { VerifyingKey memory vk = verifyingKey(); require(input.length + 1 == vk.IC.length); // Compute the linear combination vk_x @@ -11810,7 +13172,7 @@ BOOST_AUTO_TEST_CASE(snark) return 0; } event Verified(string); - function verifyTx() returns (bool) { + function verifyTx() public returns (bool) { uint[] memory input = new uint[](9); Proof memory proof; proof.A = Pairing.G1Point(12873740738727497448187997291915224677121726020054032516825496230827252793177, 21804419174137094775122804775419507726154084057848719988004616848382402162497); @@ -11833,7 +13195,7 @@ BOOST_AUTO_TEST_CASE(snark) input[7] = 9643208548031422463313148630985736896287522941726746581856185889848792022807; input[8] = 18066496933330839731877828156604; if (verify(input, proof) == 0) { - Verified("Transaction successfully verified."); + emit Verified("Transaction successfully verified."); return true; } else { return false; @@ -11856,17 +13218,17 @@ BOOST_AUTO_TEST_CASE(abi_encode) { char const* sourceCode = R"( contract C { - function f0() returns (bytes) { + function f0() public returns (bytes memory) { return abi.encode(); } - function f1() returns (bytes) { + function f1() public returns (bytes memory) { return abi.encode(1, 2); } - function f2() returns (bytes) { + function f2() public returns (bytes memory) { string memory x = "abc"; return abi.encode(1, x, 2); } - function f3() returns (bytes r) { + function f3() public returns (bytes memory r) { // test that memory is properly allocated string memory x = "abc"; r = abi.encode(1, x, 2); @@ -11875,7 +13237,7 @@ BOOST_AUTO_TEST_CASE(abi_encode) y[0] = "e"; require(y[0] == "e"); } - function f4() returns (bytes) { + function f4() public returns (bytes memory) { bytes4 x = "abcd"; return abi.encode(bytes2(x)); } @@ -11895,17 +13257,17 @@ BOOST_AUTO_TEST_CASE(abi_encode_v2) pragma experimental ABIEncoderV2; contract C { struct S { uint a; uint[] b; } - function f0() public pure returns (bytes) { + function f0() public pure returns (bytes memory) { return abi.encode(); } - function f1() public pure returns (bytes) { + function f1() public pure returns (bytes memory) { return abi.encode(1, 2); } - function f2() public pure returns (bytes) { + function f2() public pure returns (bytes memory) { string memory x = "abc"; return abi.encode(1, x, 2); } - function f3() public pure returns (bytes r) { + function f3() public pure returns (bytes memory r) { // test that memory is properly allocated string memory x = "abc"; r = abi.encode(1, x, 2); @@ -11915,7 +13277,7 @@ BOOST_AUTO_TEST_CASE(abi_encode_v2) require(y[0] == "e"); } S s; - function f4() public returns (bytes r) { + function f4() public returns (bytes memory r) { string memory x = "abc"; s.a = 7; s.b.push(2); @@ -11941,17 +13303,17 @@ BOOST_AUTO_TEST_CASE(abi_encodePacked) { char const* sourceCode = R"( contract C { - function f0() public pure returns (bytes) { + function f0() public pure returns (bytes memory) { return abi.encodePacked(); } - function f1() public pure returns (bytes) { + function f1() public pure returns (bytes memory) { return abi.encodePacked(uint8(1), uint8(2)); } - function f2() public pure returns (bytes) { + function f2() public pure returns (bytes memory) { string memory x = "abc"; return abi.encodePacked(uint8(1), x, uint8(2)); } - function f3() public pure returns (bytes r) { + function f3() public pure returns (bytes memory r) { // test that memory is properly allocated string memory x = "abc"; r = abi.encodePacked(uint8(1), x, uint8(2)); @@ -11973,17 +13335,17 @@ BOOST_AUTO_TEST_CASE(abi_encode_with_selector) { char const* sourceCode = R"( contract C { - function f0() public pure returns (bytes) { + function f0() public pure returns (bytes memory) { return abi.encodeWithSelector(0x12345678); } - function f1() public pure returns (bytes) { + function f1() public pure returns (bytes memory) { return abi.encodeWithSelector(0x12345678, "abc"); } - function f2() public pure returns (bytes) { + function f2() public pure returns (bytes memory) { bytes4 x = 0x12345678; return abi.encodeWithSelector(x, "abc"); } - function f3() public pure returns (bytes) { + function f3() public pure returns (bytes memory) { bytes4 x = 0x12345678; return abi.encodeWithSelector(x, uint(-1)); } @@ -12005,22 +13367,22 @@ BOOST_AUTO_TEST_CASE(abi_encode_with_selectorv2) char const* sourceCode = R"( pragma experimental ABIEncoderV2; contract C { - function f0() public pure returns (bytes) { + function f0() public pure returns (bytes memory) { return abi.encodeWithSelector(0x12345678); } - function f1() public pure returns (bytes) { + function f1() public pure returns (bytes memory) { return abi.encodeWithSelector(0x12345678, "abc"); } - function f2() public pure returns (bytes) { + function f2() public pure returns (bytes memory) { bytes4 x = 0x12345678; return abi.encodeWithSelector(x, "abc"); } - function f3() public pure returns (bytes) { + function f3() public pure returns (bytes memory) { bytes4 x = 0x12345678; return abi.encodeWithSelector(x, uint(-1)); } struct S { uint a; string b; uint16 c; } - function f4() public pure returns (bytes) { + function f4() public pure returns (bytes memory) { bytes4 x = 0x12345678; S memory s; s.a = 0x1234567; @@ -12051,19 +13413,19 @@ BOOST_AUTO_TEST_CASE(abi_encode_with_signature) { char const* sourceCode = R"T( contract C { - function f0() public pure returns (bytes) { + function f0() public pure returns (bytes memory) { return abi.encodeWithSignature("f(uint256)"); } - function f1() public pure returns (bytes) { + function f1() public pure returns (bytes memory) { string memory x = "f(uint256)"; return abi.encodeWithSignature(x, "abc"); } string xstor; - function f1s() public returns (bytes) { + function f1s() public returns (bytes memory) { xstor = "f(uint256)"; return abi.encodeWithSignature(xstor, "abc"); } - function f2() public pure returns (bytes r, uint[] ar) { + function f2() public pure returns (bytes memory r, uint[] memory ar) { string memory x = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua."; uint[] memory y = new uint[](4); y[0] = uint(-1); @@ -12095,19 +13457,19 @@ BOOST_AUTO_TEST_CASE(abi_encode_with_signaturev2) char const* sourceCode = R"T( pragma experimental ABIEncoderV2; contract C { - function f0() public pure returns (bytes) { + function f0() public pure returns (bytes memory) { return abi.encodeWithSignature("f(uint256)"); } - function f1() public pure returns (bytes) { + function f1() public pure returns (bytes memory) { string memory x = "f(uint256)"; return abi.encodeWithSignature(x, "abc"); } string xstor; - function f1s() public returns (bytes) { + function f1s() public returns (bytes memory) { xstor = "f(uint256)"; return abi.encodeWithSignature(xstor, "abc"); } - function f2() public pure returns (bytes r, uint[] ar) { + function f2() public pure returns (bytes memory r, uint[] memory ar) { string memory x = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua."; uint[] memory y = new uint[](4); y[0] = uint(-1); @@ -12120,7 +13482,7 @@ BOOST_AUTO_TEST_CASE(abi_encode_with_signaturev2) ar = new uint[](2); } struct S { uint a; string b; uint16 c; } - function f4() public pure returns (bytes) { + function f4() public pure returns (bytes memory) { bytes4 x = 0x12345678; S memory s; s.a = 0x1234567; @@ -12149,12 +13511,86 @@ BOOST_AUTO_TEST_CASE(abi_encode_with_signaturev2) ABI_CHECK(callContractFunction("f4()"), expectation); } +BOOST_AUTO_TEST_CASE(abi_encode_empty_string) +{ + char const* sourceCode = R"( + // Tests that this will not end up using a "bytes0" type + // (which would assert) + contract C { + function f() public pure returns (bytes memory, bytes memory) { + return (abi.encode(""), abi.encodePacked("")); + } + } + )"; + compileAndRun(sourceCode, 0, "C"); + ABI_CHECK(callContractFunction("f()"), encodeArgs( + 0x40, 0xc0, + 0x60, 0x20, 0x00, 0x00, + 0x00 + )); +} + +BOOST_AUTO_TEST_CASE(abi_encode_empty_string_v2) +{ + char const* sourceCode = R"( + // Tests that this will not end up using a "bytes0" type + // (which would assert) + pragma experimental ABIEncoderV2; + contract C { + function f() public pure returns (bytes memory, bytes memory) { + return (abi.encode(""), abi.encodePacked("")); + } + } + )"; + compileAndRun(sourceCode, 0, "C"); + ABI_CHECK(callContractFunction("f()"), encodeArgs( + 0x40, 0xa0, + 0x40, 0x20, 0x00, + 0x00 + )); +} + +BOOST_AUTO_TEST_CASE(abi_encode_rational) +{ + char const* sourceCode = R"( + // Tests that rational numbers (even negative ones) are encoded properly. + contract C { + function f() public pure returns (bytes memory) { + return abi.encode(1, -2); + } + } + )"; + compileAndRun(sourceCode, 0, "C"); + ABI_CHECK(callContractFunction("f()"), encodeArgs( + 0x20, + 0x40, u256(1), u256(-2) + )); +} + +BOOST_AUTO_TEST_CASE(abi_encode_rational_v2) +{ + char const* sourceCode = R"( + // Tests that rational numbers (even negative ones) are encoded properly. + pragma experimental ABIEncoderV2; + contract C { + function f() public pure returns (bytes memory) { + return abi.encode(1, -2); + } + } + )"; + compileAndRun(sourceCode, 0, "C"); + ABI_CHECK(callContractFunction("f()"), encodeArgs( + 0x20, + 0x40, u256(1), u256(-2) + )); +} + BOOST_AUTO_TEST_CASE(abi_encode_call) { char const* sourceCode = R"T( contract C { bool x; - function c(uint a, uint[] b) public { + function c(uint a, uint[] memory b) public { require(a == 5); require(b.length == 2); require(b[0] == 6); @@ -12166,7 +13602,8 @@ BOOST_AUTO_TEST_CASE(abi_encode_call) uint[] memory b = new uint[](2); b[0] = 6; b[1] = 7; - require(this.call(abi.encodeWithSignature("c(uint256,uint256[])", a, b))); + (bool success,) = address(this).call(abi.encodeWithSignature("c(uint256,uint256[])", a, b)); + require(success); return x; } } @@ -12178,7 +13615,6 @@ BOOST_AUTO_TEST_CASE(abi_encode_call) BOOST_AUTO_TEST_CASE(staticcall_for_view_and_pure) { char const* sourceCode = R"( - pragma experimental "v0.5.0"; contract C { uint x; function f() public returns (uint) { @@ -12197,10 +13633,10 @@ BOOST_AUTO_TEST_CASE(staticcall_for_view_and_pure) return (new C()).f(); } function fview() public returns (uint) { - return (CView(new C())).f(); + return (CView(address(new C()))).f(); } function fpure() public returns (uint) { - return (CPure(new C())).f(); + return (CPure(address(new C()))).f(); } } )"; @@ -12220,78 +13656,25 @@ BOOST_AUTO_TEST_CASE(staticcall_for_view_and_pure) } } -BOOST_AUTO_TEST_CASE(swap_peephole_optimisation) -{ - char const* sourceCode = R"( - contract C { - function lt(uint a, uint b) returns (bool c) { - assembly { - a - b - swap1 - lt - =: c - } - } - function add(uint a, uint b) returns (uint c) { - assembly { - a - b - swap1 - add - =: c - } - } - function div(uint a, uint b) returns (uint c) { - assembly { - a - b - swap1 - div - =: c - } - } - } - )"; - compileAndRun(sourceCode); - BOOST_CHECK(callContractFunction("lt(uint256,uint256)", u256(1), u256(2)) == encodeArgs(u256(1))); - BOOST_CHECK(callContractFunction("lt(uint256,uint256)", u256(2), u256(1)) == encodeArgs(u256(0))); - BOOST_CHECK(callContractFunction("add(uint256,uint256)", u256(1), u256(2)) == encodeArgs(u256(3))); - BOOST_CHECK(callContractFunction("add(uint256,uint256)", u256(100), u256(200)) == encodeArgs(u256(300))); - BOOST_CHECK(callContractFunction("div(uint256,uint256)", u256(2), u256(1)) == encodeArgs(u256(2))); - BOOST_CHECK(callContractFunction("div(uint256,uint256)", u256(200), u256(10)) == encodeArgs(u256(20))); - BOOST_CHECK(callContractFunction("div(uint256,uint256)", u256(1), u256(0)) == encodeArgs(u256(0))); - BOOST_CHECK(callContractFunction("div(uint256,uint256)", u256(0), u256(1)) == encodeArgs(u256(0))); -} - BOOST_AUTO_TEST_CASE(bitwise_shifting_constantinople) { if (!dev::test::Options::get().evmVersion().hasBitwiseShifting()) return; char const* sourceCode = R"( contract C { - function shl(uint a, uint b) returns (uint c) { + function shl(uint a, uint b) public returns (uint c) { assembly { - a - b - shl - =: c + c := shl(b, a) } } - function shr(uint a, uint b) returns (uint c) { + function shr(uint a, uint b) public returns (uint c) { assembly { - a - b - shr - =: c + c := shr(b, a) } } - function sar(uint a, uint b) returns (uint c) { + function sar(uint a, uint b) public returns (uint c) { assembly { - a - b - sar - =: c + c := sar(b, a) } } } @@ -12316,68 +13699,50 @@ BOOST_AUTO_TEST_CASE(bitwise_shifting_constants_constantinople) return; char const* sourceCode = R"( contract C { - function shl_1() returns (bool) { + function shl_1() public returns (bool) { uint c; assembly { - 1 - 2 - shl - =: c + c := shl(2, 1) } assert(c == 4); return true; } - function shl_2() returns (bool) { + function shl_2() public returns (bool) { uint c; assembly { - 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff - 1 - shl - =: c + c := shl(1, 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) } assert(c == 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe); return true; } - function shl_3() returns (bool) { + function shl_3() public returns (bool) { uint c; assembly { - 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff - 256 - shl - =: c + c := shl(256, 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) } assert(c == 0); return true; } - function shr_1() returns (bool) { + function shr_1() public returns (bool) { uint c; assembly { - 3 - 1 - shr - =: c + c := shr(1, 3) } assert(c == 1); return true; } - function shr_2() returns (bool) { + function shr_2() public returns (bool) { uint c; assembly { - 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff - 1 - shr - =: c + c := shr(1, 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) } assert(c == 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff); return true; } - function shr_3() returns (bool) { + function shr_3() public returns (bool) { uint c; assembly { - 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff - 256 - shr - =: c + c := shr(256, 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) } assert(c == 0); return true; @@ -12403,7 +13768,7 @@ BOOST_AUTO_TEST_CASE(senders_balance) } contract D { C c = new C(); - constructor() payable { } + constructor() public payable { } function f() public view returns (uint) { return c.f(); } @@ -12413,6 +13778,271 @@ BOOST_AUTO_TEST_CASE(senders_balance) BOOST_CHECK(callContractFunction("f()") == encodeArgs(u256(27))); } +BOOST_AUTO_TEST_CASE(abi_decode_trivial) +{ + char const* sourceCode = R"( + contract C { + function f(bytes memory data) public pure returns (uint) { + return abi.decode(data, (uint)); + } + } + )"; + compileAndRun(sourceCode); + ABI_CHECK(callContractFunction("f(bytes)", 0x20, 0x20, 33), encodeArgs(u256(33))); +} + +BOOST_AUTO_TEST_CASE(abi_encode_decode_simple) +{ + char const* sourceCode = R"XX( + contract C { + function f() public pure returns (uint, bytes memory) { + bytes memory arg = "abcdefg"; + return abi.decode(abi.encode(uint(33), arg), (uint, bytes)); + } + } + )XX"; + compileAndRun(sourceCode); + ABI_CHECK( + callContractFunction("f()"), + encodeArgs(33, 0x40, 7, "abcdefg") + ); +} + +BOOST_AUTO_TEST_CASE(abi_decode_simple) +{ + char const* sourceCode = R"( + contract C { + function f(bytes memory data) public pure returns (uint, bytes memory) { + return abi.decode(data, (uint, bytes)); + } + } + )"; + compileAndRun(sourceCode); + ABI_CHECK( + callContractFunction("f(bytes)", 0x20, 0x20 * 4, 33, 0x40, 7, "abcdefg"), + encodeArgs(33, 0x40, 7, "abcdefg") + ); +} + +BOOST_AUTO_TEST_CASE(abi_decode_v2) +{ + char const* sourceCode = R"( + pragma experimental ABIEncoderV2; + contract C { + struct S { uint a; uint[] b; } + function f() public pure returns (S memory) { + S memory s; + s.a = 8; + s.b = new uint[](3); + s.b[0] = 9; + s.b[1] = 10; + s.b[2] = 11; + return abi.decode(abi.encode(s), (S)); + } + } + )"; + compileAndRun(sourceCode); + ABI_CHECK( + callContractFunction("f()"), + encodeArgs(0x20, 8, 0x40, 3, 9, 10, 11) + ); +} + +BOOST_AUTO_TEST_CASE(abi_decode_simple_storage) +{ + char const* sourceCode = R"( + contract C { + bytes data; + function f(bytes memory _data) public returns (uint, bytes memory) { + data = _data; + return abi.decode(data, (uint, bytes)); + } + } + )"; + compileAndRun(sourceCode); + ABI_CHECK( + callContractFunction("f(bytes)", 0x20, 0x20 * 4, 33, 0x40, 7, "abcdefg"), + encodeArgs(33, 0x40, 7, "abcdefg") + ); +} + +BOOST_AUTO_TEST_CASE(abi_decode_v2_storage) +{ + char const* sourceCode = R"( + pragma experimental ABIEncoderV2; + contract C { + bytes data; + struct S { uint a; uint[] b; } + function f() public returns (S memory) { + S memory s; + s.a = 8; + s.b = new uint[](3); + s.b[0] = 9; + s.b[1] = 10; + s.b[2] = 11; + data = abi.encode(s); + return abi.decode(data, (S)); + } + } + )"; + compileAndRun(sourceCode); + ABI_CHECK( + callContractFunction("f()"), + encodeArgs(0x20, 8, 0x40, 3, 9, 10, 11) + ); +} + +BOOST_AUTO_TEST_CASE(abi_decode_calldata) +{ + char const* sourceCode = R"( + contract C { + function f(bytes calldata data) external pure returns (uint, bytes memory r) { + return abi.decode(data, (uint, bytes)); + } + } + )"; + compileAndRun(sourceCode); + ABI_CHECK( + callContractFunction("f(bytes)", 0x20, 0x20 * 4, 33, 0x40, 7, "abcdefg"), + encodeArgs(33, 0x40, 7, "abcdefg") + ); +} + +BOOST_AUTO_TEST_CASE(abi_decode_v2_calldata) +{ + char const* sourceCode = R"( + pragma experimental ABIEncoderV2; + contract C { + struct S { uint a; uint[] b; } + function f(bytes calldata data) external pure returns (S memory) { + return abi.decode(data, (S)); + } + } + )"; + compileAndRun(sourceCode); + ABI_CHECK( + callContractFunction("f(bytes)", 0x20, 0x20 * 7, 0x20, 33, 0x40, 3, 10, 11, 12), + encodeArgs(0x20, 33, 0x40, 3, 10, 11, 12) + ); +} + +BOOST_AUTO_TEST_CASE(abi_decode_static_array) +{ + char const* sourceCode = R"( + contract C { + function f(bytes calldata data) external pure returns (uint[2][3] memory) { + return abi.decode(data, (uint[2][3])); + } + } + )"; + compileAndRun(sourceCode); + ABI_CHECK( + callContractFunction("f(bytes)", 0x20, 6 * 0x20, 1, 2, 3, 4, 5, 6), + encodeArgs(1, 2, 3, 4, 5, 6) + ); +} + +BOOST_AUTO_TEST_CASE(abi_decode_static_array_v2) +{ + char const* sourceCode = R"( + pragma experimental ABIEncoderV2; + contract C { + function f(bytes calldata data) external pure returns (uint[2][3] memory) { + return abi.decode(data, (uint[2][3])); + } + } + )"; + compileAndRun(sourceCode); + ABI_CHECK( + callContractFunction("f(bytes)", 0x20, 6 * 0x20, 1, 2, 3, 4, 5, 6), + encodeArgs(1, 2, 3, 4, 5, 6) + ); +} + +BOOST_AUTO_TEST_CASE(abi_decode_dynamic_array) +{ + char const* sourceCode = R"( + contract C { + function f(bytes calldata data) external pure returns (uint[] memory) { + return abi.decode(data, (uint[])); + } + } + )"; + compileAndRun(sourceCode); + ABI_CHECK( + callContractFunction("f(bytes)", 0x20, 6 * 0x20, 0x20, 4, 3, 4, 5, 6), + encodeArgs(0x20, 4, 3, 4, 5, 6) + ); +} + +BOOST_AUTO_TEST_CASE(write_storage_external) +{ + char const* sourceCode = R"( + contract C { + uint public x; + function f(uint y) public payable { + x = y; + } + function g(uint y) external { + x = y; + } + function h() public { + this.g(12); + } + } + contract D { + C c = new C(); + function f() public payable returns (uint) { + c.g(3); + return c.x(); + } + function g() public returns (uint) { + c.g(8); + return c.x(); + } + function h() public returns (uint) { + c.h(); + return c.x(); + } + } + )"; + compileAndRun(sourceCode, 0, "D"); + ABI_CHECK(callContractFunction("f()"), encodeArgs(3)); + ABI_CHECK(callContractFunction("g()"), encodeArgs(8)); + ABI_CHECK(callContractFunction("h()"), encodeArgs(12)); +} + +BOOST_AUTO_TEST_CASE(test_underscore_in_hex) +{ + char const* sourceCode = R"( + contract test { + function f(bool cond) public pure returns (uint) { + uint32 x = 0x1234_ab; + uint y = 0x1234_abcd_1234; + return cond ? x : y; + } + } + )"; + compileAndRun(sourceCode); + ABI_CHECK(callContractFunction("f(bool)", true), encodeArgs(u256(0x1234ab))); + ABI_CHECK(callContractFunction("f(bool)", false), encodeArgs(u256(0x1234abcd1234))); +} + +BOOST_AUTO_TEST_CASE(flipping_sign_tests) +{ + char const* sourceCode = R"( + contract test { + function f() public returns (bool){ + int x = -2**255; + assert(-x == x); + return true; + } + } + )"; + compileAndRun(sourceCode); + ABI_CHECK(callContractFunction("f()"), encodeArgs(true)); +} + BOOST_AUTO_TEST_SUITE_END() } diff --git a/test/libsolidity/SolidityExpressionCompiler.cpp b/test/libsolidity/SolidityExpressionCompiler.cpp index 90d8265c..309cbf0b 100644 --- a/test/libsolidity/SolidityExpressionCompiler.cpp +++ b/test/libsolidity/SolidityExpressionCompiler.cpp @@ -77,7 +77,7 @@ Declaration const& resolveDeclaration( ) { ASTNode const* scope = &_sourceUnit; - // bracers are required, cause msvc couldnt handle this macro in for statement + // bracers are required, cause msvc couldn't handle this macro in for statement for (string const& namePart: _namespacedName) { auto declarations = _resolver.resolveName(namePart, scope); @@ -175,12 +175,12 @@ BOOST_AUTO_TEST_CASE(literal_true) { char const* sourceCode = R"( contract test { - function f() { var x = true; } + function f() public { bool x = true; } } )"; bytes code = compileFirstExpression(sourceCode); - bytes expectation({byte(Instruction::PUSH1), 0x1}); + bytes expectation({uint8_t(Instruction::PUSH1), 0x1}); BOOST_CHECK_EQUAL_COLLECTIONS(code.begin(), code.end(), expectation.begin(), expectation.end()); } @@ -188,12 +188,12 @@ BOOST_AUTO_TEST_CASE(literal_false) { char const* sourceCode = R"( contract test { - function f() { var x = false; } + function f() { bool x = false; } } )"; bytes code = compileFirstExpression(sourceCode); - bytes expectation({byte(Instruction::PUSH1), 0x0}); + bytes expectation({uint8_t(Instruction::PUSH1), 0x0}); BOOST_CHECK_EQUAL_COLLECTIONS(code.begin(), code.end(), expectation.begin(), expectation.end()); } @@ -201,12 +201,12 @@ BOOST_AUTO_TEST_CASE(int_literal) { char const* sourceCode = R"( contract test { - function f() { var x = 0x12345678901234567890; } + function f() { uint x = 0x12345678901234567890; } } )"; bytes code = compileFirstExpression(sourceCode); - bytes expectation({byte(Instruction::PUSH10), 0x12, 0x34, 0x56, 0x78, 0x90, + bytes expectation({uint8_t(Instruction::PUSH10), 0x12, 0x34, 0x56, 0x78, 0x90, 0x12, 0x34, 0x56, 0x78, 0x90}); BOOST_CHECK_EQUAL_COLLECTIONS(code.begin(), code.end(), expectation.begin(), expectation.end()); } @@ -215,14 +215,14 @@ BOOST_AUTO_TEST_CASE(int_with_wei_ether_subdenomination) { char const* sourceCode = R"( contract test { - function test () { - var x = 1 wei; + constructor() { + uint x = 1 wei; } } )"; bytes code = compileFirstExpression(sourceCode); - bytes expectation({byte(Instruction::PUSH1), 0x1}); + bytes expectation({uint8_t(Instruction::PUSH1), 0x1}); BOOST_CHECK_EQUAL_COLLECTIONS(code.begin(), code.end(), expectation.begin(), expectation.end()); } @@ -231,13 +231,13 @@ BOOST_AUTO_TEST_CASE(int_with_szabo_ether_subdenomination) char const* sourceCode = R"( contract test { function test () { - var x = 1 szabo; + uint x = 1 szabo; } } )"; bytes code = compileFirstExpression(sourceCode); - bytes expectation({byte(Instruction::PUSH5), 0xe8, 0xd4, 0xa5, 0x10, 0x00}); + bytes expectation({uint8_t(Instruction::PUSH5), 0xe8, 0xd4, 0xa5, 0x10, 0x00}); BOOST_CHECK_EQUAL_COLLECTIONS(code.begin(), code.end(), expectation.begin(), expectation.end()); } @@ -245,14 +245,15 @@ BOOST_AUTO_TEST_CASE(int_with_finney_ether_subdenomination) { char const* sourceCode = R"( contract test { - function test () + constructor() { - var x = 1 finney; + uint x = 1 finney; } - })"; + } + )"; bytes code = compileFirstExpression(sourceCode); - bytes expectation({byte(Instruction::PUSH7), 0x3, 0x8d, 0x7e, 0xa4, 0xc6, 0x80, 0x00}); + bytes expectation({uint8_t(Instruction::PUSH7), 0x3, 0x8d, 0x7e, 0xa4, 0xc6, 0x80, 0x00}); BOOST_CHECK_EQUAL_COLLECTIONS(code.begin(), code.end(), expectation.begin(), expectation.end()); } @@ -260,14 +261,14 @@ BOOST_AUTO_TEST_CASE(int_with_ether_ether_subdenomination) { char const* sourceCode = R"( contract test { - function test () { - var x = 1 ether; + constructor() { + uint x = 1 ether; } } )"; bytes code = compileFirstExpression(sourceCode); - bytes expectation({byte(Instruction::PUSH8), 0xd, 0xe0, 0xb6, 0xb3, 0xa7, 0x64, 0x00, 0x00}); + bytes expectation({uint8_t(Instruction::PUSH8), 0xd, 0xe0, 0xb6, 0xb3, 0xa7, 0x64, 0x00, 0x00}); BOOST_CHECK_EQUAL_COLLECTIONS(code.begin(), code.end(), expectation.begin(), expectation.end()); } @@ -275,17 +276,17 @@ BOOST_AUTO_TEST_CASE(comparison) { char const* sourceCode = R"( contract test { - function f() { var x = (0x10aa < 0x11aa) != true; } + function f() { bool x = (0x10aa < 0x11aa) != true; } } )"; bytes code = compileFirstExpression(sourceCode); - bytes expectation({byte(Instruction::PUSH1), 0x1, byte(Instruction::ISZERO), byte(Instruction::ISZERO), - byte(Instruction::PUSH2), 0x11, 0xaa, - byte(Instruction::PUSH2), 0x10, 0xaa, - byte(Instruction::LT), byte(Instruction::ISZERO), byte(Instruction::ISZERO), - byte(Instruction::EQ), - byte(Instruction::ISZERO)}); + bytes expectation({uint8_t(Instruction::PUSH1), 0x1, uint8_t(Instruction::ISZERO), uint8_t(Instruction::ISZERO), + uint8_t(Instruction::PUSH2), 0x11, 0xaa, + uint8_t(Instruction::PUSH2), 0x10, 0xaa, + uint8_t(Instruction::LT), uint8_t(Instruction::ISZERO), uint8_t(Instruction::ISZERO), + uint8_t(Instruction::EQ), + uint8_t(Instruction::ISZERO)}); BOOST_CHECK_EQUAL_COLLECTIONS(code.begin(), code.end(), expectation.begin(), expectation.end()); } @@ -293,32 +294,32 @@ BOOST_AUTO_TEST_CASE(short_circuiting) { char const* sourceCode = R"( contract test { - function f() { var x = true != (4 <= 8 + 10 || 9 != 2); } + function f() { bool x = true != (4 <= 8 + 10 || 9 != 2); } } )"; bytes code = compileFirstExpression(sourceCode); - bytes expectation({byte(Instruction::PUSH1), 0x12, // 8 + 10 - byte(Instruction::PUSH1), 0x4, - byte(Instruction::GT), - byte(Instruction::ISZERO), // after this we have 4 <= 8 + 10 - byte(Instruction::DUP1), - byte(Instruction::PUSH1), 0x11, - byte(Instruction::JUMPI), // short-circuit if it is true - byte(Instruction::POP), - byte(Instruction::PUSH1), 0x2, - byte(Instruction::PUSH1), 0x9, - byte(Instruction::EQ), - byte(Instruction::ISZERO), // after this we have 9 != 2 - byte(Instruction::JUMPDEST), - byte(Instruction::ISZERO), byte(Instruction::ISZERO), - byte(Instruction::PUSH1), 0x1, byte(Instruction::ISZERO), byte(Instruction::ISZERO), - byte(Instruction::EQ), - byte(Instruction::ISZERO)}); + bytes expectation({uint8_t(Instruction::PUSH1), 0x12, // 8 + 10 + uint8_t(Instruction::PUSH1), 0x4, + uint8_t(Instruction::GT), + uint8_t(Instruction::ISZERO), // after this we have 4 <= 8 + 10 + uint8_t(Instruction::DUP1), + uint8_t(Instruction::PUSH1), 0x11, + uint8_t(Instruction::JUMPI), // short-circuit if it is true + uint8_t(Instruction::POP), + uint8_t(Instruction::PUSH1), 0x2, + uint8_t(Instruction::PUSH1), 0x9, + uint8_t(Instruction::EQ), + uint8_t(Instruction::ISZERO), // after this we have 9 != 2 + uint8_t(Instruction::JUMPDEST), + uint8_t(Instruction::ISZERO), uint8_t(Instruction::ISZERO), + uint8_t(Instruction::PUSH1), 0x1, uint8_t(Instruction::ISZERO), uint8_t(Instruction::ISZERO), + uint8_t(Instruction::EQ), + uint8_t(Instruction::ISZERO)}); BOOST_CHECK_EQUAL_COLLECTIONS(code.begin(), code.end(), expectation.begin(), expectation.end()); } -BOOST_AUTO_TEST_CASE(arithmetics) +BOOST_AUTO_TEST_CASE(arithmetic) { char const* sourceCode = R"( contract test { @@ -326,37 +327,37 @@ BOOST_AUTO_TEST_CASE(arithmetics) } )"; bytes code = compileFirstExpression(sourceCode, {}, {{"test", "f", "y"}}); - bytes expectation({byte(Instruction::PUSH1), 0x1, - byte(Instruction::PUSH1), 0x2, - byte(Instruction::PUSH1), 0x3, - byte(Instruction::PUSH1), 0x4, - byte(Instruction::PUSH1), 0x5, - byte(Instruction::PUSH1), 0x6, - byte(Instruction::PUSH1), 0x7, - byte(Instruction::PUSH1), 0x8, - byte(Instruction::DUP9), - byte(Instruction::XOR), - byte(Instruction::AND), - byte(Instruction::OR), - byte(Instruction::SUB), - byte(Instruction::ADD), - byte(Instruction::DUP2), - byte(Instruction::ISZERO), - byte(Instruction::ISZERO), - byte(Instruction::PUSH1), 0x1d, - byte(Instruction::JUMPI), - byte(Instruction::INVALID), - byte(Instruction::JUMPDEST), - byte(Instruction::MOD), - byte(Instruction::DUP2), - byte(Instruction::ISZERO), - byte(Instruction::ISZERO), - byte(Instruction::PUSH1), 0x26, - byte(Instruction::JUMPI), - byte(Instruction::INVALID), - byte(Instruction::JUMPDEST), - byte(Instruction::DIV), - byte(Instruction::MUL)}); + bytes expectation({uint8_t(Instruction::PUSH1), 0x1, + uint8_t(Instruction::PUSH1), 0x2, + uint8_t(Instruction::PUSH1), 0x3, + uint8_t(Instruction::PUSH1), 0x4, + uint8_t(Instruction::PUSH1), 0x5, + uint8_t(Instruction::PUSH1), 0x6, + uint8_t(Instruction::PUSH1), 0x7, + uint8_t(Instruction::PUSH1), 0x8, + uint8_t(Instruction::DUP9), + uint8_t(Instruction::XOR), + uint8_t(Instruction::AND), + uint8_t(Instruction::OR), + uint8_t(Instruction::SUB), + uint8_t(Instruction::ADD), + uint8_t(Instruction::DUP2), + uint8_t(Instruction::ISZERO), + uint8_t(Instruction::ISZERO), + uint8_t(Instruction::PUSH1), 0x1d, + uint8_t(Instruction::JUMPI), + uint8_t(Instruction::INVALID), + uint8_t(Instruction::JUMPDEST), + uint8_t(Instruction::MOD), + uint8_t(Instruction::DUP2), + uint8_t(Instruction::ISZERO), + uint8_t(Instruction::ISZERO), + uint8_t(Instruction::PUSH1), 0x26, + uint8_t(Instruction::JUMPI), + uint8_t(Instruction::INVALID), + uint8_t(Instruction::JUMPDEST), + uint8_t(Instruction::DIV), + uint8_t(Instruction::MUL)}); BOOST_CHECK_EQUAL_COLLECTIONS(code.begin(), code.end(), expectation.begin(), expectation.end()); } @@ -369,13 +370,13 @@ BOOST_AUTO_TEST_CASE(unary_operators) )"; bytes code = compileFirstExpression(sourceCode, {}, {{"test", "f", "y"}}); - bytes expectation({byte(Instruction::PUSH1), 0x2, - byte(Instruction::DUP2), - byte(Instruction::PUSH1), 0x0, - byte(Instruction::SUB), - byte(Instruction::NOT), - byte(Instruction::EQ), - byte(Instruction::ISZERO)}); + bytes expectation({uint8_t(Instruction::PUSH1), 0x2, + uint8_t(Instruction::DUP2), + uint8_t(Instruction::PUSH1), 0x0, + uint8_t(Instruction::SUB), + uint8_t(Instruction::NOT), + uint8_t(Instruction::EQ), + uint8_t(Instruction::ISZERO)}); BOOST_CHECK_EQUAL_COLLECTIONS(code.begin(), code.end(), expectation.begin(), expectation.end()); } @@ -383,53 +384,53 @@ BOOST_AUTO_TEST_CASE(unary_inc_dec) { char const* sourceCode = R"( contract test { - function f(uint a) returns (uint x) { x = --a ^ (a-- ^ (++a ^ a++)); } + function f(uint a) public returns (uint x) { x = --a ^ (a-- ^ (++a ^ a++)); } } )"; bytes code = compileFirstExpression(sourceCode, {}, {{"test", "f", "a"}, {"test", "f", "x"}}); // Stack: a, x - bytes expectation({byte(Instruction::DUP2), - byte(Instruction::DUP1), - byte(Instruction::PUSH1), 0x1, - byte(Instruction::ADD), + bytes expectation({uint8_t(Instruction::DUP2), + uint8_t(Instruction::DUP1), + uint8_t(Instruction::PUSH1), 0x1, + uint8_t(Instruction::ADD), // Stack here: a x a (a+1) - byte(Instruction::SWAP3), - byte(Instruction::POP), // first ++ + uint8_t(Instruction::SWAP3), + uint8_t(Instruction::POP), // first ++ // Stack here: (a+1) x a - byte(Instruction::DUP3), - byte(Instruction::PUSH1), 0x1, - byte(Instruction::ADD), + uint8_t(Instruction::DUP3), + uint8_t(Instruction::PUSH1), 0x1, + uint8_t(Instruction::ADD), // Stack here: (a+1) x a (a+2) - byte(Instruction::SWAP3), - byte(Instruction::POP), + uint8_t(Instruction::SWAP3), + uint8_t(Instruction::POP), // Stack here: (a+2) x a - byte(Instruction::DUP3), // second ++ - byte(Instruction::XOR), + uint8_t(Instruction::DUP3), // second ++ + uint8_t(Instruction::XOR), // Stack here: (a+2) x a^(a+2) - byte(Instruction::DUP3), - byte(Instruction::DUP1), - byte(Instruction::PUSH1), 0x1, - byte(Instruction::SWAP1), - byte(Instruction::SUB), + uint8_t(Instruction::DUP3), + uint8_t(Instruction::DUP1), + uint8_t(Instruction::PUSH1), 0x1, + uint8_t(Instruction::SWAP1), + uint8_t(Instruction::SUB), // Stack here: (a+2) x a^(a+2) (a+2) (a+1) - byte(Instruction::SWAP4), - byte(Instruction::POP), // first -- - byte(Instruction::XOR), + uint8_t(Instruction::SWAP4), + uint8_t(Instruction::POP), // first -- + uint8_t(Instruction::XOR), // Stack here: (a+1) x a^(a+2)^(a+2) - byte(Instruction::DUP3), - byte(Instruction::PUSH1), 0x1, - byte(Instruction::SWAP1), - byte(Instruction::SUB), + uint8_t(Instruction::DUP3), + uint8_t(Instruction::PUSH1), 0x1, + uint8_t(Instruction::SWAP1), + uint8_t(Instruction::SUB), // Stack here: (a+1) x a^(a+2)^(a+2) a - byte(Instruction::SWAP3), - byte(Instruction::POP), // second ++ + uint8_t(Instruction::SWAP3), + uint8_t(Instruction::POP), // second ++ // Stack here: a x a^(a+2)^(a+2) - byte(Instruction::DUP3), // will change - byte(Instruction::XOR), - byte(Instruction::SWAP1), - byte(Instruction::POP), - byte(Instruction::DUP1)}); + uint8_t(Instruction::DUP3), // will change + uint8_t(Instruction::XOR), + uint8_t(Instruction::SWAP1), + uint8_t(Instruction::POP), + uint8_t(Instruction::DUP1)}); // Stack here: a x a^(a+2)^(a+2)^a BOOST_CHECK_EQUAL_COLLECTIONS(code.begin(), code.end(), expectation.begin(), expectation.end()); } @@ -444,16 +445,16 @@ BOOST_AUTO_TEST_CASE(assignment) bytes code = compileFirstExpression(sourceCode, {}, {{"test", "f", "a"}, {"test", "f", "b"}}); // Stack: a, b - bytes expectation({byte(Instruction::PUSH1), 0x2, - byte(Instruction::DUP2), - byte(Instruction::DUP4), - byte(Instruction::ADD), + bytes expectation({uint8_t(Instruction::PUSH1), 0x2, + uint8_t(Instruction::DUP2), + uint8_t(Instruction::DUP4), + uint8_t(Instruction::ADD), // Stack here: a b 2 a+b - byte(Instruction::SWAP3), - byte(Instruction::POP), - byte(Instruction::DUP3), + uint8_t(Instruction::SWAP3), + uint8_t(Instruction::POP), + uint8_t(Instruction::DUP3), // Stack here: a+b b 2 a+b - byte(Instruction::MUL)}); + uint8_t(Instruction::MUL)}); BOOST_CHECK_EQUAL_COLLECTIONS(code.begin(), code.end(), expectation.begin(), expectation.end()); } @@ -466,7 +467,7 @@ BOOST_AUTO_TEST_CASE(negative_literals_8bits) )"; bytes code = compileFirstExpression(sourceCode); - bytes expectation(bytes({byte(Instruction::PUSH32)}) + bytes(31, 0xff) + bytes(1, 0x80)); + bytes expectation(bytes({uint8_t(Instruction::PUSH32)}) + bytes(31, 0xff) + bytes(1, 0x80)); BOOST_CHECK_EQUAL_COLLECTIONS(code.begin(), code.end(), expectation.begin(), expectation.end()); } @@ -479,7 +480,7 @@ BOOST_AUTO_TEST_CASE(negative_literals_16bits) )"; bytes code = compileFirstExpression(sourceCode); - bytes expectation(bytes({byte(Instruction::PUSH32)}) + bytes(30, 0xff) + bytes{0xf5, 0x43}); + bytes expectation(bytes({uint8_t(Instruction::PUSH32)}) + bytes(30, 0xff) + bytes{0xf5, 0x43}); BOOST_CHECK_EQUAL_COLLECTIONS(code.begin(), code.end(), expectation.begin(), expectation.end()); } @@ -489,12 +490,12 @@ BOOST_AUTO_TEST_CASE(intermediately_overflowing_literals) // have been applied char const* sourceCode = R"( contract test { - function f() { var x = (0xffffffffffffffffffffffffffffffffffffffff * 0xffffffffffffffffffffffffff01) & 0xbf; } + function f() { uint8 x = (0x00ffffffffffffffffffffffffffffffffffffffff * 0xffffffffffffffffffffffffff01) & 0xbf; } } )"; bytes code = compileFirstExpression(sourceCode); - bytes expectation(bytes({byte(Instruction::PUSH1), 0xbf})); + bytes expectation(bytes({uint8_t(Instruction::PUSH1), 0xbf})); BOOST_CHECK_EQUAL_COLLECTIONS(code.begin(), code.end(), expectation.begin(), expectation.end()); } @@ -508,13 +509,13 @@ BOOST_AUTO_TEST_CASE(blockhash) } )"; - auto blockhashFun = make_shared<FunctionType>(strings{"uint256"}, strings{"bytes32"}, + auto blockhashFun = make_shared<FunctionType>(strings{"uint256"}, strings{"bytes32"}, FunctionType::Kind::BlockHash, false, StateMutability::View); - + bytes code = compileFirstExpression(sourceCode, {}, {}, {make_shared<MagicVariableDeclaration>("blockhash", blockhashFun)}); - bytes expectation({byte(Instruction::PUSH1), 0x03, - byte(Instruction::BLOCKHASH)}); + bytes expectation({uint8_t(Instruction::PUSH1), 0x03, + uint8_t(Instruction::BLOCKHASH)}); BOOST_CHECK_EQUAL_COLLECTIONS(code.begin(), code.end(), expectation.begin(), expectation.end()); } @@ -522,32 +523,17 @@ BOOST_AUTO_TEST_CASE(gas_left) { char const* sourceCode = R"( contract test { - function f() returns (uint256 val) { - return msg.gas; - } - } - )"; - bytes code = compileFirstExpression( - sourceCode, {}, {}, - {make_shared<MagicVariableDeclaration>("msg", make_shared<MagicType>(MagicType::Kind::Message))} - ); - - bytes expectation({byte(Instruction::GAS)}); - BOOST_CHECK_EQUAL_COLLECTIONS(code.begin(), code.end(), expectation.begin(), expectation.end()); - - sourceCode = R"( - contract test { - function f() returns (uint256 val) { + function f() public returns (uint256 val) { return gasleft(); } } )"; - code = compileFirstExpression( + bytes code = compileFirstExpression( sourceCode, {}, {}, {make_shared<MagicVariableDeclaration>("gasleft", make_shared<FunctionType>(strings(), strings{"uint256"}, FunctionType::Kind::GasLeft))} ); - expectation = bytes({byte(Instruction::GAS)}); + bytes expectation = bytes({uint8_t(Instruction::GAS)}); BOOST_CHECK_EQUAL_COLLECTIONS(code.begin(), code.end(), expectation.begin(), expectation.end()); } diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index 5af67659..5ec010c7 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -26,7 +26,7 @@ #include <libsolidity/ast/AST.h> -#include <libdevcore/SHA3.h> +#include <libdevcore/Keccak256.h> #include <boost/test/unit_test.hpp> @@ -43,280 +43,6 @@ namespace test BOOST_FIXTURE_TEST_SUITE(SolidityNameAndTypeResolution, AnalysisFramework) -BOOST_AUTO_TEST_CASE(name_references) -{ - char const* text = R"( - contract test { - uint256 variable; - function f(uint256) public returns (uint out) { f(variable); test; out; } - } - )"; - CHECK_SUCCESS(text); -} - -BOOST_AUTO_TEST_CASE(undeclared_name) -{ - char const* text = R"( - contract test { - uint256 variable; - function f(uint256 arg) public { - f(notfound); - } - } - )"; - CHECK_ERROR(text, DeclarationError, "Undeclared identifier."); -} - -BOOST_AUTO_TEST_CASE(undeclared_name_is_not_fatal) -{ - char const* text = R"( - contract test { - uint256 variable; - function f(uint256 arg) public { - f(notfound); - f(notfound); - } - } - )"; - CHECK_ERROR_ALLOW_MULTI(text, DeclarationError, (vector<string>{"Undeclared identifier", "Undeclared identifier"})); -} - -BOOST_AUTO_TEST_CASE(reference_to_later_declaration) -{ - char const* text = R"( - contract test { - function g() public { f(); } - function f() public {} - } - )"; - CHECK_SUCCESS(text); -} - -BOOST_AUTO_TEST_CASE(type_inference_smoke_test) -{ - char const* text = R"( - contract test { - function f(uint256 arg1, uint32 arg2) public returns (bool ret) { - var x = arg1 + arg2 == 8; ret = x; - } - } - )"; - CHECK_SUCCESS(text); -} - -BOOST_AUTO_TEST_CASE(type_checking_return) -{ - char const* text = R"( - contract test { - function f() public returns (bool r) { return 1 >= 2; } - } - )"; - CHECK_SUCCESS(text); -} - -BOOST_AUTO_TEST_CASE(type_checking_return_wrong_number) -{ - char const* text = R"( - contract test { - function f() public returns (bool r1, bool r2) { return 1 >= 2; } - } - )"; - CHECK_ERROR(text, TypeError, "Different number of arguments in return statement than in returns declaration."); -} - -BOOST_AUTO_TEST_CASE(type_checking_return_wrong_type) -{ - char const* text = R"( - contract test { - function f() public returns (uint256 r) { return 1 >= 2; } - } - )"; - CHECK_ERROR(text, TypeError, "Return argument type bool is not implicitly convertible to expected type (type of first return variable) uint256."); -} - -BOOST_AUTO_TEST_CASE(type_checking_function_call) -{ - char const* text = R"( - contract test { - function f() public returns (bool) { return g(12, true) == 3; } - function g(uint256, bool) public returns (uint256) { } - } - )"; - CHECK_SUCCESS(text); -} - -BOOST_AUTO_TEST_CASE(type_conversion_for_comparison) -{ - char const* text = R"( - contract test { - function f() public { uint32(2) == int64(2); } - } - )"; - CHECK_SUCCESS(text); -} - -BOOST_AUTO_TEST_CASE(type_conversion_for_comparison_invalid) -{ - char const* text = R"( - contract test { - function f() public { int32(2) == uint64(2); } - } - )"; - CHECK_ERROR(text, TypeError, "Operator == not compatible with types int32 and uint64"); -} - -BOOST_AUTO_TEST_CASE(type_inference_explicit_conversion) -{ - char const* text = R"( - contract test { - function f() public returns (int256 r) { var x = int256(uint32(2)); return x; } - } - )"; - CHECK_SUCCESS(text); -} - -BOOST_AUTO_TEST_CASE(large_string_literal) -{ - char const* text = R"( - contract test { - function f() public { var x = "123456789012345678901234567890123"; } - } - )"; - CHECK_SUCCESS(text); -} - -BOOST_AUTO_TEST_CASE(balance) -{ - char const* text = R"( - contract test { - function fun() public { - uint256 x = address(0).balance; - } - } - )"; - CHECK_SUCCESS(text); -} - -BOOST_AUTO_TEST_CASE(balance_invalid) -{ - char const* text = R"( - contract test { - function fun() public { - address(0).balance = 7; - } - } - )"; - CHECK_ERROR(text, TypeError, "Expression has to be an lvalue."); -} - -BOOST_AUTO_TEST_CASE(assignment_to_mapping) -{ - char const* text = R"( - contract test { - struct str { - mapping(uint=>uint) map; - } - str data; - function fun() public { - var a = data.map; - data.map = a; - } - } - )"; - CHECK_ERROR(text, TypeError, "Mappings cannot be assigned to."); -} - -BOOST_AUTO_TEST_CASE(assignment_to_struct) -{ - char const* text = R"( - contract test { - struct str { - mapping(uint=>uint) map; - } - str data; - function fun() public { - var a = data; - data = a; - } - } - )"; - CHECK_SUCCESS(text); -} - -BOOST_AUTO_TEST_CASE(forward_function_reference) -{ - char const* text = R"( - contract First { - function fun() public returns (bool) { - return Second(1).fun(1, true, 3) > 0; - } - } - contract Second { - function fun(uint, bool, uint) public returns (uint) { - if (First(2).fun() == true) return 1; - } - } - )"; - CHECK_SUCCESS(text); -} - -BOOST_AUTO_TEST_CASE(comparison_bitop_precedence) -{ - char const* text = R"( - contract First { - function fun() public returns (bool ret) { - return 1 & 2 == 8 & 9 && 1 ^ 2 < 4 | 6; - } - } - )"; - CHECK_SUCCESS(text); -} - -BOOST_AUTO_TEST_CASE(comparison_of_function_types) -{ - char const* text = R"( - contract C { - function f() public returns (bool ret) { - return this.f < this.f; - } - } - )"; - CHECK_ERROR(text, TypeError, "Operator < not compatible"); - text = R"( - contract C { - function f() public returns (bool ret) { - return f < f; - } - } - )"; - CHECK_ERROR(text, TypeError, "Operator < not compatible"); - text = R"( - contract C { - function f() public returns (bool ret) { - return f == f; - } - function g() public returns (bool ret) { - return f != f; - } - } - )"; - CHECK_SUCCESS(text); -} - -BOOST_AUTO_TEST_CASE(comparison_of_mapping_types) -{ - char const* text = R"( - contract C { - mapping(uint => uint) x; - function f() public returns (bool ret) { - var y = x; - return x == y; - } - } - )"; - CHECK_ERROR(text, TypeError, "Operator == not compatible"); -} - BOOST_AUTO_TEST_CASE(function_no_implementation) { SourceUnit const* sourceUnit = nullptr; @@ -337,7 +63,7 @@ BOOST_AUTO_TEST_CASE(abstract_contract) { SourceUnit const* sourceUnit = nullptr; char const* text = R"( - contract base { function foo(); } + contract base { function foo() public; } contract derived is base { function foo() public {} } )"; sourceUnit = parseAndAnalyse(text); @@ -356,7 +82,7 @@ BOOST_AUTO_TEST_CASE(abstract_contract_with_overload) { SourceUnit const* sourceUnit = nullptr; char const* text = R"( - contract base { function foo(bool); } + contract base { function foo(bool) public; } contract derived is base { function foo(uint) public {} } )"; sourceUnit = parseAndAnalyse(text); @@ -369,34 +95,12 @@ BOOST_AUTO_TEST_CASE(abstract_contract_with_overload) BOOST_CHECK(!derived->annotation().unimplementedFunctions.empty()); } -BOOST_AUTO_TEST_CASE(create_abstract_contract) -{ - char const* text = R"( - contract base { function foo(); } - contract derived { - base b; - function foo() public { b = new base(); } - } - )"; - CHECK_ERROR(text, TypeError, "Trying to create an instance of an abstract contract."); -} - -BOOST_AUTO_TEST_CASE(redeclare_implemented_abstract_function_as_abstract) -{ - char const* text = R"( - contract base { function foo(); } - contract derived is base { function foo() public {} } - contract wrong is derived { function foo(); } - )"; - CHECK_ERROR(text, TypeError, "Redeclaring an already implemented function as abstract"); -} - BOOST_AUTO_TEST_CASE(implement_abstract_via_constructor) { SourceUnit const* sourceUnit = nullptr; char const* text = R"( - contract base { function foo(); } - contract foo is base { function foo() public {} } + contract base { function foo() public; } + contract foo is base { constructor() public {} } )"; sourceUnit = parseAndAnalyse(text); std::vector<ASTPointer<ASTNode>> nodes = sourceUnit->nodes(); @@ -454,7 +158,7 @@ BOOST_AUTO_TEST_CASE(function_external_types) uint a; } contract Test { - function boo(uint, bool, bytes8, bool[2], uint[], C, address[]) external returns (uint ret) { + function boo(uint, bool, bytes8, bool[2] calldata, uint[] calldata, C, address[] calldata) external returns (uint ret) { ret = 5; } } @@ -472,9 +176,9 @@ BOOST_AUTO_TEST_CASE(function_external_types) BOOST_AUTO_TEST_CASE(enum_external_type) { - // bug #1801 SourceUnit const* sourceUnit = nullptr; char const* text = R"( + // test for bug #1801 contract Test { enum ActionChoices { GoLeft, GoRight, GoStraight, Sit } function boo(ActionChoices enumArg) external returns (uint ret) { @@ -499,13 +203,13 @@ BOOST_AUTO_TEST_CASE(external_structs) pragma experimental ABIEncoderV2; contract Test { enum ActionChoices { GoLeft, GoRight, GoStraight, Sit } - struct Empty {} + struct Simple { uint i; } struct Nested { X[2][] a; uint y; } - struct X { bytes32 x; Test t; Empty[] e; } - function f(ActionChoices, uint, Empty) external {} - function g(Test, Nested) external {} - function h(function(Nested) external returns (uint)[]) external {} - function i(Nested[]) external {} + struct X { bytes32 x; Test t; Simple[] s; } + function f(ActionChoices, uint, Simple calldata) external {} + function g(Test, Nested calldata) external {} + function h(function(Nested memory) external returns (uint)[] calldata) external {} + function i(Nested[] calldata) external {} } )"; SourceUnit const* sourceUnit = parseAndAnalyse(text); @@ -514,10 +218,10 @@ BOOST_AUTO_TEST_CASE(external_structs) { auto functions = contract->definedFunctions(); BOOST_REQUIRE(!functions.empty()); - BOOST_CHECK_EQUAL("f(uint8,uint256,())", functions[0]->externalSignature()); - BOOST_CHECK_EQUAL("g(address,((bytes32,address,()[])[2][],uint256))", functions[1]->externalSignature()); + BOOST_CHECK_EQUAL("f(uint8,uint256,(uint256))", functions[0]->externalSignature()); + BOOST_CHECK_EQUAL("g(address,((bytes32,address,(uint256)[])[2][],uint256))", functions[1]->externalSignature()); BOOST_CHECK_EQUAL("h(function[])", functions[2]->externalSignature()); - BOOST_CHECK_EQUAL("i(((bytes32,address,()[])[2][],uint256)[])", functions[3]->externalSignature()); + BOOST_CHECK_EQUAL("i(((bytes32,address,(uint256)[])[2][],uint256)[])", functions[3]->externalSignature()); } } @@ -527,13 +231,13 @@ BOOST_AUTO_TEST_CASE(external_structs_in_libraries) pragma experimental ABIEncoderV2; library Test { enum ActionChoices { GoLeft, GoRight, GoStraight, Sit } - struct Empty {} + struct Simple { uint i; } struct Nested { X[2][] a; uint y; } - struct X { bytes32 x; Test t; Empty[] e; } - function f(ActionChoices, uint, Empty) external {} - function g(Test, Nested) external {} - function h(function(Nested) external returns (uint)[]) external {} - function i(Nested[]) external {} + struct X { bytes32 x; Test t; Simple[] s; } + function f(ActionChoices, uint, Simple calldata) external {} + function g(Test, Nested calldata) external {} + function h(function(Nested memory) external returns (uint)[] calldata) external {} + function i(Nested[] calldata) external {} } )"; SourceUnit const* sourceUnit = parseAndAnalyse(text); @@ -542,7 +246,7 @@ BOOST_AUTO_TEST_CASE(external_structs_in_libraries) { auto functions = contract->definedFunctions(); BOOST_REQUIRE(!functions.empty()); - BOOST_CHECK_EQUAL("f(Test.ActionChoices,uint256,Test.Empty)", functions[0]->externalSignature()); + BOOST_CHECK_EQUAL("f(Test.ActionChoices,uint256,Test.Simple)", functions[0]->externalSignature()); BOOST_CHECK_EQUAL("g(Test,Test.Nested)", functions[1]->externalSignature()); BOOST_CHECK_EQUAL("h(function[])", functions[2]->externalSignature()); BOOST_CHECK_EQUAL("i(Test.Nested[])", functions[3]->externalSignature()); @@ -568,336 +272,6 @@ BOOST_AUTO_TEST_CASE(struct_with_mapping_in_library) } } -BOOST_AUTO_TEST_CASE(functions_with_identical_structs_in_interface) -{ - char const* text = R"( - pragma experimental ABIEncoderV2; - - contract C { - struct S1 { } - struct S2 { } - function f(S1) pure {} - function f(S2) pure {} - } - )"; - CHECK_ERROR(text, TypeError, "Function overload clash during conversion to external types for arguments"); -} - -BOOST_AUTO_TEST_CASE(functions_with_different_structs_in_interface) -{ - char const* text = R"( - pragma experimental ABIEncoderV2; - - contract C { - struct S1 { function() external a; } - struct S2 { bytes24 a; } - function f(S1) pure {} - function f(S2) pure {} - } - )"; - CHECK_SUCCESS(text); -} - -BOOST_AUTO_TEST_CASE(functions_with_stucts_of_non_external_types_in_interface) -{ - char const* text = R"( - pragma experimental ABIEncoderV2; - - contract C { - struct S { function() internal a; } - function f(S) {} - } - )"; - CHECK_ERROR(text, TypeError, "Internal or recursive type is not allowed for public or external functions."); -} - -BOOST_AUTO_TEST_CASE(functions_with_stucts_of_non_external_types_in_interface_2) -{ - char const* text = R"( - pragma experimental ABIEncoderV2; - - contract C { - struct S { mapping(uint => uint) a; } - function f(S) {} - } - )"; - CHECK_ERROR(text, TypeError, "Internal or recursive type is not allowed for public or external functions."); -} - -BOOST_AUTO_TEST_CASE(functions_with_stucts_of_non_external_types_in_interface_nested) -{ - char const* text = R"( - pragma experimental ABIEncoderV2; - - contract C { - struct T { mapping(uint => uint) a; } - struct S { T[][2] b; } - function f(S) {} - } - )"; - CHECK_ERROR(text, TypeError, "Internal or recursive type is not allowed for public or external functions."); -} - -BOOST_AUTO_TEST_CASE(returning_multi_dimensional_arrays_new_abi) -{ - char const* text = R"( - pragma experimental ABIEncoderV2; - - contract C { - function f() public pure returns (string[][]) {} - } - )"; - CHECK_WARNING(text, "Experimental features"); -} - -BOOST_AUTO_TEST_CASE(returning_multi_dimensional_arrays) -{ - char const* text = R"( - contract C { - function f() public pure returns (string[][]) {} - } - )"; - CHECK_ERROR(text, TypeError, "only supported in the new experimental ABI encoder"); -} - -BOOST_AUTO_TEST_CASE(returning_multi_dimensional_static_arrays) -{ - char const* text = R"( - contract C { - function f() public pure returns (uint[][2]) {} - } - )"; - CHECK_ERROR(text, TypeError, "only supported in the new experimental ABI encoder"); -} - -BOOST_AUTO_TEST_CASE(returning_arrays_in_structs_new_abi) -{ - char const* text = R"( - pragma experimental ABIEncoderV2; - - contract C { - struct S { string[] s; } - function f() public pure returns (S) {} - } - )"; - CHECK_WARNING(text, "Experimental features"); -} - -BOOST_AUTO_TEST_CASE(returning_arrays_in_structs_arrays) -{ - char const* text = R"( - contract C { - struct S { string[] s; } - function f() public pure returns (S x) {} - } - )"; - CHECK_ERROR(text, TypeError, "only supported in the new experimental ABI encoder"); -} - -BOOST_AUTO_TEST_CASE(function_external_call_allowed_conversion) -{ - char const* text = R"( - contract C {} - contract Test { - function externalCall() public { - C arg; - this.g(arg); - } - function g (C c) external {} - } - )"; - CHECK_SUCCESS(text); -} - -BOOST_AUTO_TEST_CASE(function_external_call_not_allowed_conversion) -{ - char const* text = R"( - contract C {} - contract Test { - function externalCall() public { - address arg; - this.g(arg); - } - function g (C c) external {} - } - )"; - CHECK_ERROR(text, TypeError, "Invalid type for argument in function call. Invalid implicit conversion from address to contract C requested."); -} - -BOOST_AUTO_TEST_CASE(function_internal_allowed_conversion) -{ - char const* text = R"( - contract C { - uint a; - } - contract Test { - C a; - function g (C c) public {} - function internalCall() public { - g(a); - } - } - )"; - CHECK_SUCCESS(text); -} - -BOOST_AUTO_TEST_CASE(function_internal_not_allowed_conversion) -{ - char const* text = R"( - contract C { - uint a; - } - contract Test { - address a; - function g (C c) public {} - function internalCall() public { - g(a); - } - } - )"; - CHECK_ERROR(text, TypeError, "Invalid type for argument in function call. Invalid implicit conversion from address to contract C requested."); -} - -BOOST_AUTO_TEST_CASE(hash_collision_in_interface) -{ - char const* text = R"( - contract test { - function gsf() public { } - function tgeo() public { } - } - )"; - CHECK_ERROR(text, TypeError, "Function signature hash collision for tgeo()"); -} - -BOOST_AUTO_TEST_CASE(inheritance_basic) -{ - char const* text = R"( - contract base { uint baseMember; struct BaseType { uint element; } } - contract derived is base { - BaseType data; - function f() public { baseMember = 7; } - } - )"; - CHECK_SUCCESS(text); -} - -BOOST_AUTO_TEST_CASE(inheritance_diamond_basic) -{ - char const* text = R"( - contract root { function rootFunction() public {} } - contract inter1 is root { function f() public {} } - contract inter2 is root { function f() public {} } - contract derived is root, inter2, inter1 { - function g() public { f(); rootFunction(); } - } - )"; - CHECK_SUCCESS(text); -} - -BOOST_AUTO_TEST_CASE(cyclic_inheritance) -{ - char const* text = R"( - contract A is B { } - contract B is A { } - )"; - CHECK_ERROR_ALLOW_MULTI(text, TypeError, (vector<string>{"Definition of base has to precede definition of derived contract"})); -} - -BOOST_AUTO_TEST_CASE(legal_override_direct) -{ - char const* text = R"( - contract B { function f() public {} } - contract C is B { function f(uint i) public {} } - )"; - CHECK_SUCCESS(text); -} - -BOOST_AUTO_TEST_CASE(legal_override_indirect) -{ - char const* text = R"( - contract A { function f(uint a) public {} } - contract B { function f() public {} } - contract C is A, B { } - )"; - CHECK_SUCCESS(text); -} - -BOOST_AUTO_TEST_CASE(illegal_override_visibility) -{ - char const* text = R"( - contract B { function f() internal {} } - contract C is B { function f() public {} } - )"; - CHECK_ERROR(text, TypeError, "Overriding function visibility differs"); -} - -BOOST_AUTO_TEST_CASE(complex_inheritance) -{ - char const* text = R"( - contract A { function f() public { uint8 x = C(0).g(); } } - contract B { function f() public {} function g() public returns (uint8) {} } - contract C is A, B { } - )"; - CHECK_SUCCESS(text); -} - -BOOST_AUTO_TEST_CASE(missing_base_constructor_arguments) -{ - char const* text = R"( - contract A { function A(uint a) public { } } - contract B is A { } - )"; - CHECK_SUCCESS(text); -} - -BOOST_AUTO_TEST_CASE(base_constructor_arguments_override) -{ - char const* text = R"( - contract A { function A(uint a) public { } } - contract B is A { } - )"; - CHECK_SUCCESS(text); -} - -BOOST_AUTO_TEST_CASE(implicit_derived_to_base_conversion) -{ - char const* text = R"( - contract A { } - contract B is A { - function f() public { A a = B(1); } - } - )"; - CHECK_SUCCESS(text); -} - -BOOST_AUTO_TEST_CASE(implicit_base_to_derived_conversion) -{ - char const* text = R"( - contract A { } - contract B is A { - function f() public { B b = A(1); } - } - )"; - CHECK_ERROR(text, TypeError, "Type contract A is not implicitly convertible to expected type contract B."); -} - -BOOST_AUTO_TEST_CASE(super_excludes_current_contract) -{ - char const* text = R"( - contract A { - function b() public {} - } - - contract B is A { - function f() public { - super.f(); - } - } - )"; - - CHECK_ERROR(text, TypeError, "Member \"f\" not found or not visible after argument-dependent lookup in contract super B"); -} - BOOST_AUTO_TEST_CASE(state_variable_accessors) { char const* text = R"( @@ -939,20 +313,6 @@ BOOST_AUTO_TEST_CASE(state_variable_accessors) BOOST_CHECK(function->stateMutability() == StateMutability::View); } -BOOST_AUTO_TEST_CASE(function_clash_with_state_variable_accessor) -{ - char const* text = R"( - contract test { - function fun() public { - uint64(2); - } - uint256 foo; - function foo() public {} - } - )"; - CHECK_ERROR(text, DeclarationError, "Identifier already declared."); -} - BOOST_AUTO_TEST_CASE(private_state_variable) { char const* text = R"( @@ -975,3859 +335,40 @@ BOOST_AUTO_TEST_CASE(private_state_variable) BOOST_CHECK_MESSAGE(function == nullptr, "Accessor function of an internal variable should not exist"); } -BOOST_AUTO_TEST_CASE(base_class_state_variable_accessor) -{ - // test for issue #1126 https://github.com/ethereum/cpp-ethereum/issues/1126 - char const* text = R"( - contract Parent { - uint256 public m_aMember; - } - contract Child is Parent { - function foo() public returns (uint256) { return Parent.m_aMember; } - } - )"; - CHECK_SUCCESS(text); -} - -BOOST_AUTO_TEST_CASE(struct_accessor_one_array_only) -{ - char const* sourceCode = R"( - contract test { - struct Data { uint[15] m_array; } - Data public data; - } - )"; - CHECK_ERROR(sourceCode, TypeError, "Internal or recursive type is not allowed for public state variables."); -} - -BOOST_AUTO_TEST_CASE(base_class_state_variable_internal_member) -{ - char const* text = R"( - contract Parent { - uint256 internal m_aMember; - } - contract Child is Parent { - function foo() public returns (uint256) { return Parent.m_aMember; } - } - )"; - CHECK_SUCCESS(text); -} - -BOOST_AUTO_TEST_CASE(state_variable_member_of_wrong_class1) -{ - char const* text = R"( - contract Parent1 { - uint256 internal m_aMember1; - } - contract Parent2 is Parent1 { - uint256 internal m_aMember2; - } - contract Child is Parent2 { - function foo() public returns (uint256) { return Parent2.m_aMember1; } - } - )"; - CHECK_ERROR(text, TypeError, "Member \"m_aMember1\" not found or not visible after argument-dependent lookup in type(contract Parent2)"); -} - -BOOST_AUTO_TEST_CASE(state_variable_member_of_wrong_class2) -{ - char const* text = R"( - contract Parent1 { - uint256 internal m_aMember1; - } - contract Parent2 is Parent1 { - uint256 internal m_aMember2; - } - contract Child is Parent2 { - function foo() public returns (uint256) { return Child.m_aMember2; } - uint256 public m_aMember3; - } - )"; - CHECK_ERROR(text, TypeError, "Member \"m_aMember2\" not found or not visible after argument-dependent lookup in type(contract Child)"); -} - -BOOST_AUTO_TEST_CASE(fallback_function) -{ - char const* text = R"( - contract C { - uint x; - function() public { x = 2; } - } - )"; - CHECK_SUCCESS(text); -} - -BOOST_AUTO_TEST_CASE(fallback_function_with_arguments) -{ - char const* text = R"( - contract C { - uint x; - function(uint a) public { x = 2; } - } - )"; - CHECK_ERROR(text, TypeError, "Fallback function cannot take parameters."); -} - -BOOST_AUTO_TEST_CASE(fallback_function_in_library) -{ - char const* text = R"( - library C { - function() public {} - } - )"; - CHECK_ERROR(text, TypeError, "Libraries cannot have fallback functions."); -} - -BOOST_AUTO_TEST_CASE(fallback_function_with_return_parameters) -{ - char const* text = R"( - contract C { - function() public returns (uint) { } - } - )"; - CHECK_ERROR(text, TypeError, "Fallback function cannot return values."); -} - -BOOST_AUTO_TEST_CASE(fallback_function_twice) -{ - char const* text = R"( - contract C { - uint x; - function() public { x = 2; } - function() public { x = 3; } - } - )"; - CHECK_ERROR_ALLOW_MULTI(text, DeclarationError, (vector<string>{ - "Only one fallback function is" - })); -} - -BOOST_AUTO_TEST_CASE(fallback_function_inheritance) -{ - char const* text = R"( - contract A { - uint x; - function() public { x = 1; } - } - contract C is A { - function() public { x = 2; } - } - )"; - CHECK_SUCCESS(text); -} - -BOOST_AUTO_TEST_CASE(event) -{ - char const* text = R"( - contract c { - event e(uint indexed a, bytes3 indexed s, bool indexed b); - function f() public { e(2, "abc", true); } - } - )"; - CHECK_SUCCESS(text); -} - -BOOST_AUTO_TEST_CASE(event_too_many_indexed) -{ - char const* text = R"( - contract c { - event e(uint indexed a, bytes3 indexed b, bool indexed c, uint indexed d); - } - )"; - CHECK_ERROR(text, TypeError, "More than 3 indexed arguments for event."); -} - -BOOST_AUTO_TEST_CASE(anonymous_event_four_indexed) -{ - char const* text = R"( - contract c { - event e(uint indexed a, bytes3 indexed b, bool indexed c, uint indexed d) anonymous; - } - )"; - CHECK_SUCCESS(text); -} - -BOOST_AUTO_TEST_CASE(anonymous_event_too_many_indexed) -{ - char const* text = R"( - contract c { - event e(uint indexed a, bytes3 indexed b, bool indexed c, uint indexed d, uint indexed e) anonymous; - } - )"; - CHECK_ERROR(text, TypeError, "More than 4 indexed arguments for anonymous event."); -} - -BOOST_AUTO_TEST_CASE(events_with_same_name) -{ - char const* text = R"( - contract TestIt { - event A(); - event A(uint i); - } - )"; - CHECK_SUCCESS(text); -} - -BOOST_AUTO_TEST_CASE(events_with_same_name_unnamed_arguments) -{ - char const* text = R"( - contract test { - event A(uint); - event A(uint, uint); - } - )"; - CHECK_SUCCESS(text); -} - -BOOST_AUTO_TEST_CASE(events_with_same_name_different_types) -{ - char const* text = R"( - contract test { - event A(uint); - event A(bytes); - } - )"; - CHECK_SUCCESS(text); -} - -BOOST_AUTO_TEST_CASE(double_event_declaration) -{ - char const* text = R"( - contract test { - event A(uint i); - event A(uint i); - } - )"; - CHECK_ERROR(text, DeclarationError, "Event with same name and arguments defined twice."); -} - -BOOST_AUTO_TEST_CASE(double_event_declaration_ignores_anonymous) -{ - char const* text = R"( - contract test { - event A(uint i); - event A(uint i) anonymous; - } - )"; - CHECK_ERROR(text, DeclarationError, "Event with same name and arguments defined twice."); -} - -BOOST_AUTO_TEST_CASE(double_event_declaration_ignores_indexed) -{ - char const* text = R"( - contract test { - event A(uint i); - event A(uint indexed i); - } - )"; - CHECK_ERROR(text, DeclarationError, "Event with same name and arguments defined twice."); -} - -BOOST_AUTO_TEST_CASE(event_call) -{ - char const* text = R"( - contract c { - event e(uint a, bytes3 indexed s, bool indexed b); - function f() public { e(2, "abc", true); } - } - )"; - CHECK_SUCCESS(text); -} - -BOOST_AUTO_TEST_CASE(event_function_inheritance_clash) -{ - char const* text = R"( - contract A { - function dup() public returns (uint) { - return 1; - } - } - contract B { - event dup(); - } - contract C is A, B { - } - )"; - CHECK_ERROR(text, DeclarationError, "Identifier already declared."); -} - -BOOST_AUTO_TEST_CASE(function_event_inheritance_clash) -{ - char const* text = R"( - contract B { - event dup(); - } - contract A { - function dup() public returns (uint) { - return 1; - } - } - contract C is B, A { - } - )"; - CHECK_ERROR(text, DeclarationError, "Identifier already declared."); -} - -BOOST_AUTO_TEST_CASE(function_event_in_contract_clash) -{ - char const* text = R"( - contract A { - event dup(); - function dup() public returns (uint) { - return 1; - } - } - )"; - CHECK_ERROR(text, DeclarationError, "Identifier already declared."); -} - -BOOST_AUTO_TEST_CASE(event_inheritance) -{ - char const* text = R"( - contract base { - event e(uint a, bytes3 indexed s, bool indexed b); - } - contract c is base { - function f() public { e(2, "abc", true); } - } - )"; - CHECK_SUCCESS(text); -} - -BOOST_AUTO_TEST_CASE(multiple_events_argument_clash) -{ - char const* text = R"( - contract c { - event e1(uint a, uint e1, uint e2); - event e2(uint a, uint e1, uint e2); - } - )"; - CHECK_SUCCESS(text); -} - -BOOST_AUTO_TEST_CASE(access_to_default_function_visibility) -{ - char const* text = R"( - contract c { - function f() public {} - } - contract d { - function g() public { c(0).f(); } - } - )"; - CHECK_SUCCESS(text); -} - -BOOST_AUTO_TEST_CASE(access_to_internal_function) -{ - char const* text = R"( - contract c { - function f() internal {} - } - contract d { - function g() public { c(0).f(); } - } - )"; - CHECK_ERROR(text, TypeError, "Member \"f\" not found or not visible after argument-dependent lookup in contract c"); -} - -BOOST_AUTO_TEST_CASE(access_to_default_state_variable_visibility) -{ - char const* text = R"( - contract c { - uint a; - } - contract d { - function g() public { c(0).a(); } - } - )"; - CHECK_ERROR(text, TypeError, "Member \"a\" not found or not visible after argument-dependent lookup in contract c"); -} - -BOOST_AUTO_TEST_CASE(access_to_internal_state_variable) -{ - char const* text = R"( - contract c { - uint public a; - } - contract d { - function g() public { c(0).a(); } - } - )"; - CHECK_SUCCESS(text); -} - -BOOST_AUTO_TEST_CASE(error_count_in_named_args) -{ - char const* sourceCode = R"( - contract test { - function a(uint a, uint b) public returns (uint r) { - r = a + b; - } - function b() public returns (uint r) { - r = a({a: 1}); - } - } - )"; - CHECK_ERROR(sourceCode, TypeError, "Wrong argument count for function call: 1 arguments given but expected 2."); -} - -BOOST_AUTO_TEST_CASE(empty_in_named_args) -{ - char const* sourceCode = R"( - contract test { - function a(uint a, uint b) public returns (uint r) { - r = a + b; - } - function b() public returns (uint r) { - r = a({}); - } - } - )"; - CHECK_ERROR(sourceCode, TypeError, "Wrong argument count for function call: 0 arguments given but expected 2."); -} - -BOOST_AUTO_TEST_CASE(duplicate_parameter_names_in_named_args) -{ - char const* sourceCode = R"( - contract test { - function a(uint a, uint b) public returns (uint r) { - r = a + b; - } - function b() public returns (uint r) { - r = a({a: 1, a: 2}); - } - } - )"; - CHECK_ERROR(sourceCode, TypeError, "Duplicate named argument."); -} - -BOOST_AUTO_TEST_CASE(invalid_parameter_names_in_named_args) -{ - char const* sourceCode = R"( - contract test { - function a(uint a, uint b) public returns (uint r) { - r = a + b; - } - function b() public returns (uint r) { - r = a({a: 1, c: 2}); - } - } - )"; - CHECK_ERROR(sourceCode, TypeError, "Named argument does not match function declaration."); -} - -BOOST_AUTO_TEST_CASE(empty_name_input_parameter) -{ - char const* text = R"( - contract test { - function f(uint) public { } - } - )"; - CHECK_SUCCESS(text); -} - -BOOST_AUTO_TEST_CASE(constant_input_parameter) -{ - char const* text = R"( - contract test { - function f(uint[] constant a) public { } - } - )"; - CHECK_ERROR_ALLOW_MULTI(text, TypeError, (vector<string>{ - "Illegal use of \"constant\" specifier", - "Constants of non-value type not yet implemented", - "Uninitialized \"constant\" variable" - })); -} - -BOOST_AUTO_TEST_CASE(empty_name_return_parameter) -{ - char const* text = R"( - contract test { - function f() public returns (bool) { } - } - )"; - CHECK_SUCCESS(text); -} - -BOOST_AUTO_TEST_CASE(empty_name_input_parameter_with_named_one) -{ - char const* text = R"( - contract test { - function f(uint, uint k) public returns (uint ret_k) { - return k; - } - } - )"; - CHECK_SUCCESS(text); -} - -BOOST_AUTO_TEST_CASE(empty_name_return_parameter_with_named_one) -{ - char const* text = R"( - contract test { - function f() public returns (uint ret_k, uint) { - return 5; - } - } - )"; - CHECK_ERROR(text, TypeError, "Different number of arguments in return statement than in returns declaration."); -} - -BOOST_AUTO_TEST_CASE(disallow_declaration_of_void_type) -{ - char const* sourceCode = R"( - contract c { - function f() public { var (x) = f(); } - } - )"; - CHECK_ERROR(sourceCode, TypeError, "Not enough components (0) in value to assign all variables (1)."); -} - -BOOST_AUTO_TEST_CASE(overflow_caused_by_ether_units) -{ - char const* sourceCodeFine = R"( - contract c { - function c () public { - a = 115792089237316195423570985008687907853269984665640564039458; - } - uint256 a; - } - )"; - CHECK_SUCCESS(sourceCodeFine); - char const* sourceCode = R"( - contract c { - function c () public { - a = 115792089237316195423570985008687907853269984665640564039458 ether; - } - uint256 a; - } - )"; - CHECK_ERROR(sourceCode, TypeError, "Type int_const 1157...(70 digits omitted)...0000 is not implicitly convertible to expected type uint256."); -} - -BOOST_AUTO_TEST_CASE(exp_operator_exponent_too_big) -{ - char const* sourceCode = R"( - contract test { - function f() public returns (uint d) { return 2 ** 10000000000; } - } - )"; - CHECK_ERROR(sourceCode, TypeError, "Operator ** not compatible with types int_const 2 and int_const 10000000000"); -} - -BOOST_AUTO_TEST_CASE(exp_warn_literal_base) -{ - char const* sourceCode = R"( - contract test { - function f() pure public returns(uint) { - uint8 x = 100; - return 10**x; - } - } - )"; - CHECK_WARNING(sourceCode, "might overflow"); - sourceCode = R"( - contract test { - function f() pure public returns(uint) { - uint8 x = 100; - return uint8(10)**x; - } - } - )"; - CHECK_SUCCESS(sourceCode); - sourceCode = R"( - contract test { - function f() pure public returns(uint) { - return 2**80; - } - } - )"; - CHECK_SUCCESS(sourceCode); -} - -BOOST_AUTO_TEST_CASE(shift_warn_literal_base) -{ - char const* sourceCode = R"( - contract test { - function f() pure public returns(uint) { - uint8 x = 100; - return 10 << x; - } - } - )"; - CHECK_WARNING(sourceCode, "might overflow"); - sourceCode = R"( - contract test { - function f() pure public returns(uint) { - uint8 x = 100; - return uint8(10) << x; - } - } - )"; - CHECK_SUCCESS(sourceCode); - sourceCode = R"( - contract test { - function f() pure public returns(uint) { - return 2 << 80; - } - } - )"; - CHECK_SUCCESS(sourceCode); - sourceCode = R"( - contract test { - function f() pure public returns(uint) { - uint8 x = 100; - return 10 >> x; - } - } - )"; - CHECK_SUCCESS(sourceCode); -} - -BOOST_AUTO_TEST_CASE(warn_var_from_zero) -{ - char const* sourceCode = R"( - contract test { - function f() pure public returns (uint) { - var i = 1; - return i; - } - } - )"; - CHECK_WARNING_ALLOW_MULTI(sourceCode, (std::vector<std::string>{ - "uint8, which can hold values between 0 and 255", - "Use of the \"var\" keyword is deprecated." - })); - sourceCode = R"( - contract test { - function f() pure public { - var i = 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff; - i; - } - } - )"; - CHECK_WARNING_ALLOW_MULTI(sourceCode, (std::vector<std::string>{ - "uint256, which can hold values between 0 and 115792089237316195423570985008687907853269984665640564039457584007913129639935", - "Use of the \"var\" keyword is deprecated." - })); - sourceCode = R"( - contract test { - function f() pure public { - var i = -2; - i; - } - } - )"; - CHECK_WARNING_ALLOW_MULTI(sourceCode, (std::vector<std::string>{ - "int8, which can hold values between -128 and 127", - "Use of the \"var\" keyword is deprecated." - })); - sourceCode = R"( - contract test { - function f() pure public { - for (var i = 0; i < msg.data.length; i++) { } - } - } - )"; - CHECK_WARNING_ALLOW_MULTI(sourceCode, (std::vector<std::string>{ - "uint8, which can hold", - "Use of the \"var\" keyword is deprecated." - })); -} - -BOOST_AUTO_TEST_CASE(enum_member_access) -{ - char const* text = R"( - contract test { - enum ActionChoices { GoLeft, GoRight, GoStraight, Sit } - function test() - { - choices = ActionChoices.GoStraight; - } - ActionChoices choices; - } - )"; - CHECK_SUCCESS(text); -} - -BOOST_AUTO_TEST_CASE(enum_member_access_accross_contracts) -{ - char const* text = R"( - contract Interface { - enum MyEnum { One, Two } - } - contract Impl { - function test() public returns (Interface.MyEnum) { - return Interface.MyEnum.One; - } - } - )"; - CHECK_SUCCESS(text); -} - -BOOST_AUTO_TEST_CASE(enum_invalid_member_access) -{ - char const* text = R"( - contract test { - enum ActionChoices { GoLeft, GoRight, GoStraight, Sit } - function test() public { - choices = ActionChoices.RunAroundWavingYourHands; - } - ActionChoices choices; - } - )"; - CHECK_ERROR(text, TypeError, "Member \"RunAroundWavingYourHands\" not found or not visible after argument-dependent lookup in type(enum test.ActionChoices)"); -} - -BOOST_AUTO_TEST_CASE(enum_invalid_direct_member_access) -{ - char const* text = R"( - contract test { - enum ActionChoices { GoLeft, GoRight, GoStraight, Sit } - function test() public { - choices = Sit; - } - ActionChoices choices; - } - )"; - CHECK_ERROR(text, DeclarationError, "Undeclared identifier."); -} - -BOOST_AUTO_TEST_CASE(enum_explicit_conversion_is_okay) -{ - char const* text = R"( - contract test { - enum ActionChoices { GoLeft, GoRight, GoStraight, Sit } - function test() public { - a = uint256(ActionChoices.GoStraight); - b = uint64(ActionChoices.Sit); - } - uint256 a; - uint64 b; - } - )"; - CHECK_SUCCESS(text); -} - -BOOST_AUTO_TEST_CASE(int_to_enum_explicit_conversion_is_okay) -{ - char const* text = R"( - contract test { - enum ActionChoices { GoLeft, GoRight, GoStraight, Sit } - function test() public { - a = 2; - b = ActionChoices(a); - } - uint256 a; - ActionChoices b; - } - )"; - CHECK_SUCCESS(text); -} - -BOOST_AUTO_TEST_CASE(enum_implicit_conversion_is_not_okay_256) -{ - char const* text = R"( - contract test { - enum ActionChoices { GoLeft, GoRight, GoStraight, Sit } - function test() public { - a = ActionChoices.GoStraight; - } - uint256 a; - } - )"; - CHECK_ERROR(text, TypeError, "Type enum test.ActionChoices is not implicitly convertible to expected type uint256."); -} - -BOOST_AUTO_TEST_CASE(enum_implicit_conversion_is_not_okay_64) -{ - char const* text = R"( - contract test { - enum ActionChoices { GoLeft, GoRight, GoStraight, Sit } - function test() public { - b = ActionChoices.Sit; - } - uint64 b; - } - )"; - CHECK_ERROR(text, TypeError, "Type enum test.ActionChoices is not implicitly convertible to expected type uint64."); -} - -BOOST_AUTO_TEST_CASE(enum_to_enum_conversion_is_not_okay) -{ - char const* text = R"( - contract test { - enum Paper { Up, Down, Left, Right } - enum Ground { North, South, West, East } - function test() public { - Ground(Paper.Up); - } - } - )"; - CHECK_ERROR(text, TypeError, "Explicit type conversion not allowed from \"enum test.Paper\" to \"enum test.Ground\"."); -} - -BOOST_AUTO_TEST_CASE(enum_duplicate_values) -{ - char const* text = R"( - contract test { - enum ActionChoices { GoLeft, GoRight, GoLeft, Sit } - } - )"; - CHECK_ERROR(text, DeclarationError, "Identifier already declared."); -} - -BOOST_AUTO_TEST_CASE(enum_name_resolution_under_current_contract_name) -{ - char const* text = R"( - contract A { - enum Foo { - First, - Second - } - - function a() public { - A.Foo; - } - } - )"; - CHECK_SUCCESS(text); -} - -BOOST_AUTO_TEST_CASE(private_visibility) -{ - char const* sourceCode = R"( - contract base { - function f() private {} - } - contract derived is base { - function g() public { f(); } - } - )"; - CHECK_ERROR(sourceCode, DeclarationError, "Undeclared identifier."); -} - -BOOST_AUTO_TEST_CASE(private_visibility_via_explicit_base_access) -{ - char const* sourceCode = R"( - contract base { - function f() private {} - } - contract derived is base { - function g() public { base.f(); } - } - )"; - CHECK_ERROR(sourceCode, TypeError, "Member \"f\" not found or not visible after argument-dependent lookup in type(contract base)"); -} - -BOOST_AUTO_TEST_CASE(external_visibility) -{ - char const* sourceCode = R"( - contract c { - function f() external {} - function g() public { f(); } - } - )"; - CHECK_ERROR(sourceCode, DeclarationError, "Undeclared identifier."); -} - -BOOST_AUTO_TEST_CASE(similar_name_suggestions_expected) -{ - char const* sourceCode = R"( - contract c { - function func() {} - function g() public { fun(); } - } - )"; - CHECK_ERROR(sourceCode, DeclarationError, "Undeclared identifier. Did you mean \"func\"?"); -} - -BOOST_AUTO_TEST_CASE(no_name_suggestion) -{ - char const* sourceCode = R"( - contract c { - function g() public { fun(); } - } - )"; - CHECK_ERROR(sourceCode, DeclarationError, "Undeclared identifier."); -} - -BOOST_AUTO_TEST_CASE(multiple_similar_suggestions) -{ - char const* sourceCode = R"( - contract c { - function g() public { - uint var1 = 1; - uint var2 = 1; - uint var3 = 1; - uint var4 = 1; - uint var5 = varx; - } - } - )"; - CHECK_ERROR(sourceCode, DeclarationError, "Undeclared identifier. Did you mean \"var1\", \"var2\", \"var3\", \"var4\" or \"var5\"?"); -} - -BOOST_AUTO_TEST_CASE(multiple_scopes_suggestions) -{ - char const* sourceCode = R"( - contract c { - uint log9 = 2; - function g() public { - uint log8 = 3; - uint var1 = lgox; - } - } - )"; - CHECK_ERROR(sourceCode, DeclarationError, "Undeclared identifier. Did you mean \"log8\", \"log9\", \"log0\", \"log1\", \"log2\", \"log3\" or \"log4\"?"); -} - -BOOST_AUTO_TEST_CASE(inheritence_suggestions) -{ - char const* sourceCode = R"( - contract a { function func() public {} } - contract c is a { - function g() public { - uint var1 = fun(); - } - } - )"; - CHECK_ERROR(sourceCode, DeclarationError, "Undeclared identifier. Did you mean \"func\"?"); -} - -BOOST_AUTO_TEST_CASE(no_spurious_suggestions) -{ - char const* sourceCode = R"( - contract c { - function g() public { - uint va = 1; - uint vb = vaxyz; - } - } - )"; - CHECK_ERROR(sourceCode, DeclarationError, "Undeclared identifier."); - - sourceCode = R"( - contract c { - function g() public { - uint va = 1; - uint vb = x; - } - } - )"; - CHECK_ERROR(sourceCode, DeclarationError, "Undeclared identifier."); -} - -BOOST_AUTO_TEST_CASE(external_base_visibility) -{ - char const* sourceCode = R"( - contract base { - function f() external {} - } - contract derived is base { - function g() public { base.f(); } - } - )"; - CHECK_ERROR(sourceCode, TypeError, "Member \"f\" not found or not visible after argument-dependent lookup in type(contract base)"); -} - -BOOST_AUTO_TEST_CASE(external_argument_assign) -{ - char const* sourceCode = R"( - contract c { - function f(uint a) external { a = 1; } - } - )"; - CHECK_ERROR(sourceCode, TypeError, "Expression has to be an lvalue."); -} - -BOOST_AUTO_TEST_CASE(external_argument_increment) -{ - char const* sourceCode = R"( - contract c { - function f(uint a) external { a++; } - } - )"; - CHECK_ERROR(sourceCode, TypeError, "Expression has to be an lvalue."); -} - -BOOST_AUTO_TEST_CASE(external_argument_delete) -{ - char const* sourceCode = R"( - contract c { - function f(uint a) external { delete a; } - } - )"; - CHECK_ERROR(sourceCode, TypeError, "Expression has to be an lvalue."); -} - -BOOST_AUTO_TEST_CASE(test_for_bug_override_function_with_bytearray_type) -{ - char const* sourceCode = R"( - contract Vehicle { - function f(bytes) external returns (uint256 r) {r = 1;} - } - contract Bike is Vehicle { - function f(bytes) external returns (uint256 r) {r = 42;} - } - )"; - CHECK_SUCCESS(sourceCode); -} - -BOOST_AUTO_TEST_CASE(array_with_nonconstant_length) -{ - char const* text = R"( - contract c { - function f(uint a) public { uint8[a] x; } - } - )"; - CHECK_ERROR(text, TypeError, "Invalid array length, expected integer literal or constant expression."); -} - -BOOST_AUTO_TEST_CASE(array_with_negative_length) -{ - char const* text = R"( - contract c { - function f(uint a) public { uint8[-1] x; } - } - )"; - CHECK_ERROR(text, TypeError, "Array with negative length specified"); -} - -BOOST_AUTO_TEST_CASE(array_copy_with_different_types1) -{ - char const* text = R"( - contract c { - bytes a; - uint[] b; - function f() public { b = a; } - } - )"; - CHECK_ERROR(text, TypeError, "Type bytes storage ref is not implicitly convertible to expected type uint256[] storage ref."); -} - -BOOST_AUTO_TEST_CASE(array_copy_with_different_types2) -{ - char const* text = R"( - contract c { - uint32[] a; - uint8[] b; - function f() public { b = a; } - } - )"; - CHECK_ERROR(text, TypeError, "Type uint32[] storage ref is not implicitly convertible to expected type uint8[] storage ref."); -} - -BOOST_AUTO_TEST_CASE(array_copy_with_different_types_conversion_possible) -{ - char const* text = R"( - contract c { - uint32[] a; - uint8[] b; - function f() public { a = b; } - } - )"; - CHECK_SUCCESS(text); -} - -BOOST_AUTO_TEST_CASE(array_copy_with_different_types_static_dynamic) -{ - char const* text = R"( - contract c { - uint32[] a; - uint8[80] b; - function f() public { a = b; } - } - )"; - CHECK_SUCCESS(text); -} - -BOOST_AUTO_TEST_CASE(array_copy_with_different_types_dynamic_static) -{ - char const* text = R"( - contract c { - uint[] a; - uint[80] b; - function f() public { b = a; } - } - )"; - CHECK_ERROR(text, TypeError, "Type uint256[] storage ref is not implicitly convertible to expected type uint256[80] storage ref."); -} - -BOOST_AUTO_TEST_CASE(array_of_undeclared_type) -{ - char const* text = R"( - contract c { - a[] public foo; - } - )"; - CHECK_ERROR(text, DeclarationError, "Identifier not found or not unique."); -} - -BOOST_AUTO_TEST_CASE(storage_variable_initialization_with_incorrect_type_int) -{ - char const* text = R"( - contract c { - uint8 a = 1000; - } - )"; - CHECK_ERROR(text, TypeError, "Type int_const 1000 is not implicitly convertible to expected type uint8."); -} - -BOOST_AUTO_TEST_CASE(storage_variable_initialization_with_incorrect_type_string) -{ - char const* text = R"( - contract c { - uint a = "abc"; - } - )"; - CHECK_ERROR(text, TypeError, "Type literal_string \"abc\" is not implicitly convertible to expected type uint256."); -} - -BOOST_AUTO_TEST_CASE(test_fromElementaryTypeName) -{ - - BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::Int, 0, 0)) == *make_shared<IntegerType>(256, IntegerType::Modifier::Signed)); - BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::IntM, 8, 0)) == *make_shared<IntegerType>(8, IntegerType::Modifier::Signed)); - BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::IntM, 16, 0)) == *make_shared<IntegerType>(16, IntegerType::Modifier::Signed)); - BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::IntM, 24, 0)) == *make_shared<IntegerType>(24, IntegerType::Modifier::Signed)); - BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::IntM, 32, 0)) == *make_shared<IntegerType>(32, IntegerType::Modifier::Signed)); - BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::IntM, 40, 0)) == *make_shared<IntegerType>(40, IntegerType::Modifier::Signed)); - BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::IntM, 48, 0)) == *make_shared<IntegerType>(48, IntegerType::Modifier::Signed)); - BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::IntM, 56, 0)) == *make_shared<IntegerType>(56, IntegerType::Modifier::Signed)); - BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::IntM, 64, 0)) == *make_shared<IntegerType>(64, IntegerType::Modifier::Signed)); - BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::IntM, 72, 0)) == *make_shared<IntegerType>(72, IntegerType::Modifier::Signed)); - BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::IntM, 80, 0)) == *make_shared<IntegerType>(80, IntegerType::Modifier::Signed)); - BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::IntM, 88, 0)) == *make_shared<IntegerType>(88, IntegerType::Modifier::Signed)); - BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::IntM, 96, 0)) == *make_shared<IntegerType>(96, IntegerType::Modifier::Signed)); - BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::IntM, 104, 0)) == *make_shared<IntegerType>(104, IntegerType::Modifier::Signed)); - BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::IntM, 112, 0)) == *make_shared<IntegerType>(112, IntegerType::Modifier::Signed)); - BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::IntM, 120, 0)) == *make_shared<IntegerType>(120, IntegerType::Modifier::Signed)); - BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::IntM, 128, 0)) == *make_shared<IntegerType>(128, IntegerType::Modifier::Signed)); - BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::IntM, 136, 0)) == *make_shared<IntegerType>(136, IntegerType::Modifier::Signed)); - BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::IntM, 144, 0)) == *make_shared<IntegerType>(144, IntegerType::Modifier::Signed)); - BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::IntM, 152, 0)) == *make_shared<IntegerType>(152, IntegerType::Modifier::Signed)); - BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::IntM, 160, 0)) == *make_shared<IntegerType>(160, IntegerType::Modifier::Signed)); - BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::IntM, 168, 0)) == *make_shared<IntegerType>(168, IntegerType::Modifier::Signed)); - BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::IntM, 176, 0)) == *make_shared<IntegerType>(176, IntegerType::Modifier::Signed)); - BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::IntM, 184, 0)) == *make_shared<IntegerType>(184, IntegerType::Modifier::Signed)); - BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::IntM, 192, 0)) == *make_shared<IntegerType>(192, IntegerType::Modifier::Signed)); - BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::IntM, 200, 0)) == *make_shared<IntegerType>(200, IntegerType::Modifier::Signed)); - BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::IntM, 208, 0)) == *make_shared<IntegerType>(208, IntegerType::Modifier::Signed)); - BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::IntM, 216, 0)) == *make_shared<IntegerType>(216, IntegerType::Modifier::Signed)); - BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::IntM, 224, 0)) == *make_shared<IntegerType>(224, IntegerType::Modifier::Signed)); - BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::IntM, 232, 0)) == *make_shared<IntegerType>(232, IntegerType::Modifier::Signed)); - BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::IntM, 240, 0)) == *make_shared<IntegerType>(240, IntegerType::Modifier::Signed)); - BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::IntM, 248, 0)) == *make_shared<IntegerType>(248, IntegerType::Modifier::Signed)); - BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::IntM, 256, 0)) == *make_shared<IntegerType>(256, IntegerType::Modifier::Signed)); - - BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::UInt, 0, 0)) == *make_shared<IntegerType>(256, IntegerType::Modifier::Unsigned)); - BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::UIntM, 8, 0)) == *make_shared<IntegerType>(8, IntegerType::Modifier::Unsigned)); - BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::UIntM, 16, 0)) == *make_shared<IntegerType>(16, IntegerType::Modifier::Unsigned)); - BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::UIntM, 24, 0)) == *make_shared<IntegerType>(24, IntegerType::Modifier::Unsigned)); - BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::UIntM, 32, 0)) == *make_shared<IntegerType>(32, IntegerType::Modifier::Unsigned)); - BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::UIntM, 40, 0)) == *make_shared<IntegerType>(40, IntegerType::Modifier::Unsigned)); - BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::UIntM, 48, 0)) == *make_shared<IntegerType>(48, IntegerType::Modifier::Unsigned)); - BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::UIntM, 56, 0)) == *make_shared<IntegerType>(56, IntegerType::Modifier::Unsigned)); - BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::UIntM, 64, 0)) == *make_shared<IntegerType>(64, IntegerType::Modifier::Unsigned)); - BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::UIntM, 72, 0)) == *make_shared<IntegerType>(72, IntegerType::Modifier::Unsigned)); - BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::UIntM, 80, 0)) == *make_shared<IntegerType>(80, IntegerType::Modifier::Unsigned)); - BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::UIntM, 88, 0)) == *make_shared<IntegerType>(88, IntegerType::Modifier::Unsigned)); - BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::UIntM, 96, 0)) == *make_shared<IntegerType>(96, IntegerType::Modifier::Unsigned)); - BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::UIntM, 104, 0)) == *make_shared<IntegerType>(104, IntegerType::Modifier::Unsigned)); - BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::UIntM, 112, 0)) == *make_shared<IntegerType>(112, IntegerType::Modifier::Unsigned)); - BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::UIntM, 120, 0)) == *make_shared<IntegerType>(120, IntegerType::Modifier::Unsigned)); - BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::UIntM, 128, 0)) == *make_shared<IntegerType>(128, IntegerType::Modifier::Unsigned)); - BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::UIntM, 136, 0)) == *make_shared<IntegerType>(136, IntegerType::Modifier::Unsigned)); - BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::UIntM, 144, 0)) == *make_shared<IntegerType>(144, IntegerType::Modifier::Unsigned)); - BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::UIntM, 152, 0)) == *make_shared<IntegerType>(152, IntegerType::Modifier::Unsigned)); - BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::UIntM, 160, 0)) == *make_shared<IntegerType>(160, IntegerType::Modifier::Unsigned)); - BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::UIntM, 168, 0)) == *make_shared<IntegerType>(168, IntegerType::Modifier::Unsigned)); - BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::UIntM, 176, 0)) == *make_shared<IntegerType>(176, IntegerType::Modifier::Unsigned)); - BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::UIntM, 184, 0)) == *make_shared<IntegerType>(184, IntegerType::Modifier::Unsigned)); - BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::UIntM, 192, 0)) == *make_shared<IntegerType>(192, IntegerType::Modifier::Unsigned)); - BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::UIntM, 200, 0)) == *make_shared<IntegerType>(200, IntegerType::Modifier::Unsigned)); - BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::UIntM, 208, 0)) == *make_shared<IntegerType>(208, IntegerType::Modifier::Unsigned)); - BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::UIntM, 216, 0)) == *make_shared<IntegerType>(216, IntegerType::Modifier::Unsigned)); - BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::UIntM, 224, 0)) == *make_shared<IntegerType>(224, IntegerType::Modifier::Unsigned)); - BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::UIntM, 232, 0)) == *make_shared<IntegerType>(232, IntegerType::Modifier::Unsigned)); - BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::UIntM, 240, 0)) == *make_shared<IntegerType>(240, IntegerType::Modifier::Unsigned)); - BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::UIntM, 248, 0)) == *make_shared<IntegerType>(248, IntegerType::Modifier::Unsigned)); - BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::UIntM, 256, 0)) == *make_shared<IntegerType>(256, IntegerType::Modifier::Unsigned)); - - BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::Byte, 0, 0)) == *make_shared<FixedBytesType>(1)); - BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::BytesM, 1, 0)) == *make_shared<FixedBytesType>(1)); - BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::BytesM, 2, 0)) == *make_shared<FixedBytesType>(2)); - BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::BytesM, 3, 0)) == *make_shared<FixedBytesType>(3)); - BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::BytesM, 4, 0)) == *make_shared<FixedBytesType>(4)); - BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::BytesM, 5, 0)) == *make_shared<FixedBytesType>(5)); - BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::BytesM, 6, 0)) == *make_shared<FixedBytesType>(6)); - BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::BytesM, 7, 0)) == *make_shared<FixedBytesType>(7)); - BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::BytesM, 8, 0)) == *make_shared<FixedBytesType>(8)); - BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::BytesM, 9, 0)) == *make_shared<FixedBytesType>(9)); - BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::BytesM, 10, 0)) == *make_shared<FixedBytesType>(10)); - BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::BytesM, 11, 0)) == *make_shared<FixedBytesType>(11)); - BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::BytesM, 12, 0)) == *make_shared<FixedBytesType>(12)); - BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::BytesM, 13, 0)) == *make_shared<FixedBytesType>(13)); - BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::BytesM, 14, 0)) == *make_shared<FixedBytesType>(14)); - BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::BytesM, 15, 0)) == *make_shared<FixedBytesType>(15)); - BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::BytesM, 16, 0)) == *make_shared<FixedBytesType>(16)); - BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::BytesM, 17, 0)) == *make_shared<FixedBytesType>(17)); - BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::BytesM, 18, 0)) == *make_shared<FixedBytesType>(18)); - BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::BytesM, 19, 0)) == *make_shared<FixedBytesType>(19)); - BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::BytesM, 20, 0)) == *make_shared<FixedBytesType>(20)); - BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::BytesM, 21, 0)) == *make_shared<FixedBytesType>(21)); - BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::BytesM, 22, 0)) == *make_shared<FixedBytesType>(22)); - BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::BytesM, 23, 0)) == *make_shared<FixedBytesType>(23)); - BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::BytesM, 24, 0)) == *make_shared<FixedBytesType>(24)); - BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::BytesM, 25, 0)) == *make_shared<FixedBytesType>(25)); - BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::BytesM, 26, 0)) == *make_shared<FixedBytesType>(26)); - BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::BytesM, 27, 0)) == *make_shared<FixedBytesType>(27)); - BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::BytesM, 28, 0)) == *make_shared<FixedBytesType>(28)); - BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::BytesM, 29, 0)) == *make_shared<FixedBytesType>(29)); - BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::BytesM, 30, 0)) == *make_shared<FixedBytesType>(30)); - BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::BytesM, 31, 0)) == *make_shared<FixedBytesType>(31)); - BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::BytesM, 32, 0)) == *make_shared<FixedBytesType>(32)); - - BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::Fixed, 0, 0)) == *make_shared<FixedPointType>(128, 18, FixedPointType::Modifier::Signed)); - BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::UFixed, 0, 0)) == *make_shared<FixedPointType>(128, 18, FixedPointType::Modifier::Unsigned)); -} - -BOOST_AUTO_TEST_CASE(test_byte_is_alias_of_byte1) -{ - char const* text = R"( - contract c { - bytes arr; - function f() public { byte a = arr[0];} - } - )"; - CHECK_SUCCESS(text); -} - -BOOST_AUTO_TEST_CASE(warns_assigning_decimal_to_bytesxx) -{ - char const* text = R"( - contract Foo { - bytes32 a = 7; - } - )"; - CHECK_WARNING(text, "Decimal literal assigned to bytesXX variable will be left-aligned."); -} - -BOOST_AUTO_TEST_CASE(does_not_warn_assigning_hex_number_to_bytesxx) -{ - char const* text = R"( - contract Foo { - bytes32 a = 0x1234; - } - )"; - CHECK_SUCCESS_NO_WARNINGS(text); -} - -BOOST_AUTO_TEST_CASE(explicit_conversion_from_decimal_to_bytesxx) -{ - char const* text = R"( - contract Foo { - bytes32 a = bytes32(7); - } - )"; - CHECK_SUCCESS_NO_WARNINGS(text); -} - -BOOST_AUTO_TEST_CASE(assigning_value_to_const_variable) -{ - char const* text = R"( - contract Foo { - function changeIt() public { x = 9; } - uint constant x = 56; - } - )"; - CHECK_ERROR(text, TypeError, "Cannot assign to a constant variable."); -} - -BOOST_AUTO_TEST_CASE(assigning_state_to_const_variable_0_4_x) -{ - char const* text = R"( - contract C { - address constant x = msg.sender; - } - )"; - CHECK_WARNING(text, "Initial value for constant variable has to be compile-time constant."); -} - -BOOST_AUTO_TEST_CASE(assigning_state_to_const_variable) -{ - char const* text = R"( - pragma experimental "v0.5.0"; - - contract C { - address constant x = msg.sender; - } - )"; - CHECK_ERROR(text, TypeError, "Initial value for constant variable has to be compile-time constant."); -} - -BOOST_AUTO_TEST_CASE(constant_string_literal_disallows_assignment) -{ - char const* text = R"( - contract Test { - string constant x = "abefghijklmnopqabcdefghijklmnopqabcdefghijklmnopqabca"; - function f() public { - x[0] = "f"; - } - } - )"; - - // Even if this is made possible in the future, we should not allow assignment - // to elements of constant arrays. - CHECK_ERROR(text, TypeError, "Index access for string is not possible."); -} - -BOOST_AUTO_TEST_CASE(assignment_to_const_var_involving_conversion) -{ - char const* text = R"( - contract C { - C constant x = C(0x123); - } - )"; - CHECK_SUCCESS(text); -} - -BOOST_AUTO_TEST_CASE(assignment_to_const_var_involving_expression) -{ - char const* text = R"( - contract C { - uint constant x = 0x123 + 0x456; - } - )"; - CHECK_SUCCESS(text); -} - -BOOST_AUTO_TEST_CASE(assignment_to_const_var_involving_keccak) -{ - char const* text = R"( - contract C { - bytes32 constant x = keccak256("abc"); - } - )"; - CHECK_SUCCESS(text); -} - -BOOST_AUTO_TEST_CASE(assignment_to_const_array_vars) -{ - char const* text = R"( - contract C { - uint[3] constant x = [uint(1), 2, 3]; - } - )"; - CHECK_ERROR(text, TypeError, "implemented"); -} - -BOOST_AUTO_TEST_CASE(assignment_to_const_string_bytes) -{ - char const* text = R"( - contract C { - bytes constant a = "\x00\x01\x02"; - bytes constant b = hex"000102"; - string constant c = "hello"; - } - )"; - CHECK_SUCCESS(text); -} - -BOOST_AUTO_TEST_CASE(constant_struct) -{ - char const* text = R"( - contract C { - struct S { uint x; uint[] y; } - S constant x = S(5, new uint[](4)); - } - )"; - CHECK_ERROR(text, TypeError, "implemented"); -} - -BOOST_AUTO_TEST_CASE(address_is_constant) -{ - char const* text = R"( - contract C { - address constant x = 0x1212121212121212121212121212121212121212; - } - )"; - CHECK_SUCCESS_NO_WARNINGS(text); -} - -BOOST_AUTO_TEST_CASE(uninitialized_const_variable) -{ - char const* text = R"( - contract Foo { - uint constant y; - } - )"; - CHECK_ERROR(text, TypeError, "Uninitialized \"constant\" variable."); -} - -BOOST_AUTO_TEST_CASE(overloaded_function_cannot_resolve) -{ - char const* sourceCode = R"( - contract test { - function f() public returns (uint) { return 1; } - function f(uint a) public returns (uint) { return a; } - function g() public returns (uint) { return f(3, 5); } - } - )"; - CHECK_ERROR(sourceCode, TypeError, "No matching declaration found after argument-dependent lookup."); -} - -BOOST_AUTO_TEST_CASE(ambiguous_overloaded_function) -{ - // literal 1 can be both converted to uint and uint8, so the call is ambiguous. - char const* sourceCode = R"( - contract test { - function f(uint8 a) public returns (uint) { return a; } - function f(uint a) public returns (uint) { return 2*a; } - function g() public returns (uint) { return f(1); } - } - )"; - CHECK_ERROR(sourceCode, TypeError, "No unique declaration found after argument-dependent lookup."); -} - -BOOST_AUTO_TEST_CASE(assignment_of_nonoverloaded_function) -{ - char const* sourceCode = R"( - contract test { - function f(uint a) public returns (uint) { return 2 * a; } - function g() public returns (uint) { var x = f; return x(7); } - } - )"; - CHECK_SUCCESS(sourceCode); -} - -BOOST_AUTO_TEST_CASE(assignment_of_overloaded_function) -{ - char const* sourceCode = R"( - contract test { - function f() public returns (uint) { return 1; } - function f(uint a) public returns (uint) { return 2 * a; } - function g() public returns (uint) { var x = f; return x(7); } - } - )"; - CHECK_ERROR(sourceCode, TypeError, "No matching declaration found after variable lookup."); -} - -BOOST_AUTO_TEST_CASE(external_types_clash) -{ - char const* sourceCode = R"( - contract base { - enum a { X } - function f(a) public { } - } - contract test is base { - function f(uint8 a) public { } - } - )"; - CHECK_ERROR(sourceCode, TypeError, "Function overload clash during conversion to external types for arguments."); -} - -BOOST_AUTO_TEST_CASE(override_changes_return_types) -{ - char const* sourceCode = R"( - contract base { - function f(uint a) public returns (uint) { } - } - contract test is base { - function f(uint a) public returns (uint8) { } - } - )"; - CHECK_ERROR(sourceCode, TypeError, "Overriding function return types differ"); -} - -BOOST_AUTO_TEST_CASE(equal_overload) -{ - char const* sourceCode = R"( - contract C { - function test(uint a) public returns (uint b) { } - function test(uint a) external {} - } - )"; - CHECK_ALLOW_MULTI(sourceCode, (vector<pair<Error::Type, string>>{ - {Error::Type::DeclarationError, "Function with same name and arguments defined twice."}, - {Error::Type::TypeError, "Overriding function visibility differs"} - })); -} - -BOOST_AUTO_TEST_CASE(uninitialized_var) -{ - char const* sourceCode = R"( - contract C { - function f() public returns (uint) { var x; return 2; } - } - )"; - CHECK_ERROR(sourceCode, TypeError, "Assignment necessary for type detection."); -} - BOOST_AUTO_TEST_CASE(string) { char const* sourceCode = R"( contract C { string s; - function f(string x) external { s = x; } - } - )"; - BOOST_CHECK_NO_THROW(parseAndAnalyse(sourceCode)); -} - -BOOST_AUTO_TEST_CASE(invalid_utf8_implicit) -{ - char const* sourceCode = R"( - contract C { - string s = "\xa0\x00"; - } - )"; - CHECK_ERROR(sourceCode, TypeError, "invalid UTF-8"); -} - -BOOST_AUTO_TEST_CASE(invalid_utf8_explicit) -{ - char const* sourceCode = R"( - contract C { - string s = string("\xa0\x00"); - } - )"; - CHECK_ERROR(sourceCode, TypeError, "Explicit type conversion not allowed"); -} - -BOOST_AUTO_TEST_CASE(large_utf8_codepoint) -{ - char const* sourceCode = R"( - contract C { - string s = "\xf0\x9f\xa6\x84"; - } - )"; - CHECK_SUCCESS(sourceCode); -} - -BOOST_AUTO_TEST_CASE(string_index) -{ - char const* sourceCode = R"( - contract C { - string s; - function f() public { var a = s[2]; } - } - )"; - CHECK_ERROR(sourceCode, TypeError, "Index access for string is not possible."); -} - -BOOST_AUTO_TEST_CASE(string_length) -{ - char const* sourceCode = R"( - contract C { - string s; - function f() public { var a = s.length; } - } - )"; - CHECK_ERROR(sourceCode, TypeError, "Member \"length\" not found or not visible after argument-dependent lookup in string storage ref"); -} - -BOOST_AUTO_TEST_CASE(negative_integers_to_signed_out_of_bound) -{ - char const* sourceCode = R"( - contract test { - int8 public i = -129; - } - )"; - CHECK_ERROR(sourceCode, TypeError, "Type int_const -129 is not implicitly convertible to expected type int8."); -} - -BOOST_AUTO_TEST_CASE(negative_integers_to_signed_min) -{ - char const* sourceCode = R"( - contract test { - int8 public i = -128; + function f(string calldata x) external { s = x; } } )"; BOOST_CHECK_NO_THROW(parseAndAnalyse(sourceCode)); } -BOOST_AUTO_TEST_CASE(positive_integers_to_signed_out_of_bound) -{ - char const* sourceCode = R"( - contract test { - int8 public j = 128; - } - )"; - CHECK_ERROR(sourceCode, TypeError, "Type int_const 128 is not implicitly convertible to expected type int8."); -} - -BOOST_AUTO_TEST_CASE(positive_integers_to_signed_out_of_bound_max) -{ - char const* sourceCode = R"( - contract test { - int8 public j = 127; - } - )"; - BOOST_CHECK_NO_THROW(parseAndAnalyse(sourceCode)); -} - -BOOST_AUTO_TEST_CASE(negative_integers_to_unsigned) -{ - char const* sourceCode = R"( - contract test { - uint8 public x = -1; - } - )"; - CHECK_ERROR(sourceCode, TypeError, "Type int_const -1 is not implicitly convertible to expected type uint8."); -} - -BOOST_AUTO_TEST_CASE(positive_integers_to_unsigned_out_of_bound) -{ - char const* sourceCode = R"( - contract test { - uint8 public x = 700; - } - )"; - CHECK_ERROR(sourceCode, TypeError, "Type int_const 700 is not implicitly convertible to expected type uint8."); -} - -BOOST_AUTO_TEST_CASE(integer_boolean_operators) -{ - char const* sourceCode1 = R"( - contract test { function() public { uint x = 1; uint y = 2; x || y; } } - )"; - CHECK_ERROR(sourceCode1, TypeError, "Operator || not compatible with types uint256 and uint256"); - char const* sourceCode2 = R"( - contract test { function() public { uint x = 1; uint y = 2; x && y; } } - )"; - CHECK_ERROR(sourceCode2, TypeError, "Operator && not compatible with types uint256 and uint256"); - char const* sourceCode3 = R"( - contract test { function() public { uint x = 1; !x; } } - )"; - CHECK_ERROR(sourceCode3, TypeError, "Unary operator ! cannot be applied to type uint256"); -} - -BOOST_AUTO_TEST_CASE(exp_signed_variable) -{ - char const* sourceCode1 = R"( - contract test { function() public { uint x = 3; int y = -4; x ** y; } } - )"; - CHECK_ERROR(sourceCode1, TypeError, "Operator ** not compatible with types uint256 and int256"); - char const* sourceCode2 = R"( - contract test { function() public { uint x = 3; int y = -4; y ** x; } } - )"; - CHECK_ERROR(sourceCode2, TypeError, "Operator ** not compatible with types int256 and uint256"); - char const* sourceCode3 = R"( - contract test { function() public { int x = -3; int y = -4; x ** y; } } - )"; - CHECK_ERROR(sourceCode3, TypeError, "Operator ** not compatible with types int256 and int256"); -} - -BOOST_AUTO_TEST_CASE(reference_compare_operators) -{ - char const* sourceCode1 = R"( - contract test { bytes a; bytes b; function() public { a == b; } } - )"; - CHECK_ERROR(sourceCode1, TypeError, "Operator == not compatible with types bytes storage ref and bytes storage ref"); - char const* sourceCode2 = R"( - contract test { struct s {uint a;} s x; s y; function() public { x == y; } } - )"; - CHECK_ERROR(sourceCode2, TypeError, "Operator == not compatible with types struct test.s storage ref and struct test.s storage ref"); -} - -BOOST_AUTO_TEST_CASE(overwrite_memory_location_external) -{ - char const* sourceCode = R"( - contract C { - function f(uint[] memory a) external {} - } - )"; - CHECK_ERROR(sourceCode, TypeError, "Location has to be calldata for external functions (remove the \"memory\" or \"storage\" keyword)."); -} - -BOOST_AUTO_TEST_CASE(overwrite_storage_location_external) -{ - char const* sourceCode = R"( - contract C { - function f(uint[] storage a) external {} - } - )"; - CHECK_ERROR(sourceCode, TypeError, "Location has to be calldata for external functions (remove the \"memory\" or \"storage\" keyword)."); -} - -BOOST_AUTO_TEST_CASE(storage_location_local_variables) -{ - char const* sourceCode = R"( - contract C { - function f() public { - uint[] storage x; - uint[] memory y; - uint[] memory z; - x;y;z; - } - } - )"; - BOOST_CHECK_NO_THROW(parseAndAnalyse(sourceCode)); -} - -BOOST_AUTO_TEST_CASE(no_mappings_in_memory_array) -{ - char const* sourceCode = R"( - contract C { - function f() public { - mapping(uint=>uint)[] memory x; - } - } - )"; - CHECK_ERROR(sourceCode, TypeError, "Type mapping(uint256 => uint256)[] memory is only valid in storage."); -} - -BOOST_AUTO_TEST_CASE(assignment_mem_to_local_storage_variable) -{ - char const* sourceCode = R"( - contract C { - uint[] data; - function f(uint[] x) public { - var dataRef = data; - dataRef = x; - } - } - )"; - CHECK_ERROR(sourceCode, TypeError, "Type uint256[] memory is not implicitly convertible to expected type uint256[] storage pointer."); -} - -BOOST_AUTO_TEST_CASE(storage_assign_to_different_local_variable) -{ - char const* sourceCode = R"( - contract C { - uint[] data; - uint8[] otherData; - function f() public { - uint8[] storage x = otherData; - uint[] storage y = data; - y = x; - // note that data = otherData works - } - } - )"; - CHECK_ERROR(sourceCode, TypeError, "Type uint8[] storage pointer is not implicitly convertible to expected type uint256[] storage pointer."); -} - -BOOST_AUTO_TEST_CASE(uninitialized_mapping_variable) -{ - char const* sourceCode = R"( - contract C { - function f() public { - mapping(uint => uint) x; - x; - } - } - )"; - CHECK_ERROR(sourceCode, TypeError, "Uninitialized mapping. Mappings cannot be created dynamically, you have to assign them from a state variable"); -} - -BOOST_AUTO_TEST_CASE(uninitialized_mapping_array_variable) -{ - char const* sourceCode = R"( - contract C { - function f() pure public { - mapping(uint => uint)[] storage x; - x; - } - } - )"; - CHECK_WARNING(sourceCode, "Uninitialized storage pointer"); -} - -BOOST_AUTO_TEST_CASE(uninitialized_mapping_array_variable_050) -{ - char const* sourceCode = R"( - pragma experimental "v0.5.0"; - contract C { - function f() pure public { - mapping(uint => uint)[] storage x; - x; - } - } - )"; - CHECK_ERROR(sourceCode, DeclarationError, "Uninitialized storage pointer"); -} - -BOOST_AUTO_TEST_CASE(no_delete_on_storage_pointers) -{ - char const* sourceCode = R"( - contract C { - uint[] data; - function f() public { - var x = data; - delete x; - } - } - )"; - CHECK_ERROR(sourceCode, TypeError, "Unary operator delete cannot be applied to type uint256[] storage pointer"); -} - -BOOST_AUTO_TEST_CASE(assignment_mem_storage_variable_directly) -{ - char const* sourceCode = R"( - contract C { - uint[] data; - function f(uint[] x) public { - data = x; - } - } - )"; - BOOST_CHECK_NO_THROW(parseAndAnalyse(sourceCode)); -} - -BOOST_AUTO_TEST_CASE(function_argument_mem_to_storage) -{ - char const* sourceCode = R"( - contract C { - function f(uint[] storage x) private { - } - function g(uint[] x) public { - f(x); - } - } - )"; - CHECK_ERROR(sourceCode, TypeError, "Invalid type for argument in function call. Invalid implicit conversion from uint256[] memory to uint256[] storage pointer requested."); -} - -BOOST_AUTO_TEST_CASE(function_argument_storage_to_mem) -{ - char const* sourceCode = R"( - contract C { - function f(uint[] storage x) private { - g(x); - } - function g(uint[] x) public { - } - } - )"; - BOOST_CHECK_NO_THROW(parseAndAnalyse(sourceCode)); -} - -BOOST_AUTO_TEST_CASE(mem_array_assignment_changes_base_type) -{ - // Such an assignment is possible in storage, but not in memory - // (because it would incur an otherwise unnecessary copy). - // This requirement might be lifted, though. - char const* sourceCode = R"( - contract C { - function f(uint8[] memory x) private { - uint[] memory y = x; - } - } - )"; - CHECK_ERROR(sourceCode, TypeError, "Type uint8[] memory is not implicitly convertible to expected type uint256[] memory."); -} - BOOST_AUTO_TEST_CASE(dynamic_return_types_not_possible) { char const* sourceCode = R"( contract C { - function f(uint) public returns (string); + function f(uint) public returns (string memory); function g() public { - var x = this.f(2); + string memory x = this.f(2); // we can assign to x but it is not usable. bytes(x).length; } } )"; if (dev::test::Options::get().evmVersion() == EVMVersion::homestead()) - CHECK_ERROR(sourceCode, TypeError, "Explicit type conversion not allowed from \"inaccessible dynamic type\" to \"bytes storage pointer\"."); + CHECK_ERROR(sourceCode, TypeError, "Type inaccessible dynamic type is not implicitly convertible to expected type string memory."); else - CHECK_WARNING(sourceCode, "Use of the \"var\" keyword is deprecated"); -} - -BOOST_AUTO_TEST_CASE(memory_arrays_not_resizeable) -{ - char const* sourceCode = R"( - contract C { - function f() public { - uint[] memory x; - x.length = 2; - } - } - )"; - CHECK_ERROR(sourceCode, TypeError, "Expression has to be an lvalue."); -} - -BOOST_AUTO_TEST_CASE(struct_constructor) -{ - char const* sourceCode = R"( - contract C { - struct S { uint a; bool x; } - function f() public { - S memory s = S(1, true); - } - } - )"; - BOOST_CHECK_NO_THROW(parseAndAnalyse(sourceCode)); -} - -BOOST_AUTO_TEST_CASE(struct_constructor_nested) -{ - char const* sourceCode = R"( - contract C { - struct X { uint x1; uint x2; } - struct S { uint s1; uint[3] s2; X s3; } - function f() public { - uint[3] memory s2; - S memory s = S(1, s2, X(4, 5)); - } - } - )"; - BOOST_CHECK_NO_THROW(parseAndAnalyse(sourceCode)); -} - -BOOST_AUTO_TEST_CASE(struct_named_constructor) -{ - char const* sourceCode = R"( - contract C { - struct S { uint a; bool x; } - function f() public { - S memory s = S({a: 1, x: true}); - } - } - )"; - BOOST_CHECK_NO_THROW(parseAndAnalyse(sourceCode)); -} - -BOOST_AUTO_TEST_CASE(literal_strings) -{ - char const* text = R"( - contract Foo { - function f() public { - string memory long = "01234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890"; - string memory short = "123"; - long; short; - } - } - )"; - CHECK_SUCCESS(text); -} - -BOOST_AUTO_TEST_CASE(string_bytes_conversion) -{ - char const* text = R"( - contract Test { - string s; - bytes b; - function h(string _s) external { bytes(_s).length; } - function i(string _s) internal { bytes(_s).length; } - function j() internal { bytes(s).length; } - function k(bytes _b) external { string(_b); } - function l(bytes _b) internal { string(_b); } - function m() internal { string(b); } - } - )"; - CHECK_SUCCESS(text); -} - -BOOST_AUTO_TEST_CASE(inheriting_from_library) -{ - char const* text = R"( - library Lib {} - contract Test is Lib {} - )"; - CHECK_ERROR(text, TypeError, "Libraries cannot be inherited from."); -} - -BOOST_AUTO_TEST_CASE(inheriting_library) -{ - char const* text = R"( - contract Test {} - library Lib is Test {} - )"; - CHECK_ERROR(text, TypeError, "Library is not allowed to inherit."); -} - -BOOST_AUTO_TEST_CASE(library_having_variables) -{ - char const* text = R"( - library Lib { uint x; } - )"; - CHECK_ERROR(text, TypeError, "Library cannot have non-constant state variables"); -} - -BOOST_AUTO_TEST_CASE(valid_library) -{ - char const* text = R"( - library Lib { uint constant x = 9; } - )"; - CHECK_SUCCESS(text); -} - -BOOST_AUTO_TEST_CASE(call_to_library_function) -{ - char const* text = R"( - library Lib { - function min(uint, uint) public returns (uint); - } - contract Test { - function f() public { - uint t = Lib.min(12, 7); - } - } - )"; - CHECK_SUCCESS(text); -} - -BOOST_AUTO_TEST_CASE(creating_contract_within_the_contract) -{ - char const* sourceCode = R"( - contract Test { - function f() public { var x = new Test(); } - } - )"; - CHECK_ERROR(sourceCode, TypeError, "Circular reference for contract creation (cannot create instance of derived or same contract)."); -} - -BOOST_AUTO_TEST_CASE(array_out_of_bound_access) -{ - char const* text = R"( - contract c { - uint[2] dataArray; - function set5th() public returns (bool) { - dataArray[5] = 2; - return true; - } - } - )"; - CHECK_ERROR(text, TypeError, "Out of bounds array access."); -} - -BOOST_AUTO_TEST_CASE(literal_string_to_storage_pointer) -{ - char const* text = R"( - contract C { - function f() public { string x = "abc"; } - } - )"; - CHECK_ERROR(text, TypeError, "Type literal_string \"abc\" is not implicitly convertible to expected type string storage pointer."); -} - -BOOST_AUTO_TEST_CASE(non_initialized_references) -{ - char const* text = R"( - contract c - { - struct s { - uint a; - } - function f() public { - s storage x; - x.a = 2; - } - } - )"; - - CHECK_WARNING(text, "Uninitialized storage pointer"); -} - -BOOST_AUTO_TEST_CASE(non_initialized_references_050) -{ - char const* text = R"( - pragma experimental "v0.5.0"; - contract c - { - struct s { - uint a; - } - function f() public { - s storage x; - } - } - )"; - - CHECK_ERROR(text, DeclarationError, "Uninitialized storage pointer"); -} - -BOOST_AUTO_TEST_CASE(keccak256_with_large_integer_constant) -{ - char const* text = R"( - contract c - { - function f() public { keccak256(2**500); } - } - )"; - CHECK_ERROR(text, TypeError, "Invalid rational number (too large or division by zero)."); -} - -BOOST_AUTO_TEST_CASE(cyclic_binary_dependency) -{ - char const* text = R"( - contract A { function f() public { new B(); } } - contract B { function f() public { new C(); } } - contract C { function f() public { new A(); } } - )"; - CHECK_ERROR(text, TypeError, "Circular reference for contract creation (cannot create instance of derived or same contract)."); -} - -BOOST_AUTO_TEST_CASE(cyclic_binary_dependency_via_inheritance) -{ - char const* text = R"( - contract A is B { } - contract B { function f() public { new C(); } } - contract C { function f() public { new A(); } } - )"; - CHECK_ERROR(text, TypeError, "Definition of base has to precede definition of derived contract"); -} - -BOOST_AUTO_TEST_CASE(multi_variable_declaration_fail) -{ - char const* text = R"( - contract C { function f() public { var (x,y); x = 1; y = 1;} } - )"; - CHECK_ERROR(text, TypeError, "Assignment necessary for type detection."); -} - -BOOST_AUTO_TEST_CASE(multi_variable_declaration_wildcards_fine) -{ - char const* text = R"( - contract C { - function three() public returns (uint, uint, uint); - function two() public returns (uint, uint); - function none(); - function f() public { - var (a,) = three(); - var (b,c,) = two(); - var (,d) = three(); - var (,e,g) = two(); - var (,,) = three(); - var () = none(); - a;b;c;d;e;g; - } - } - )"; - CHECK_SUCCESS(text); -} - -BOOST_AUTO_TEST_CASE(multi_variable_declaration_wildcards_fail_1) -{ - char const* text = R"( - contract C { - function one() public returns (uint); - function f() public { var (a, b, ) = one(); } - } - )"; - CHECK_ERROR(text, TypeError, "Not enough components (1) in value to assign all variables (2)."); -} -BOOST_AUTO_TEST_CASE(multi_variable_declaration_wildcards_fail_2) -{ - char const* text = R"( - contract C { - function one() public returns (uint); - function f() public { var (a, , ) = one(); } - } - )"; - CHECK_ERROR(text, TypeError, "Not enough components (1) in value to assign all variables (2)."); -} - -BOOST_AUTO_TEST_CASE(multi_variable_declaration_wildcards_fail_3) -{ - char const* text = R"( - contract C { - function one() public returns (uint); - function f() public { var (, , a) = one(); } - } - )"; - CHECK_ERROR(text, TypeError, "Not enough components (1) in value to assign all variables (2)."); -} - -BOOST_AUTO_TEST_CASE(multi_variable_declaration_wildcards_fail_4) -{ - char const* text = R"( - contract C { - function one() public returns (uint); - function f() public { var (, a, b) = one(); } - } - )"; - CHECK_ERROR(text, TypeError, "Not enough components (1) in value to assign all variables (2)."); -} - -BOOST_AUTO_TEST_CASE(tuples) -{ - char const* text = R"( - contract C { - function f() public { - uint a = (1); - var (b,) = (uint8(1),); - var (c,d) = (uint32(1), 2 + a); - var (e,) = (uint64(1), 2, b); - a;b;c;d;e; - } - } - )"; - CHECK_SUCCESS(text); -} - -BOOST_AUTO_TEST_CASE(tuples_empty_components) -{ - char const* text = R"( - contract C { - function f() public { - (1,,2); - } - } - )"; - CHECK_ERROR(text, TypeError, "Tuple component cannot be empty."); -} - -BOOST_AUTO_TEST_CASE(multi_variable_declaration_wildcards_fail_5) -{ - char const* text = R"( - contract C { - function one() public returns (uint); - function f() public { var (,) = one(); } - } - )"; - CHECK_ERROR(text, TypeError, "Wildcard both at beginning and end of variable declaration list is only allowed if the number of components is equal."); -} - -BOOST_AUTO_TEST_CASE(multi_variable_declaration_wildcards_fail_6) -{ - char const* text = R"( - contract C { - function two() public returns (uint, uint); - function f() public { var (a, b, c) = two(); } - } - )"; - CHECK_ERROR(text, TypeError, "Not enough components (2) in value to assign all variables (3)"); -} - -BOOST_AUTO_TEST_CASE(tuple_assignment_from_void_function) -{ - char const* text = R"( - contract C { - function f() public { } - function g() public { - var (x,) = (f(), f()); - } - } - )"; - CHECK_ERROR(text, TypeError, "Cannot declare variable with void (empty tuple) type."); -} - -BOOST_AUTO_TEST_CASE(tuple_compound_assignment) -{ - char const* text = R"( - contract C { - function f() public returns (uint a, uint b) { - (a, b) += (1, 1); - } - } - )"; - CHECK_ERROR(text, TypeError, "Compound assignment is not allowed for tuple types."); -} - -BOOST_AUTO_TEST_CASE(member_access_parser_ambiguity) -{ - char const* text = R"( - contract C { - struct R { uint[10][10] y; } - struct S { uint a; uint b; uint[20][20][20] c; R d; } - S data; - function f() public { - C.S x = data; - C.S memory y; - C.S[10] memory z; - C.S[10]; - y.a = 2; - x.c[1][2][3] = 9; - x.d.y[2][2] = 3; - } - } - )"; - CHECK_SUCCESS(text); -} - -BOOST_AUTO_TEST_CASE(using_for_library) -{ - char const* text = R"( - library D { } - contract C { - using D for uint; - } - )"; - CHECK_SUCCESS(text); -} - -BOOST_AUTO_TEST_CASE(using_for_not_library) -{ - char const* text = R"( - contract D { } - contract C { - using D for uint; - } - )"; - CHECK_ERROR(text, TypeError, "Library name expected."); -} - -BOOST_AUTO_TEST_CASE(using_for_function_exists) -{ - char const* text = R"( - library D { function double(uint self) public returns (uint) { return 2*self; } } - contract C { - using D for uint; - function f(uint a) public { - a.double; - } - } - )"; - CHECK_SUCCESS(text); -} - -BOOST_AUTO_TEST_CASE(using_for_function_on_int) -{ - char const* text = R"( - library D { function double(uint self) public returns (uint) { return 2*self; } } - contract C { - using D for uint; - function f(uint a) public returns (uint) { - return a.double(); - } - } - )"; - CHECK_SUCCESS(text); -} - -BOOST_AUTO_TEST_CASE(using_for_function_on_struct) -{ - char const* text = R"( - library D { struct s { uint a; } function mul(s storage self, uint x) public returns (uint) { return self.a *= x; } } - contract C { - using D for D.s; - D.s x; - function f(uint a) public returns (uint) { - return x.mul(a); - } - } - )"; - CHECK_SUCCESS(text); -} - -BOOST_AUTO_TEST_CASE(using_for_overload) -{ - char const* text = R"( - library D { - struct s { uint a; } - function mul(s storage self, uint x) public returns (uint) { return self.a *= x; } - function mul(s storage, bytes32) public returns (bytes32) { } - } - contract C { - using D for D.s; - D.s x; - function f(uint a) public returns (uint) { - return x.mul(a); - } - } - )"; - CHECK_SUCCESS(text); -} - -BOOST_AUTO_TEST_CASE(using_for_by_name) -{ - char const* text = R"( - library D { struct s { uint a; } function mul(s storage self, uint x) public returns (uint) { return self.a *= x; } } - contract C { - using D for D.s; - D.s x; - function f(uint a) public returns (uint) { - return x.mul({x: a}); - } - } - )"; - CHECK_SUCCESS(text); -} - -BOOST_AUTO_TEST_CASE(using_for_mismatch) -{ - char const* text = R"( - library D { function double(bytes32 self) public returns (uint) { return 2; } } - contract C { - using D for uint; - function f(uint a) public returns (uint) { - return a.double(); - } - } - )"; - CHECK_ERROR(text, TypeError, "Member \"double\" not found or not visible after argument-dependent lookup in uint256"); -} - -BOOST_AUTO_TEST_CASE(using_for_not_used) -{ - // This is an error because the function is only bound to uint. - // Had it been bound to *, it would have worked. - char const* text = R"( - library D { function double(uint self) public returns (uint) { return 2; } } - contract C { - using D for uint; - function f(uint16 a) public returns (uint) { - return a.double(); - } - } - )"; - CHECK_ERROR(text, TypeError, "Member \"double\" not found or not visible after argument-dependent lookup in uint16"); -} - -BOOST_AUTO_TEST_CASE(library_memory_struct) -{ - char const* text = R"( - pragma experimental ABIEncoderV2; - library c { - struct S { uint x; } - function f() public returns (S ) {} - } - )"; - CHECK_SUCCESS(text); -} - -BOOST_AUTO_TEST_CASE(using_for_arbitrary_mismatch) -{ - // Bound to a, but self type does not match. - char const* text = R"( - library D { function double(bytes32 self) public returns (uint) { return 2; } } - contract C { - using D for *; - function f(uint a) public returns (uint) { - return a.double(); - } - } - )"; - CHECK_ERROR(text, TypeError, "Member \"double\" not found or not visible after argument-dependent lookup in uint256"); -} - -BOOST_AUTO_TEST_CASE(bound_function_in_var) -{ - char const* text = R"( - library D { struct s { uint a; } function mul(s storage self, uint x) public returns (uint) { return self.a *= x; } } - contract C { - using D for D.s; - D.s x; - function f(uint a) public returns (uint) { - var g = x.mul; - return g({x: a}); - } - } - )"; - CHECK_SUCCESS(text); -} - -BOOST_AUTO_TEST_CASE(create_memory_arrays) -{ - char const* text = R"( - library L { - struct R { uint[10][10] y; } - struct S { uint a; uint b; uint[20][20][20] c; R d; } - } - contract C { - function f(uint size) public { - L.S[][] memory x = new L.S[][](10); - var y = new uint[](20); - var z = new bytes(size); - x;y;z; - } - } - )"; - CHECK_SUCCESS(text); -} - -BOOST_AUTO_TEST_CASE(mapping_in_memory_array) -{ - char const* text = R"( - contract C { - function f(uint size) public { - var x = new mapping(uint => uint)[](4); - } - } - )"; - CHECK_ERROR(text, TypeError, "Type cannot live outside storage."); -} - -BOOST_AUTO_TEST_CASE(new_for_non_array) -{ - char const* text = R"( - contract C { - function f(uint size) public { - var x = new uint(7); - } - } - )"; - CHECK_ERROR(text, TypeError, "Contract or array type expected."); -} - -BOOST_AUTO_TEST_CASE(invalid_args_creating_memory_array) -{ - char const* text = R"( - contract C { - function f(uint size) public { - var x = new uint[](); - } - } - )"; - CHECK_ERROR(text, TypeError, "Wrong argument count for function call: 0 arguments given but expected 1."); -} - -BOOST_AUTO_TEST_CASE(invalid_args_creating_struct) -{ - char const* text = R"( - contract C { - struct S { uint a; uint b; } - - function f() public { - var s = S({a: 1}); - } - } - )"; - CHECK_ERROR(text, TypeError, "Wrong argument count for struct constructor: 1 arguments given but expected 2."); -} - -BOOST_AUTO_TEST_CASE(function_overload_array_type) -{ - char const* text = R"( - contract M { - function f(uint[]); - function f(int[]); - } - )"; - CHECK_SUCCESS(text); -} - -BOOST_AUTO_TEST_CASE(inline_array_declaration_and_passing_implicit_conversion) -{ - char const* text = R"( - contract C { - function f() public returns (uint) { - uint8 x = 7; - uint16 y = 8; - uint32 z = 9; - uint32[3] memory ending = [x, y, z]; - return (ending[1]); - } - } - )"; - CHECK_SUCCESS(text); -} - -BOOST_AUTO_TEST_CASE(inline_array_declaration_and_passing_implicit_conversion_strings) -{ - char const* text = R"( - contract C { - function f() public returns (string) { - string memory x = "Hello"; - string memory y = "World"; - string[2] memory z = [x, y]; - return (z[0]); - } - } - )"; - CHECK_SUCCESS(text); -} - -BOOST_AUTO_TEST_CASE(inline_array_declaration_const_int_conversion) -{ - char const* text = R"( - contract C { - function f() public returns (uint) { - uint8[4] memory z = [1,2,3,5]; - return (z[0]); - } - } - )"; - CHECK_SUCCESS(text); -} - -BOOST_AUTO_TEST_CASE(inline_array_declaration_const_string_conversion) -{ - char const* text = R"( - contract C { - function f() public returns (string) { - string[2] memory z = ["Hello", "World"]; - return (z[0]); - } - } - )"; - CHECK_SUCCESS(text); -} - -BOOST_AUTO_TEST_CASE(inline_array_declaration_no_type) -{ - char const* text = R"( - contract C { - function f() public returns (uint) { - return ([4,5,6][1]); - } - } - )"; - CHECK_SUCCESS(text); -} - -BOOST_AUTO_TEST_CASE(inline_array_declaration_no_type_strings) -{ - char const* text = R"( - contract C { - function f() public returns (string) { - return (["foo", "man", "choo"][1]); - } - } - )"; - CHECK_SUCCESS(text); -} - -BOOST_AUTO_TEST_CASE(inline_struct_declaration_arrays) -{ - char const* text = R"( - contract C { - struct S { - uint a; - string b; - } - function f() { - S[2] memory x = [S({a: 1, b: "fish"}), S({a: 2, b: "fish"})]; - } - } - )"; - CHECK_SUCCESS(text); -} - -BOOST_AUTO_TEST_CASE(invalid_types_in_inline_array) -{ - char const* text = R"( - contract C { - function f() public { - uint[3] x = [45, 'foo', true]; - } - } - )"; - CHECK_ERROR(text, TypeError, "Unable to deduce common type for array elements."); -} - -BOOST_AUTO_TEST_CASE(dynamic_inline_array) -{ - char const* text = R"( - contract C { - function f() public { - uint8[4][4] memory dyn = [[1, 2, 3, 4], [2, 3, 4, 5], [3, 4, 5, 6], [4, 5, 6, 7]]; - } - } - )"; - CHECK_SUCCESS(text); -} - -BOOST_AUTO_TEST_CASE(lvalues_as_inline_array) -{ - char const* text = R"( - contract C { - function f() public { - [1, 2, 3]++; - [1, 2, 3] = [4, 5, 6]; - } - } - )"; - CHECK_ERROR(text, TypeError, "Inline array type cannot be declared as LValue."); -} - -BOOST_AUTO_TEST_CASE(break_not_in_loop) -{ - char const* text = R"( - contract C { - function f() public { - if (true) - break; - } - } - )"; - CHECK_ERROR(text, SyntaxError, "\"break\" has to be in a \"for\" or \"while\" loop."); -} - -BOOST_AUTO_TEST_CASE(continue_not_in_loop) -{ - char const* text = R"( - contract C { - function f() public { - if (true) - continue; - } - } - )"; - CHECK_ERROR(text, SyntaxError, "\"continue\" has to be in a \"for\" or \"while\" loop."); -} - -BOOST_AUTO_TEST_CASE(continue_not_in_loop_2) -{ - char const* text = R"( - contract C { - function f() public { - while (true) - { - } - continue; - } - } - )"; - CHECK_ERROR(text, SyntaxError, "\"continue\" has to be in a \"for\" or \"while\" loop."); -} - -BOOST_AUTO_TEST_CASE(invalid_different_types_for_conditional_expression) -{ - char const* text = R"( - contract C { - function f() public { - true ? true : 2; - } - } - )"; - CHECK_ERROR(text, TypeError, "True expression's type bool doesn't match false expression's type uint8."); -} - -BOOST_AUTO_TEST_CASE(left_value_in_conditional_expression_not_supported_yet) -{ - char const* text = R"( - contract C { - function f() public { - uint x; - uint y; - (true ? x : y) = 1; - } - } - )"; - CHECK_ERROR_ALLOW_MULTI(text, TypeError, (std::vector<std::string>{ - "Conditional expression as left value is not supported yet.", - "Expression has to be an lvalue" - })); -} - -BOOST_AUTO_TEST_CASE(conditional_expression_with_different_struct) -{ - char const* text = R"( - contract C { - struct s1 { - uint x; - } - struct s2 { - uint x; - } - function f() public { - s1 memory x; - s2 memory y; - true ? x : y; - } - } - )"; - CHECK_ERROR(text, TypeError, "True expression's type struct C.s1 memory doesn't match false expression's type struct C.s2 memory."); -} - -BOOST_AUTO_TEST_CASE(conditional_expression_with_different_function_type) -{ - char const* text = R"( - contract C { - function x(bool) public {} - function y() public {} - - function f() public { - true ? x : y; - } - } - )"; - CHECK_ERROR(text, TypeError, "True expression's type function (bool) doesn't match false expression's type function ()."); -} - -BOOST_AUTO_TEST_CASE(conditional_expression_with_different_enum) -{ - char const* text = R"( - contract C { - enum small { A, B, C, D } - enum big { A, B, C, D } - - function f() public { - small x; - big y; - - true ? x : y; - } - } - )"; - CHECK_ERROR(text, TypeError, "True expression's type enum C.small doesn't match false expression's type enum C.big."); -} - -BOOST_AUTO_TEST_CASE(conditional_expression_with_different_mapping) -{ - char const* text = R"( - contract C { - mapping(uint8 => uint8) table1; - mapping(uint32 => uint8) table2; - - function f() public { - true ? table1 : table2; - } - } - )"; - CHECK_ERROR(text, TypeError, "True expression's type mapping(uint8 => uint8) doesn't match false expression's type mapping(uint32 => uint8)."); -} - -BOOST_AUTO_TEST_CASE(conditional_with_all_types) -{ - char const* text = R"( - contract C { - struct s1 { - uint x; - } - s1 struct_x; - s1 struct_y; - - function fun_x() public {} - function fun_y() public {} - - enum small { A, B, C, D } - - mapping(uint8 => uint8) table1; - mapping(uint8 => uint8) table2; - - function f() public { - // integers - uint x; - uint y; - uint g = true ? x : y; - g += 1; // Avoid unused var warning - - // integer constants - uint h = true ? 1 : 3; - h += 1; // Avoid unused var warning - - // string literal - var i = true ? "hello" : "world"; - i = "used"; //Avoid unused var warning - } - function f2() public { - // bool - bool j = true ? true : false; - j = j && true; // Avoid unused var warning - - // real is not there yet. - - // array - byte[2] memory a; - byte[2] memory b; - var k = true ? a : b; - k[0] = byte(0); //Avoid unused var warning - - bytes memory e; - bytes memory f; - var l = true ? e : f; - l[0] = byte(0); // Avoid unused var warning - - // fixed bytes - bytes2 c; - bytes2 d; - var m = true ? c : d; - m &= m; - - } - function f3() public { - // contract doesn't fit in here - - // struct - struct_x = true ? struct_x : struct_y; - - // function - var r = true ? fun_x : fun_y; - r(); // Avoid unused var warning - // enum - small enum_x; - small enum_y; - enum_x = true ? enum_x : enum_y; - - // tuple - var (n, o) = true ? (1, 2) : (3, 4); - (n, o) = (o, n); // Avoid unused var warning - // mapping - var p = true ? table1 : table2; - p[0] = 0; // Avoid unused var warning - // typetype - var q = true ? uint32(1) : uint32(2); - q += 1; // Avoid unused var warning - // modifier doesn't fit in here - - // magic doesn't fit in here - - // module doesn't fit in here - } - } - )"; - CHECK_SUCCESS(text); -} - -BOOST_AUTO_TEST_CASE(index_access_for_bytes) -{ - char const* text = R"( - contract C { - bytes20 x; - function f(bytes16 b) public { - b[uint(x[2])]; - } - } - )"; - CHECK_SUCCESS(text); -} - -BOOST_AUTO_TEST_CASE(uint7_and_uintM_as_identifier) -{ - char const* text = R"( - contract test { - string uintM = "Hello 4 you"; - function f() public { - uint8 uint7 = 3; - uint7 = 5; - string memory intM; - uint bytesM = 21; - intM; bytesM; - } - } - )"; - CHECK_SUCCESS(text); -} - -BOOST_AUTO_TEST_CASE(varM_disqualified_as_keyword) -{ - char const* text = R"( - contract test { - function f() public { - uintM something = 3; - intM should = 4; - bytesM fail = "now"; - } - } - )"; - CHECK_ERROR_ALLOW_MULTI(text, DeclarationError, (std::vector<std::string>{ - "Identifier not found or not unique.", - "Identifier not found or not unique.", - "Identifier not found or not unique." - })); -} - -BOOST_AUTO_TEST_CASE(modifier_is_not_a_valid_typename) -{ - char const* text = R"( - contract test { - modifier mod() { _; } - - function f() public { - mod g; - } - } - )"; - CHECK_ERROR(text, TypeError, "Name has to refer to a struct, enum or contract."); -} - -BOOST_AUTO_TEST_CASE(modifier_is_not_a_valid_typename_is_not_fatal) -{ - char const* text = R"( - contract test { - modifier mod() { _; } - - function f() public { - mod g; - g = f; - } - } - )"; - CHECK_ERROR_ALLOW_MULTI(text, TypeError, (std::vector<std::string>{"Name has to refer to a struct, enum or contract."})); -} - -BOOST_AUTO_TEST_CASE(function_is_not_a_valid_typename) -{ - char const* text = R"( - contract test { - function foo() public { - } - - function f() public { - foo g; - } - } - )"; - CHECK_ERROR(text, TypeError, "Name has to refer to a struct, enum or contract."); -} - -BOOST_AUTO_TEST_CASE(long_uint_variable_fails) -{ - char const* text = R"( - contract test { - function f() public { - uint99999999999999999999999999 something = 3; - } - } - )"; - CHECK_ERROR(text, DeclarationError, "Identifier not found or not unique."); -} - -BOOST_AUTO_TEST_CASE(bytes10abc_is_identifier) -{ - char const* text = R"( - contract test { - function f() public { - bytes32 bytes10abc = "abc"; - } - } - )"; - CHECK_SUCCESS(text); -} - -BOOST_AUTO_TEST_CASE(int10abc_is_identifier) -{ - char const* text = R"( - contract test { - function f() public { - uint uint10abc = 3; - int int10abc = 4; - uint10abc; int10abc; - } - } - )"; - CHECK_SUCCESS(text); -} - -BOOST_AUTO_TEST_CASE(library_functions_do_not_have_value) -{ - char const* text = R"( - library L { function l() public {} } - contract test { - function f() public { - L.l.value; - } - } - )"; - CHECK_ERROR(text, TypeError, "Member \"value\" not found or not visible after argument-dependent lookup in function ()"); -} - -BOOST_AUTO_TEST_CASE(invalid_fixed_types_0x7_mxn) -{ - char const* text = R"( - contract test { - fixed0x7 a = .3; - } - )"; - CHECK_ERROR(text, DeclarationError, "Identifier not found"); -} - -BOOST_AUTO_TEST_CASE(invalid_fixed_types_long_invalid_identifier) -{ - char const* text = R"( - contract test { - fixed99999999999999999999999999999999999999x7 b = 9.5; - } - )"; - CHECK_ERROR(text, DeclarationError, "Identifier not found"); -} - -BOOST_AUTO_TEST_CASE(invalid_fixed_types_7x8_mxn) -{ - char const* text = R"( - contract test { - fixed7x8 c = 3.12345678; - } - )"; - CHECK_ERROR(text, DeclarationError, "Identifier not found"); -} - -BOOST_AUTO_TEST_CASE(library_instances_cannot_be_used) -{ - char const* text = R"( - library L { function l() public {} } - contract test { - function f() public { - L x; - x.l(); - } - } - )"; - CHECK_ERROR(text, TypeError, "Member \"l\" not found or not visible after argument-dependent lookup in library L"); -} - -BOOST_AUTO_TEST_CASE(invalid_fixed_type_long) -{ - char const* text = R"( - contract test { - function f() public { - fixed8x888888888888888888888888888888888888888888888888888 b; - } - } - )"; - CHECK_ERROR(text, DeclarationError, "Identifier not found"); -} - -BOOST_AUTO_TEST_CASE(fixed_type_int_conversion) -{ - char const* text = R"( - contract test { - function f() public { - uint64 a = 3; - int64 b = 4; - fixed c = b; - ufixed d = a; - c; d; - } - } - )"; - CHECK_SUCCESS(text); -} - -BOOST_AUTO_TEST_CASE(fixed_type_rational_int_conversion) -{ - char const* text = R"( - contract test { - function f() public { - fixed c = 3; - ufixed d = 4; - c; d; - } - } - )"; - CHECK_SUCCESS(text); -} - -BOOST_AUTO_TEST_CASE(fixed_type_rational_fraction_conversion) -{ - char const* text = R"( - contract test { - function f() public { - fixed a = 4.5; - ufixed d = 2.5; - a; d; - } - } - )"; - CHECK_SUCCESS(text); -} - -BOOST_AUTO_TEST_CASE(invalid_int_implicit_conversion_from_fixed) -{ - char const* text = R"( - contract test { - function f() public { - fixed a = 4.5; - int b = a; - a; b; - } - } - )"; - CHECK_ERROR(text, TypeError, "Type fixed128x18 is not implicitly convertible to expected type int256"); -} - -BOOST_AUTO_TEST_CASE(rational_unary_operation) -{ - char const* text = R"( - contract test { - function f() pure public { - ufixed16x2 a = 3.25; - fixed16x2 b = -3.25; - a; b; - } - } - )"; - CHECK_SUCCESS_NO_WARNINGS(text); - - // Test deprecation warning under < 0.5.0 - text = R"( - contract test { - function f() pure public { - ufixed16x2 a = +3.25; - fixed16x2 b = -3.25; - a; b; - } - } - )"; - CHECK_WARNING(text, "Use of unary + is deprecated"); - text = R"( - contract test { - function f(uint x) pure public { - uint y = +x; - y; - } - } - )"; - CHECK_WARNING(text,"Use of unary + is deprecated"); - - // Test syntax error under 0.5.0 - text = R"( - pragma experimental "v0.5.0"; - contract test { - function f() pure public { - ufixed16x2 a = +3.25; - fixed16x2 b = -3.25; - a; b; - } - } - )"; - CHECK_ERROR(text, SyntaxError, "Use of unary + is deprecated"); - text = R"( - pragma experimental "v0.5.0"; - contract test { - function f(uint x) pure public { - uint y = +x; - y; - } - } - )"; - CHECK_ERROR(text, SyntaxError, "Use of unary + is deprecated"); -} - -BOOST_AUTO_TEST_CASE(leading_zero_rationals_convert) -{ - char const* text = R"( - contract A { - function f() pure public { - ufixed16x2 a = 0.5; - ufixed256x52 b = 0.0000000000000006661338147750939242541790008544921875; - fixed16x2 c = -0.5; - fixed256x52 d = -0.0000000000000006661338147750939242541790008544921875; - a; b; c; d; - } - } - )"; - CHECK_SUCCESS(text); -} - -BOOST_AUTO_TEST_CASE(size_capabilities_of_fixed_point_types) -{ - char const* text = R"( - contract test { - function f() public { - ufixed256x1 a = 123456781234567979695948382928485849359686494864095409282048094275023098123.5; - ufixed256x77 b = 0.920890746623327805482905058466021565416131529487595827354393978494366605267637; - ufixed224x78 c = 0.000000000001519884736399797998492268541131529487595827354393978494366605267646; - fixed256x1 d = -123456781234567979695948382928485849359686494864095409282048094275023098123.5; - fixed256x76 e = -0.93322335481643744342575580035176794825198893968114429702091846411734101080123; - fixed256x79 g = -0.0001178860664374434257558003517679482519889396811442970209184641173410108012309; - a; b; c; d; e; g; - } - } - )"; - CHECK_SUCCESS(text); -} - -BOOST_AUTO_TEST_CASE(zero_handling) -{ - char const* text = R"( - contract test { - function f() public { - fixed16x2 a = 0; a; - ufixed32x1 b = 0; b; - } - } - )"; - CHECK_SUCCESS(text); -} - -BOOST_AUTO_TEST_CASE(fixed_type_invalid_implicit_conversion_size) -{ - char const* text = R"( - contract test { - function f() public { - ufixed a = 11/4; - ufixed248x8 b = a; b; - } - } - )"; - CHECK_ERROR(text, TypeError, "Type ufixed128x18 is not implicitly convertible to expected type ufixed248x8"); -} - -BOOST_AUTO_TEST_CASE(fixed_type_invalid_implicit_conversion_lost_data) -{ - char const* text = R"( - contract test { - function f() public { - ufixed256x1 a = 1/3; a; - } - } - )"; - CHECK_ERROR(text, TypeError, "is not implicitly convertible to expected type ufixed256x1"); -} - -BOOST_AUTO_TEST_CASE(fixed_type_valid_explicit_conversions) -{ - char const* text = R"( - contract test { - function f() public { - ufixed256x80 a = ufixed256x80(1/3); a; - ufixed248x80 b = ufixed248x80(1/3); b; - ufixed8x1 c = ufixed8x1(1/3); c; - } - } - )"; - CHECK_SUCCESS(text); -} - -BOOST_AUTO_TEST_CASE(invalid_array_declaration_with_rational) -{ - char const* text = R"( - contract test { - function f() public { - uint[3.5] a; a; - } - } - )"; - CHECK_ERROR(text, TypeError, "Array with fractional length specified."); -} - -BOOST_AUTO_TEST_CASE(invalid_array_declaration_with_signed_fixed_type) -{ - char const* text = R"( - contract test { - function f() public { - uint[fixed(3.5)] a; a; - } - } - )"; - CHECK_ERROR(text, TypeError, "Invalid array length, expected integer literal or constant expression."); -} - -BOOST_AUTO_TEST_CASE(invalid_array_declaration_with_unsigned_fixed_type) -{ - char const* text = R"( - contract test { - function f() public { - uint[ufixed(3.5)] a; a; - } - } - )"; - CHECK_ERROR(text, TypeError, "Invalid array length, expected integer literal or constant expression."); -} - -BOOST_AUTO_TEST_CASE(rational_to_bytes_implicit_conversion) -{ - char const* text = R"( - contract test { - function f() public { - bytes32 c = 3.2; c; - } - } - )"; - CHECK_ERROR(text, TypeError, "is not implicitly convertible to expected type bytes32"); -} - -BOOST_AUTO_TEST_CASE(fixed_to_bytes_implicit_conversion) -{ - char const* text = R"( - contract test { - function f() public { - fixed a = 3.25; - bytes32 c = a; c; - } - } - )"; - CHECK_ERROR(text, TypeError, "fixed128x18 is not implicitly convertible to expected type bytes32"); -} - -BOOST_AUTO_TEST_CASE(mapping_with_fixed_literal) -{ - char const* text = R"( - contract test { - mapping(ufixed8x1 => string) fixedString; - function f() public { - fixedString[0.5] = "Half"; - } - } - )"; - CHECK_SUCCESS(text); -} - -BOOST_AUTO_TEST_CASE(fixed_points_inside_structs) -{ - char const* text = R"( - contract test { - struct myStruct { - ufixed a; - int b; - } - myStruct a = myStruct(3.125, 3); - } - )"; - CHECK_SUCCESS(text); -} - -BOOST_AUTO_TEST_CASE(inline_array_fixed_types) -{ - char const* text = R"( - contract test { - function f() public { - fixed[3] memory a = [fixed(3.5), fixed(-4.25), fixed(967.125)]; - } - } - )"; - CHECK_SUCCESS(text); -} - -BOOST_AUTO_TEST_CASE(inline_array_rationals) -{ - char const* text = R"( - contract test { - function f() public { - ufixed128x3[4] memory a = [ufixed128x3(3.5), 4.125, 2.5, 4.0]; - } - } - )"; - CHECK_SUCCESS(text); -} - -BOOST_AUTO_TEST_CASE(rational_index_access) -{ - char const* text = R"( - contract test { - function f() public { - uint[] memory a; - a[.5]; - } - } - )"; - CHECK_ERROR(text, TypeError, "rational_const 1 / 2 is not implicitly convertible to expected type uint256"); -} - -BOOST_AUTO_TEST_CASE(rational_to_fixed_literal_expression) -{ - char const* text = R"( - contract test { - function f() public { - ufixed64x8 a = 3.5 * 3; - ufixed64x8 b = 4 - 2.5; - ufixed64x8 c = 11 / 4; - ufixed240x5 d = 599 + 0.21875; - ufixed256x80 e = ufixed256x80(35.245 % 12.9); - ufixed256x80 f = ufixed256x80(1.2 % 2); - fixed g = 2 ** -2; - a; b; c; d; e; f; g; - } - } - )"; - CHECK_SUCCESS(text); -} - -BOOST_AUTO_TEST_CASE(rational_as_exponent_value_signed) -{ - char const* text = R"( - contract test { - function f() public { - fixed g = 2 ** -2.2; - } - } - )"; - CHECK_ERROR(text, TypeError, "not compatible with types"); -} - -BOOST_AUTO_TEST_CASE(rational_as_exponent_value_unsigned) -{ - char const* text = R"( - contract test { - function f() public { - ufixed b = 3 ** 2.5; - } - } - )"; - CHECK_ERROR(text, TypeError, "not compatible with types"); -} - -BOOST_AUTO_TEST_CASE(rational_as_exponent_half) -{ - char const* text = R"( - contract test { - function f() public { - 2 ** (1/2); - } - } - )"; - CHECK_ERROR(text, TypeError, "not compatible with types"); -} - -BOOST_AUTO_TEST_CASE(rational_as_exponent_value_neg_quarter) -{ - char const* text = R"( - contract test { - function f() public { - 42 ** (-1/4); - } - } - )"; - CHECK_ERROR(text, TypeError, "not compatible with types"); -} - -BOOST_AUTO_TEST_CASE(fixed_point_casting_exponents_15) -{ - char const* text = R"( - contract test { - function f() public { - var a = 3 ** ufixed(1.5); - } - } - )"; - CHECK_ERROR(text, TypeError, "not compatible with types"); -} - -BOOST_AUTO_TEST_CASE(fixed_point_casting_exponents_neg) -{ - char const* text = R"( - contract test { - function f() public { - var c = 42 ** fixed(-1/4); - } - } - )"; - CHECK_ERROR(text, TypeError, "not compatible with types"); + CHECK_SUCCESS_NO_WARNINGS(sourceCode); } -BOOST_AUTO_TEST_CASE(var_capable_of_holding_constant_rationals) -{ - char const* text = R"( - contract test { - function f() public { - var a = 0.12345678; - var b = 12345678.352; - var c = 0.00000009; - a; b; c; - } - } - )"; - CHECK_SUCCESS(text); -} - -BOOST_AUTO_TEST_CASE(var_and_rational_with_tuple) -{ - char const* text = R"( - contract test { - function f() public { - var (a, b) = (.5, 1/3); - a; b; - } - } - )"; - CHECK_SUCCESS(text); -} - -BOOST_AUTO_TEST_CASE(var_handle_divided_integers) -{ - char const* text = R"( - contract test { - function f() public { - var x = 1/3; - } - } - )"; - CHECK_SUCCESS(text); -} - -BOOST_AUTO_TEST_CASE(rational_bitnot_unary_operation) -{ - char const* text = R"( - contract test { - function f() public { - ~fixed(3.5); - } - } - )"; - CHECK_ERROR(text, TypeError, "cannot be applied"); -} - -BOOST_AUTO_TEST_CASE(rational_bitor_binary_operation) -{ - char const* text = R"( - contract test { - function f() public { - fixed(1.5) | 3; - } - } - )"; - CHECK_ERROR(text, TypeError, "not compatible with types"); -} - -BOOST_AUTO_TEST_CASE(rational_bitxor_binary_operation) -{ - char const* text = R"( - contract test { - function f() public { - fixed(1.75) ^ 3; - } - } - )"; - CHECK_ERROR(text, TypeError, "not compatible with types"); -} - -BOOST_AUTO_TEST_CASE(rational_bitand_binary_operation) -{ - char const* text = R"( - contract test { - function f() public { - fixed(1.75) & 3; - } - } - )"; - CHECK_ERROR(text, TypeError, "not compatible with types"); -} - -BOOST_AUTO_TEST_CASE(missing_bool_conversion) -{ - char const* text = R"( - contract test { - function b(uint a) public { - bool(a == 1); - } - } - )"; - CHECK_SUCCESS(text); -} - -BOOST_AUTO_TEST_CASE(integer_and_fixed_interaction) -{ - char const* text = R"( - contract test { - function f() public { - ufixed a = uint64(1) + ufixed(2); - } - } - )"; - CHECK_SUCCESS(text); -} - -BOOST_AUTO_TEST_CASE(one_divided_by_three_integer_conversion) -{ - char const* text = R"( - contract test { - function f() public { - uint a = 1/3; - } - } - )"; - CHECK_ERROR(text, TypeError, "is not implicitly convertible to expected type uint256. Try converting to type ufixed256x77"); -} - -BOOST_AUTO_TEST_CASE(unused_return_value) -{ - char const* text = R"( - contract test { - function g() public returns (uint) {} - function f() public { - g(); - } - } - )"; - CHECK_SUCCESS(text); -} - -BOOST_AUTO_TEST_CASE(unused_return_value_send) -{ - char const* text = R"( - contract test { - function f() public { - address(0x12).send(1); - } - } - )"; - CHECK_WARNING(text, "Failure condition of 'send' ignored. Consider using 'transfer' instead."); -} - -BOOST_AUTO_TEST_CASE(unused_return_value_call) -{ - char const* text = R"( - contract test { - function f() public { - address(0x12).call("abc"); - } - } - )"; - CHECK_WARNING(text, "Return value of low-level calls not used"); -} - -BOOST_AUTO_TEST_CASE(unused_return_value_call_value) -{ - char const* text = R"( - contract test { - function f() public { - address(0x12).call.value(2)("abc"); - } - } - )"; - CHECK_WARNING(text, "Return value of low-level calls not used"); -} - -BOOST_AUTO_TEST_CASE(unused_return_value_callcode) -{ - char const* text = R"( - contract test { - function f() public { - address(0x12).callcode("abc"); - } - } - )"; - CHECK_WARNING_ALLOW_MULTI(text, (std::vector<std::string>{ - "Return value of low-level calls not used", - "\"callcode\" has been deprecated" - })); -} - -BOOST_AUTO_TEST_CASE(unused_return_value_delegatecall) -{ - char const* text = R"( - contract test { - function f() public { - address(0x12).delegatecall("abc"); - } - } - )"; - CHECK_WARNING(text, "Return value of low-level calls not used"); -} - -BOOST_AUTO_TEST_CASE(warn_about_callcode) -{ - char const* text = R"( - contract test { - function f() pure public { - address(0x12).callcode; - } - } - )"; - CHECK_WARNING(text, "\"callcode\" has been deprecated in favour of \"delegatecall\""); - text = R"( - pragma experimental "v0.5.0"; - contract test { - function f() pure public { - address(0x12).callcode; - } - } - )"; - CHECK_ERROR(text, TypeError, "\"callcode\" has been deprecated in favour of \"delegatecall\""); -} - -BOOST_AUTO_TEST_CASE(no_warn_about_callcode_as_function) -{ - char const* text = R"( - contract test { - function callcode() pure public { - test.callcode(); - } - } - )"; - CHECK_SUCCESS_NO_WARNINGS(text); -} - -BOOST_AUTO_TEST_CASE(payable_in_library) -{ - char const* text = R"( - library test { - function f() payable public {} - } - )"; - CHECK_ERROR(text, TypeError, "Library functions cannot be payable."); -} - -BOOST_AUTO_TEST_CASE(payable_external) -{ - char const* text = R"( - contract test { - function f() payable external {} - } - )"; - CHECK_SUCCESS(text); -} - -BOOST_AUTO_TEST_CASE(payable_internal) -{ - char const* text = R"( - contract test { - function f() payable internal {} - } - )"; - CHECK_ERROR(text, TypeError, "Internal functions cannot be payable."); -} - -BOOST_AUTO_TEST_CASE(payable_private) -{ - char const* text = R"( - contract test { - function f() payable private {} - } - )"; - CHECK_ERROR(text, TypeError, "Internal functions cannot be payable."); -} - -BOOST_AUTO_TEST_CASE(illegal_override_payable) -{ - char const* text = R"( - contract B { function f() payable public {} } - contract C is B { function f() public {} } - )"; - CHECK_ERROR(text, TypeError, "Overriding function changes state mutability from \"payable\" to \"nonpayable\"."); -} - -BOOST_AUTO_TEST_CASE(illegal_override_payable_nonpayable) -{ - char const* text = R"( - contract B { function f() public {} } - contract C is B { function f() payable public {} } - )"; - CHECK_ERROR(text, TypeError, "Overriding function changes state mutability from \"nonpayable\" to \"payable\"."); -} - -BOOST_AUTO_TEST_CASE(function_variable_mixin) -{ - // bug #1798 (cpp-ethereum), related to #1286 (solidity) - char const* text = R"( - contract attribute { - bool ok = false; - } - contract func { - function ok() public returns (bool) { return true; } - } - - contract attr_func is attribute, func { - function checkOk() public returns (bool) { return ok(); } - } - )"; - CHECK_ERROR(text, DeclarationError, "Identifier already declared."); -} - -BOOST_AUTO_TEST_CASE(calling_payable) -{ - char const* text = R"( - contract receiver { function pay() payable public {} } - contract test { - function f() public { (new receiver()).pay.value(10)(); } - receiver r = new receiver(); - function g() public { r.pay.value(10)(); } - } - )"; - CHECK_SUCCESS(text); -} - -BOOST_AUTO_TEST_CASE(calling_nonpayable) -{ - char const* text = R"( - contract receiver { function nopay() public {} } - contract test { - function f() public { (new receiver()).nopay.value(10)(); } - } - )"; - CHECK_ERROR(text, TypeError, "Member \"value\" not found or not visible after argument-dependent lookup in function () external - did you forget the \"payable\" modifier?"); -} - -BOOST_AUTO_TEST_CASE(non_payable_constructor) +BOOST_AUTO_TEST_CASE(warn_nonpresent_pragma) { char const* text = R"( - contract C { - function C() { } - } - contract D { - function f() public returns (uint) { - (new C).value(2)(); - return 2; - } - } + contract C {} )"; - CHECK_ERROR(text, TypeError, "Member \"value\" not found or not visible after argument-dependent lookup in function () returns (contract C) - did you forget the \"payable\" modifier?"); -} - -BOOST_AUTO_TEST_CASE(warn_nonpresent_pragma) -{ - char const* text = "contract C {}"; auto sourceAndError = parseAnalyseAndReturnError(text, true, false); BOOST_REQUIRE(!sourceAndError.second.empty()); BOOST_REQUIRE(!!sourceAndError.first); @@ -4846,2334 +387,149 @@ BOOST_AUTO_TEST_CASE(unsatisfied_version) BOOST_CHECK(searchErrorMessage(*sourceAndError.second.front(), "Source file requires different compiler version")); } -BOOST_AUTO_TEST_CASE(invalid_array_as_statement) -{ - char const* text = R"( - contract test { - struct S { uint x; } - function test(uint k) public { S[k]; } - } - )"; - CHECK_ERROR(text, TypeError, "Integer constant expected."); -} - -BOOST_AUTO_TEST_CASE(using_directive_for_missing_selftype) -{ - char const* text = R"( - library B { - function b() public {} - } - - contract A { - using B for bytes; - - function a() public { - bytes memory x; - x.b(); - } - } - )"; - CHECK_ERROR(text, TypeError, "Member \"b\" not found or not visible after argument-dependent lookup in bytes memory"); -} - -BOOST_AUTO_TEST_CASE(shift_constant_left_negative_rvalue) -{ - char const* text = R"( - contract C { - uint public a = 0x42 << -8; - } - )"; - CHECK_ERROR(text, TypeError, "Operator << not compatible with types int_const 66 and int_const -8"); -} - -BOOST_AUTO_TEST_CASE(shift_constant_right_negative_rvalue) -{ - char const* text = R"( - contract C { - uint public a = 0x42 >> -8; - } - )"; - CHECK_ERROR(text, TypeError, "Operator >> not compatible with types int_const 66 and int_const -8"); -} - -BOOST_AUTO_TEST_CASE(shift_constant_left_excessive_rvalue) -{ - char const* text = R"( - contract C { - uint public a = 0x42 << 0x100000000; - } - )"; - CHECK_ERROR(text, TypeError, "Operator << not compatible with types int_const 66 and int_const 4294967296"); -} - -BOOST_AUTO_TEST_CASE(shift_constant_right_excessive_rvalue) -{ - char const* text = R"( - contract C { - uint public a = 0x42 >> 0x100000000; - } - )"; - CHECK_ERROR(text, TypeError, "Operator >> not compatible with types int_const 66 and int_const 4294967296"); -} - -BOOST_AUTO_TEST_CASE(shift_constant_right_fractional) -{ - char const* text = R"( - contract C { - uint public a = 0x42 >> (1 / 2); - } - )"; - CHECK_ERROR(text, TypeError, "Operator >> not compatible with types int_const 66 and rational_const 1 / 2"); -} - -BOOST_AUTO_TEST_CASE(inline_assembly_unbalanced_positive_stack) -{ - char const* text = R"( - contract test { - function f() public { - assembly { - 1 - } - } - } - )"; - CHECK_ERROR(text, DeclarationError, "Unbalanced stack at the end of a block: 1 surplus item(s)."); -} - -BOOST_AUTO_TEST_CASE(inline_assembly_unbalanced_negative_stack) -{ - char const* text = R"( - contract test { - function f() public { - assembly { - pop - } - } - } - )"; - CHECK_ERROR(text, DeclarationError, "Unbalanced stack at the end of a block: 1 missing item(s)."); -} - -BOOST_AUTO_TEST_CASE(inline_assembly_unbalanced_two_stack_load) -{ - char const* text = R"( - pragma experimental "v0.5.0"; - contract c { - uint8 x; - function f() public { - assembly { pop(x) } - } - } - )"; - CHECK_ERROR(text, TypeError, "Only local variables are supported. To access storage variables,"); -} - -BOOST_AUTO_TEST_CASE(inline_assembly_in_modifier) -{ - char const* text = R"( - pragma experimental "v0.5.0"; - contract test { - modifier m { - uint a = 1; - assembly { - a := 2 - } - _; - } - function f() public m { - } - } - )"; - CHECK_SUCCESS(text); -} - -BOOST_AUTO_TEST_CASE(inline_assembly_storage) -{ - char const* text = R"( - pragma experimental "v0.5.0"; - contract test { - uint x = 1; - function f() public { - assembly { - x := 2 - } - } - } - )"; - CHECK_ERROR(text, TypeError, "Only local variables are supported. To access storage variables,"); -} - -BOOST_AUTO_TEST_CASE(inline_assembly_storage_in_modifiers) -{ - char const* text = R"( - pragma experimental "v0.5.0"; - contract test { - uint x = 1; - modifier m { - assembly { - x := 2 - } - _; - } - function f() public m { - } - } - )"; - CHECK_ERROR(text, TypeError, "Only local variables are supported. To access storage variables,"); -} - -BOOST_AUTO_TEST_CASE(inline_assembly_constant_assign) -{ - char const* text = R"( - pragma experimental "v0.5.0"; - contract test { - uint constant x = 1; - function f() public { - assembly { - x := 2 - } - } - } - )"; - CHECK_ERROR(text, TypeError, "Constant variables not supported by inline assembly"); -} - -BOOST_AUTO_TEST_CASE(inline_assembly_constant_access) -{ - char const* text = R"( - pragma experimental "v0.5.0"; - contract test { - uint constant x = 1; - function f() public { - assembly { - let y := x - } - } - } - )"; - CHECK_ERROR(text, TypeError, "Constant variables not supported by inline assembly"); -} - -BOOST_AUTO_TEST_CASE(inline_assembly_local_variable_access_out_of_functions) -{ - char const* text = R"( - pragma experimental "v0.5.0"; - contract test { - function f() public { - uint a; - assembly { - function g() -> x { x := a } - } - } - } - )"; - CHECK_ERROR(text, DeclarationError, "Cannot access local Solidity variables from inside an inline assembly function."); -} - -BOOST_AUTO_TEST_CASE(inline_assembly_local_variable_access_out_of_functions_storage_ptr) -{ - char const* text = R"( - pragma experimental "v0.5.0"; - contract test { - uint[] r; - function f() public { - uint[] storage a = r; - assembly { - function g() -> x { x := a_offset } - } - } - } - )"; - CHECK_ERROR(text, DeclarationError, "Cannot access local Solidity variables from inside an inline assembly function."); -} - -BOOST_AUTO_TEST_CASE(inline_assembly_storage_variable_access_out_of_functions) -{ - char const* text = R"( - pragma experimental "v0.5.0"; - contract test { - uint a; - function f() pure public { - assembly { - function g() -> x { x := a_slot } - } - } - } - )"; - CHECK_SUCCESS_NO_WARNINGS(text); -} - -BOOST_AUTO_TEST_CASE(inline_assembly_constant_variable_via_offset) -{ - char const* text = R"( - contract test { - uint constant x = 2; - function f() pure public { - assembly { - let r := x_offset - } - } - } - )"; - CHECK_ERROR(text, TypeError, "Constant variables not supported by inline assembly."); -} - -BOOST_AUTO_TEST_CASE(inline_assembly_calldata_variables) -{ - char const* text = R"( - pragma experimental "v0.5.0"; - contract C { - function f(bytes bytesAsCalldata) external { - assembly { - let x := bytesAsCalldata - } - } - } - )"; - CHECK_ERROR(text, TypeError, "Call data elements cannot be accessed directly."); -} - -BOOST_AUTO_TEST_CASE(inline_assembly_050_literals_on_stack) -{ - char const* text = R"( - pragma experimental "v0.5.0"; - contract C { - function f() pure public { - assembly { - 1 - } - } - } - )"; - CHECK_ALLOW_MULTI(text, (std::vector<std::pair<Error::Type, std::string>>{ - {Error::Type::SyntaxError, "are not supposed to return"}, - {Error::Type::DeclarationError, "Unbalanced stack"}, - })); -} - -BOOST_AUTO_TEST_CASE(inline_assembly_literals_on_stack) -{ - char const* text = R"( - contract C { - function f() pure public { - assembly { - 1 - } - } - } - )"; - CHECK_ALLOW_MULTI(text, (std::vector<std::pair<Error::Type, std::string>>{ - {Error::Type::Warning, "are not supposed to return"}, - {Error::Type::DeclarationError, "Unbalanced stack"}, - })); -} - -BOOST_AUTO_TEST_CASE(inline_assembly_050_bare_instructions) -{ - char const* text = R"( - pragma experimental "v0.5.0"; - contract C { - function f() view public { - assembly { - address - pop - } - } - } - )"; - CHECK_ALLOW_MULTI(text, (std::vector<std::pair<Error::Type, std::string>>{ - {Error::Type::SyntaxError, "The use of non-functional"}, - {Error::Type::SyntaxError, "The use of non-functional"} - })); -} - -BOOST_AUTO_TEST_CASE(inline_assembly_bare_instructions) -{ - char const* text = R"( - contract C { - function f() view public { - assembly { - address - pop - } - } - } - )"; - CHECK_ALLOW_MULTI(text, (std::vector<std::pair<Error::Type, std::string>>{ - {Error::Type::Warning, "The use of non-functional"}, - {Error::Type::Warning, "The use of non-functional"} - })); -} - -BOOST_AUTO_TEST_CASE(inline_assembly_050_labels) -{ - char const* text = R"( - pragma experimental "v0.5.0"; - contract C { - function f() pure public { - assembly { - label: - } - } - } - )"; - CHECK_ALLOW_MULTI(text, (std::vector<std::pair<Error::Type, std::string>>{ - {Error::Type::SyntaxError, "Jump instructions and labels are low-level"}, - {Error::Type::SyntaxError, "The use of labels is deprecated"} - })); -} - -BOOST_AUTO_TEST_CASE(inline_assembly_labels) -{ - char const* text = R"( - contract C { - function f() pure public { - assembly { - label: - } - } - } - )"; - CHECK_ALLOW_MULTI(text, (std::vector<std::pair<Error::Type, std::string>>{ - {Error::Type::Warning, "Jump instructions and labels are low-level"}, - {Error::Type::Warning, "The use of labels is deprecated"} - })); -} - -BOOST_AUTO_TEST_CASE(inline_assembly_050_jump) -{ - char const* text = R"( - pragma experimental "v0.5.0"; - contract C { - function f() pure public { - assembly { - jump(2) - } - } - } - )"; - CHECK_ALLOW_MULTI(text, (std::vector<std::pair<Error::Type, std::string>>{ - {Error::Type::SyntaxError, "Jump instructions and labels are low-level"} - })); -} - -BOOST_AUTO_TEST_CASE(inline_assembly_jump) -{ - char const* text = R"( - contract C { - function f() pure public { - assembly { - jump(2) - } - } - } - )"; - CHECK_ALLOW_MULTI(text, (std::vector<std::pair<Error::Type, std::string>>{ - {Error::Type::TypeError, "Function declared as pure"}, - {Error::Type::Warning, "Jump instructions and labels are low-level"} - })); -} - -BOOST_AUTO_TEST_CASE(inline_assembly_050_leave_items_on_stack) -{ - char const* text = R"( - pragma experimental "v0.5.0"; - contract C { - function f() pure public { - assembly { - mload(0) - } - } - } - )"; - CHECK_ALLOW_MULTI(text, (std::vector<std::pair<Error::Type, std::string>>{ - {Error::Type::SyntaxError, "are not supposed to return"}, - {Error::Type::DeclarationError, "Unbalanced stack"}, - })); -} - -BOOST_AUTO_TEST_CASE(inline_assembly_leave_items_on_stack) -{ - char const* text = R"( - contract C { - function f() pure public { - assembly { - mload(0) - } - } - } - )"; - CHECK_ALLOW_MULTI(text, (std::vector<std::pair<Error::Type, std::string>>{ - {Error::Type::Warning, "are not supposed to return"}, - {Error::Type::DeclarationError, "Unbalanced stack"}, - })); -} - -BOOST_AUTO_TEST_CASE(invalid_mobile_type) -{ - char const* text = R"( - contract C { - function f() public { - // Invalid number - [1, 78901234567890123456789012345678901234567890123456789345678901234567890012345678012345678901234567]; - } - } - )"; - CHECK_ERROR(text, TypeError, "Invalid rational number."); -} - -BOOST_AUTO_TEST_CASE(warns_msg_value_in_non_payable_public_function) -{ - char const* text = R"( - contract C { - function f() view public { - msg.value; - } - } - )"; - CHECK_WARNING(text, "\"msg.value\" used in non-payable function. Do you want to add the \"payable\" modifier to this function?"); -} - -BOOST_AUTO_TEST_CASE(does_not_warn_msg_value_in_payable_function) -{ - char const* text = R"( - contract C { - function f() payable public { - msg.value; - } - } - )"; - CHECK_SUCCESS_NO_WARNINGS(text); -} - -BOOST_AUTO_TEST_CASE(does_not_warn_msg_value_in_internal_function) -{ - char const* text = R"( - contract C { - function f() view internal { - msg.value; - } - } - )"; - CHECK_SUCCESS_NO_WARNINGS(text); -} - -BOOST_AUTO_TEST_CASE(does_not_warn_msg_value_in_library) -{ - char const* text = R"( - library C { - function f() view public { - msg.value; - } - } - )"; - CHECK_SUCCESS_NO_WARNINGS(text); -} - -BOOST_AUTO_TEST_CASE(does_not_warn_msg_value_in_modifier_following_non_payable_public_function) -{ - char const* text = R"( - contract c { - function f() pure public { } - modifier m() { msg.value; _; } - } - )"; - CHECK_SUCCESS_NO_WARNINGS(text); -} - -BOOST_AUTO_TEST_CASE(assignment_to_constant) -{ - char const* text = R"( - contract c { - uint constant a = 1; - function f() public { a = 2; } - } - )"; - CHECK_ERROR(text, TypeError, "Cannot assign to a constant variable."); -} - -BOOST_AUTO_TEST_CASE(return_structs) -{ - char const* text = R"( - pragma experimental ABIEncoderV2; - contract C { - struct S { uint a; T[] sub; } - struct T { uint[] x; } - function f() returns (uint, S) { - } - } - )"; - CHECK_SUCCESS(text); -} - -BOOST_AUTO_TEST_CASE(read_returned_struct) -{ - char const* text = R"( - pragma experimental ABIEncoderV2; - contract A { - struct T { - int x; - int y; - } - function g() public returns (T) { - return this.g(); - } - } - )"; - CHECK_WARNING(text, "Experimental features"); -} -BOOST_AUTO_TEST_CASE(address_checksum_type_deduction) -{ - char const* text = R"( - contract C { - function f() public { - var x = 0xfA0bFc97E48458494Ccd857e1A85DC91F7F0046E; - x.send(2); - } - } - )"; - CHECK_SUCCESS(text); -} - -BOOST_AUTO_TEST_CASE(invalid_address_checksum) -{ - char const* text = R"( - contract C { - function f() pure public { - address x = 0xFA0bFc97E48458494Ccd857e1A85DC91F7F0046E; - x; - } - } - )"; - CHECK_WARNING(text, "This looks like an address but has an invalid checksum."); -} - -BOOST_AUTO_TEST_CASE(invalid_address_no_checksum) -{ - char const* text = R"( - contract C { - function f() pure public { - address x = 0xfa0bfc97e48458494ccd857e1a85dc91f7f0046e; - x; - } - } - )"; - CHECK_WARNING(text, "This looks like an address but has an invalid checksum."); -} - -BOOST_AUTO_TEST_CASE(invalid_address_length_short) -{ - char const* text = R"( - contract C { - function f() pure public { - address x = 0xA0bFc97E48458494Ccd857e1A85DC91F7F0046E; - x; - } - } - )"; - CHECK_WARNING(text, "This looks like an address but has an invalid checksum."); -} - -BOOST_AUTO_TEST_CASE(invalid_address_length_long) -{ - char const* text = R"( - contract C { - function f() pure public { - address x = 0xFA0bFc97E48458494Ccd857e1A85DC91F7F0046E0; - x; - } - } - )"; - CHECK_ALLOW_MULTI(text, (std::vector<std::pair<Error::Type, std::string>>{ - {Error::Type::Warning, "This looks like an address but has an invalid checksum."}, - {Error::Type::TypeError, "not implicitly convertible"} - })); -} - -BOOST_AUTO_TEST_CASE(address_test_for_bug_in_implementation) -{ - // A previous implementation claimed the string would be an address - char const* text = R"( - contract AddrString { - address public test = "0xCA35b7d915458EF540aDe6068dFe2F44E8fa733c"; - } - )"; - CHECK_ERROR(text, TypeError, "is not implicitly convertible to expected type address"); - text = R"( - contract AddrString { - function f() public returns (address) { - return "0xCA35b7d915458EF540aDe6068dFe2F44E8fa733c"; - } - } - )"; - CHECK_ERROR(text, TypeError, "is not implicitly convertible to expected type"); -} - -BOOST_AUTO_TEST_CASE(early_exit_on_fatal_errors) -{ - // This tests a crash that occured because we did not stop for fatal errors. - char const* text = R"( - contract C { - struct S { - ftring a; - } - S public s; - function s() s { - } - } - )"; - CHECK_ERROR(text, DeclarationError, "Identifier not found or not unique"); -} - -BOOST_AUTO_TEST_CASE(address_methods) -{ - char const* text = R"( - contract C { - function f() public { - address addr; - uint balance = addr.balance; - bool callRet = addr.call(); - bool callcodeRet = addr.callcode(); - bool delegatecallRet = addr.delegatecall(); - bool sendRet = addr.send(1); - addr.transfer(1); - callRet; callcodeRet; delegatecallRet; sendRet; - } - } - )"; - CHECK_SUCCESS(text); -} - -BOOST_AUTO_TEST_CASE(interface) -{ - char const* text = R"( - interface I { - } - )"; - CHECK_SUCCESS(text); -} - -BOOST_AUTO_TEST_CASE(interface_functions) -{ - char const* text = R"( - interface I { - function(); - function f(); - } - )"; - CHECK_SUCCESS(text); -} - -BOOST_AUTO_TEST_CASE(interface_function_bodies) -{ - char const* text = R"( - interface I { - function f() public { - } - } - )"; - CHECK_ERROR(text, TypeError, "Functions in interfaces cannot have an implementation"); -} - -BOOST_AUTO_TEST_CASE(interface_events) -{ - char const* text = R"( - interface I { - event E(); - } - )"; - CHECK_SUCCESS(text); -} - -BOOST_AUTO_TEST_CASE(interface_inheritance) -{ - char const* text = R"( - interface A { - } - interface I is A { - } - )"; - CHECK_ERROR(text, TypeError, "Interfaces cannot inherit"); -} - - -BOOST_AUTO_TEST_CASE(interface_structs) -{ - char const* text = R"( - interface I { - struct A { - } - } - )"; - CHECK_ERROR(text, TypeError, "Structs cannot be defined in interfaces"); -} - -BOOST_AUTO_TEST_CASE(interface_variables) -{ - char const* text = R"( - interface I { - uint a; - } - )"; - CHECK_ERROR(text, TypeError, "Variables cannot be declared in interfaces"); -} - -BOOST_AUTO_TEST_CASE(interface_function_parameters) -{ - char const* text = R"( - interface I { - function f(uint a) public returns (bool); - } - )"; - CHECK_SUCCESS(text); -} - -BOOST_AUTO_TEST_CASE(interface_enums) -{ - char const* text = R"( - interface I { - enum A { B, C } - } - )"; - CHECK_ERROR(text, TypeError, "Enumerable cannot be declared in interfaces"); -} - -BOOST_AUTO_TEST_CASE(using_interface) -{ - char const* text = R"( - interface I { - function f(); - } - contract C is I { - function f() public { - } - } - )"; - CHECK_SUCCESS(text); -} - -BOOST_AUTO_TEST_CASE(using_interface_complex) -{ - char const* text = R"( - interface I { - event A(); - function f(); - function g(); - function(); - } - contract C is I { - function f() public { - } - } - )"; - CHECK_SUCCESS(text); -} - -BOOST_AUTO_TEST_CASE(interface_implement_public_contract) -{ - char const* text = R"( - interface I { - function f() external; - } - contract C is I { - function f() public { - } - } - )"; - CHECK_SUCCESS_NO_WARNINGS(text); -} - -BOOST_AUTO_TEST_CASE(warn_about_throw) -{ - char const* text = R"( - contract C { - function f() pure public { - throw; - } - } - )"; - CHECK_WARNING(text, "\"throw\" is deprecated in favour of \"revert()\", \"require()\" and \"assert()\""); - text = R"( - pragma experimental "v0.5.0"; - contract C { - function f() pure public { - throw; - } - } - )"; - CHECK_ERROR(text, SyntaxError, "\"throw\" is deprecated in favour of \"revert()\", \"require()\" and \"assert()\""); -} - -BOOST_AUTO_TEST_CASE(bare_revert) -{ - char const* text = R"( - contract C { - function f(uint x) pure public { - if (x > 7) - revert; - } - } - )"; - CHECK_ERROR(text, TypeError, "No matching declaration found"); -} - -BOOST_AUTO_TEST_CASE(revert_with_reason) -{ - char const* text = R"( - contract C { - function f(uint x) pure public { - if (x > 7) - revert("abc"); - else - revert(); - } - } - )"; - CHECK_SUCCESS_NO_WARNINGS(text); -} - -BOOST_AUTO_TEST_CASE(bare_others) -{ - CHECK_WARNING("contract C { function f() pure public { selfdestruct; } }", "Statement has no effect."); - CHECK_WARNING("contract C { function f() pure public { assert; } }", "Statement has no effect."); - // This is different because it does have overloads. - CHECK_ERROR("contract C { function f() pure public { require; } }", TypeError, "No matching declaration found after variable lookup."); - CHECK_WARNING("contract C { function f() pure public { selfdestruct; } }", "Statement has no effect."); -} - -BOOST_AUTO_TEST_CASE(pure_statement_in_for_loop) -{ - char const* text = R"( - contract C { - function f() pure public { - for (uint x = 0; x < 10; true) - x++; - } - } - )"; - CHECK_WARNING(text, "Statement has no effect."); -} - -BOOST_AUTO_TEST_CASE(pure_statement_check_for_regular_for_loop) -{ - char const* text = R"( - contract C { - function f() pure public { - for (uint x = 0; true; x++) - {} - } - } - )"; - CHECK_SUCCESS(text); -} - -BOOST_AUTO_TEST_CASE(warn_unused_local) -{ - char const* text = R"( - contract C { - function f() pure public { - uint a; - } - } - )"; - CHECK_WARNING(text, "Unused local variable."); -} - -BOOST_AUTO_TEST_CASE(warn_unused_local_assigned) -{ - char const* text = R"( - contract C { - function f() pure public { - uint a = 1; - } - } - )"; - CHECK_WARNING(text, "Unused local variable."); -} - -BOOST_AUTO_TEST_CASE(warn_unused_function_parameter) -{ - char const* text = R"( - contract C { - function f(uint a) pure public { - } - } - )"; - CHECK_WARNING(text, "Unused function parameter. Remove or comment out the variable name to silence this warning."); - text = R"( - contract C { - function f(uint a) pure public { - } - } - )"; - CHECK_SUCCESS(text); -} - -BOOST_AUTO_TEST_CASE(warn_unused_return_parameter) -{ - char const* text = R"( - contract C { - function f() pure public returns (uint a) { - } - } - )"; - CHECK_WARNING(text, "Unused function parameter. Remove or comment out the variable name to silence this warning."); - text = R"( - contract C { - function f() pure public returns (uint a) { - return; - } - } - )"; - CHECK_WARNING(text, "Unused function parameter. Remove or comment out the variable name to silence this warning."); - text = R"( - contract C { - function f() pure public returns (uint) { - } - } - )"; - CHECK_SUCCESS_NO_WARNINGS(text); - text = R"( - contract C { - function f() pure public returns (uint a) { - a = 1; - } - } - )"; - CHECK_SUCCESS_NO_WARNINGS(text); - text = R"( - contract C { - function f() pure public returns (uint a) { - return 1; - } - } - )"; - CHECK_SUCCESS_NO_WARNINGS(text); -} - -BOOST_AUTO_TEST_CASE(no_unused_warning_interface_arguments) -{ - char const* text = R"( - interface I { - function f(uint a) pure external returns (uint b); - } - )"; - CHECK_SUCCESS_NO_WARNINGS(text); -} - -BOOST_AUTO_TEST_CASE(no_unused_warning_abstract_arguments) -{ - char const* text = R"( - contract C { - function f(uint a) pure public returns (uint b); - } - )"; - CHECK_SUCCESS_NO_WARNINGS(text); -} - -BOOST_AUTO_TEST_CASE(no_unused_warnings) -{ - char const* text = R"( - contract C { - function f(uint a) pure public returns (uint b) { - uint c = 1; - b = a + c; - } - } - )"; - CHECK_SUCCESS_NO_WARNINGS(text); -} - -BOOST_AUTO_TEST_CASE(no_unused_dec_after_use) -{ - char const* text = R"( - contract C { - function f() pure public { - a = 7; - uint a; - } - } - )"; - CHECK_SUCCESS_NO_WARNINGS(text); -} - -BOOST_AUTO_TEST_CASE(no_unused_inline_asm) -{ - char const* text = R"( - contract C { - function f() pure public { - uint a; - assembly { - a := 1 - } - } - } - )"; - CHECK_SUCCESS_NO_WARNINGS(text); -} - -BOOST_AUTO_TEST_CASE(shadowing_builtins_with_functions) -{ - char const* text = R"( - contract C { - function keccak256() pure public {} - } - )"; - CHECK_WARNING(text, "shadows a builtin symbol"); -} - -BOOST_AUTO_TEST_CASE(shadowing_builtins_with_variables) -{ - char const* text = R"( - contract C { - function f() pure public { - uint msg; - msg; - } - } - )"; - CHECK_WARNING(text, "shadows a builtin symbol"); -} - -BOOST_AUTO_TEST_CASE(shadowing_builtins_with_storage_variables) -{ - char const* text = R"( - contract C { - uint msg; - } - )"; - CHECK_WARNING(text, "shadows a builtin symbol"); -} - -BOOST_AUTO_TEST_CASE(shadowing_builtin_at_global_scope) -{ - char const* text = R"( - contract msg { - } - )"; - CHECK_WARNING(text, "shadows a builtin symbol"); -} - -BOOST_AUTO_TEST_CASE(shadowing_builtins_with_parameters) -{ - char const* text = R"( - contract C { - function f(uint require) pure public { - require = 2; - } - } - )"; - CHECK_WARNING(text, "shadows a builtin symbol"); -} - -BOOST_AUTO_TEST_CASE(shadowing_builtins_with_return_parameters) -{ - char const* text = R"( - contract C { - function f() pure public returns (uint require) { - require = 2; - } - } - )"; - CHECK_WARNING(text, "shadows a builtin symbol"); -} - -BOOST_AUTO_TEST_CASE(shadowing_builtins_with_events) -{ - char const* text = R"( - contract C { - event keccak256(); - } - )"; - CHECK_WARNING(text, "shadows a builtin symbol"); -} - -BOOST_AUTO_TEST_CASE(shadowing_builtins_ignores_struct) -{ - char const* text = R"( - contract C { - struct a { - uint msg; - } - } - )"; - CHECK_SUCCESS_NO_WARNINGS(text); -} - -BOOST_AUTO_TEST_CASE(shadowing_builtins_ignores_constructor) -{ - char const* text = R"( - contract C { - constructor() public {} - } - )"; - CHECK_SUCCESS_NO_WARNINGS(text); -} - -BOOST_AUTO_TEST_CASE(function_overload_is_not_shadowing) -{ - char const* text = R"( - contract C { - function f() pure public {} - function f(uint) pure public {} - } - )"; - CHECK_SUCCESS_NO_WARNINGS(text); -} - -BOOST_AUTO_TEST_CASE(function_override_is_not_shadowing) -{ - char const* text = R"( - contract D { function f() pure public {} } - contract C is D { - function f(uint) pure public {} - } - )"; - CHECK_SUCCESS_NO_WARNINGS(text); -} - -BOOST_AUTO_TEST_CASE(event_parameter_cannot_shadow_state_variable) -{ - char const* text = R"( - contract C { - address a; - event E(address a); - } - )"; - CHECK_SUCCESS_NO_WARNINGS(text); -} - -BOOST_AUTO_TEST_CASE(callable_crash) -{ - char const* text = R"( - contract C { - struct S { uint a; bool x; } - S public s; - function C() public { - 3({a: 1, x: true}); - } - } - )"; - CHECK_ERROR(text, TypeError, "Type is not callable"); -} - -BOOST_AUTO_TEST_CASE(error_transfer_non_payable_fallback) -{ - // This used to be a test for a.transfer to generate a warning - // because A's fallback function is not payable. - char const* text = R"( - contract A { - function() public {} - } - - contract B { - A a; - - function() public { - a.transfer(100); - } - } - )"; - CHECK_ALLOW_MULTI(text, (std::vector<std::pair<Error::Type, std::string>>{ - {Error::Type::Warning, "Using contract member \"transfer\" inherited from the address type is deprecated"}, - {Error::Type::TypeError, "Value transfer to a contract without a payable fallback function"} - })); -} - -BOOST_AUTO_TEST_CASE(error_transfer_no_fallback) -{ - // This used to be a test for a.transfer to generate a warning - // because A does not have a payable fallback function. - std::string text = R"( - contract A {} - - contract B { - A a; - - function() public { - a.transfer(100); - } - } - )"; - CHECK_ALLOW_MULTI(text, (std::vector<std::pair<Error::Type, std::string>>{ - {Error::Type::Warning, "Using contract member \"transfer\" inherited from the address type is deprecated"}, - {Error::Type::TypeError, "Value transfer to a contract without a payable fallback function"} - })); -} - -BOOST_AUTO_TEST_CASE(error_send_non_payable_fallback) -{ - // This used to be a test for a.send to generate a warning - // because A does not have a payable fallback function. - std::string text = R"( - contract A { - function() public {} - } - - contract B { - A a; - - function() public { - require(a.send(100)); - } - } - )"; - CHECK_ALLOW_MULTI(text, (std::vector<std::pair<Error::Type, std::string>>{ - {Error::Type::Warning, "Using contract member \"send\" inherited from the address type is deprecated"}, - {Error::Type::TypeError, "Value transfer to a contract without a payable fallback function"} - })); -} - -BOOST_AUTO_TEST_CASE(does_not_error_transfer_payable_fallback) -{ - // This used to be a test for a.transfer to generate a warning - // because A does not have a payable fallback function. - char const* text = R"( - contract A { - function() payable public {} - } - - contract B { - A a; - - function() public { - a.transfer(100); - } - } - )"; - CHECK_WARNING(text, "Using contract member \"transfer\" inherited from the address type is deprecated."); -} - -BOOST_AUTO_TEST_CASE(does_not_error_transfer_regular_function) -{ - char const* text = R"( - contract A { - function transfer() pure public {} - } - - contract B { - A a; - - function() public { - a.transfer(); - } - } - )"; - CHECK_SUCCESS_NO_WARNINGS(text); -} - BOOST_AUTO_TEST_CASE(returndatasize_as_variable) { char const* text = R"( - contract c { function f() public { uint returndatasize; assembly { returndatasize }}} + contract C { function f() public pure { uint returndatasize; returndatasize; assembly { pop(returndatasize()) }}} )"; vector<pair<Error::Type, std::string>> expectations(vector<pair<Error::Type, std::string>>{ - {Error::Type::Warning, "Variable is shadowed in inline assembly by an instruction of the same name"}, - {Error::Type::Warning, "The use of non-functional instructions is deprecated."}, - {Error::Type::DeclarationError, "Unbalanced stack"} + {Error::Type::Warning, "Variable is shadowed in inline assembly by an instruction of the same name"} }); if (!dev::test::Options::get().evmVersion().supportsReturndata()) - expectations.emplace_back(make_pair(Error::Type::Warning, std::string("\"returndatasize\" instruction is only available for Byzantium-compatible"))); + expectations.emplace_back(make_pair(Error::Type::Warning, std::string("\"returndatasize\" instruction is only available for Byzantium-compatible VMs."))); CHECK_ALLOW_MULTI(text, expectations); } BOOST_AUTO_TEST_CASE(create2_as_variable) { char const* text = R"( - contract c { function f() public { uint create2; assembly { create2(0, 0, 0, 0) } }} - )"; - CHECK_ALLOW_MULTI(text, (std::vector<std::pair<Error::Type, std::string>>{ - {Error::Type::Warning, "Variable is shadowed in inline assembly by an instruction of the same name"}, - {Error::Type::Warning, "The \"create2\" instruction is not supported by the VM version"}, - {Error::Type::DeclarationError, "Unbalanced stack"}, - {Error::Type::Warning, "not supposed to return values"} - })); -} - -BOOST_AUTO_TEST_CASE(warn_unspecified_storage) -{ - char const* text = R"( - contract C { - struct S { uint a; string b; } - S x; - function f() view public { - S storage y = x; - y; - } - } - )"; - CHECK_SUCCESS_NO_WARNINGS(text); - text = R"( - contract C { - struct S { uint a; } - S x; - function f() view public { - S y = x; - y; - } - } - )"; - CHECK_WARNING(text, "Variable is declared as a storage pointer. Use an explicit \"storage\" keyword to silence this warning"); - text = R"( - pragma experimental "v0.5.0"; - contract C { - struct S { uint a; } - S x; - function f() view public { - S y = x; - y; - } - } - )"; - CHECK_ERROR(text, TypeError, "Storage location must be specified as either \"memory\" or \"storage\"."); -} - -BOOST_AUTO_TEST_CASE(storage_location_non_array_or_struct_disallowed) -{ - char const* text = R"( - contract C { - function f(uint storage a) public { } - } + contract c { function f() public { uint create2; create2; assembly { pop(create2(0, 0, 0, 0)) } }} )"; - CHECK_ERROR(text, TypeError, "Storage location can only be given for array or struct types."); -} - -BOOST_AUTO_TEST_CASE(storage_location_non_array_or_struct_disallowed_is_not_fatal) -{ - char const* text = R"( - contract C { - function f(uint storage a) public { - a = f; - } - } - )"; - CHECK_ERROR_ALLOW_MULTI(text, TypeError, (std::vector<std::string>{"Storage location can only be given for array or struct types."})); -} - -BOOST_AUTO_TEST_CASE(implicit_conversion_disallowed) -{ - char const* text = R"( - contract C { - function f() public returns (bytes4) { - uint32 tmp = 1; - return tmp; - } - } - )"; - CHECK_ERROR(text, TypeError, "Return argument type uint32 is not implicitly convertible to expected type (type of first return variable) bytes4."); -} - -BOOST_AUTO_TEST_CASE(too_large_arrays_for_calldata) -{ - char const* text = R"( - contract C { - function f(uint[85678901234] a) pure external { - } - } - )"; - CHECK_ERROR(text, TypeError, "Array is too large to be encoded."); - text = R"( - contract C { - function f(uint[85678901234] a) pure internal { - } - } - )"; - CHECK_ERROR(text, TypeError, "Array is too large to be encoded."); - text = R"( - contract C { - function f(uint[85678901234] a) pure public { - } - } - )"; - CHECK_ERROR(text, TypeError, "Array is too large to be encoded."); -} - -BOOST_AUTO_TEST_CASE(explicit_literal_to_storage_string) -{ - char const* text = R"( - contract C { - function f() pure public { - string memory x = "abc"; - x; - } - } - )"; - CHECK_SUCCESS_NO_WARNINGS(text); - text = R"( - contract C { - function f() pure public { - string storage x = "abc"; - } - } - )"; - CHECK_ERROR(text, TypeError, "Type literal_string \"abc\" is not implicitly convertible to expected type string storage pointer."); - text = R"( - contract C { - function f() pure public { - string x = "abc"; - } - } - )"; - CHECK_ERROR(text, TypeError, "Type literal_string \"abc\" is not implicitly convertible to expected type string storage pointer."); - text = R"( - contract C { - function f() pure public { - string("abc"); - } - } - )"; - CHECK_ERROR(text, TypeError, "Explicit type conversion not allowed from \"literal_string \"abc\"\" to \"string storage pointer\""); -} - -BOOST_AUTO_TEST_CASE(modifiers_access_storage_pointer) -{ - char const* text = R"( - contract C { - struct S { uint a; } - modifier m(S storage x) { - x; - _; - } - } - )"; - CHECK_SUCCESS_NO_WARNINGS(text); -} - -BOOST_AUTO_TEST_CASE(function_types_sig) -{ - char const* text = R"( - contract C { - function f() view returns (bytes4) { - return f.selector; - } - } - )"; - CHECK_ERROR(text, TypeError, "Member \"selector\" not found"); - text = R"( - contract C { - function g() pure internal { - } - function f() view returns (bytes4) { - return g.selector; - } - } - )"; - CHECK_ERROR(text, TypeError, "Member \"selector\" not found"); - text = R"( - contract C { - function f() view returns (bytes4) { - function () g; - return g.selector; - } - } - )"; - CHECK_ERROR(text, TypeError, "Member \"selector\" not found"); - text = R"( - contract C { - function f() pure external returns (bytes4) { - return this.f.selector; - } - } - )"; - CHECK_SUCCESS_NO_WARNINGS(text); - text = R"( - contract C { - function h() pure external { - } - function f() view external returns (bytes4) { - var g = this.h; - return g.selector; - } - } - )"; - CHECK_WARNING(text, "Use of the \"var\" keyword is deprecated."); - text = R"( - contract C { - function h() pure external { - } - function f() view external returns (bytes4) { - function () pure external g = this.h; - return g.selector; - } - } - )"; - CHECK_SUCCESS_NO_WARNINGS(text); - text = R"( - contract C { - function h() pure external { - } - function f() view external returns (bytes4) { - function () pure external g = this.h; - var i = g; - return i.selector; - } - } - )"; - CHECK_WARNING(text, "Use of the \"var\" keyword is deprecated."); -} - -BOOST_AUTO_TEST_CASE(using_this_in_constructor) -{ - char const* text = R"( - contract C { - constructor() public { - this.f(); - } - function f() pure public { - } - } - )"; - CHECK_WARNING(text, "\"this\" used in constructor"); + // This needs special treatment, because the message mentions the EVM version, + // so cannot be run via isoltest. + vector<pair<Error::Type, std::string>> expectations(vector<pair<Error::Type, std::string>>{ + {Error::Type::Warning, "Variable is shadowed in inline assembly by an instruction of the same name"} + }); + if (!dev::test::Options::get().evmVersion().hasCreate2()) + expectations.emplace_back(make_pair(Error::Type::Warning, std::string("\"create2\" instruction is only available for Constantinople-compatible VMs."))); + CHECK_ALLOW_MULTI(text, expectations); } -BOOST_AUTO_TEST_CASE(do_not_crash_on_not_lvalue) +BOOST_AUTO_TEST_CASE(extcodehash_as_variable) { - // This checks for a bug that caused a crash because of continued analysis. char const* text = R"( - contract C { - mapping (uint => uint) m; - function f() public { - m(1) = 2; - } - } + contract c { function f() public view { uint extcodehash; extcodehash; assembly { pop(extcodehash(0)) } }} )"; - CHECK_ERROR_ALLOW_MULTI(text, TypeError, (std::vector<std::string>{ - "is not callable", - "Expression has to be an lvalue", - "Type int_const 2 is not implicitly" + // This needs special treatment, because the message mentions the EVM version, + // so cannot be run via isoltest. + CHECK_ALLOW_MULTI(text, (std::vector<std::pair<Error::Type, std::string>>{ + {Error::Type::Warning, "Variable is shadowed in inline assembly by an instruction of the same name"}, + {Error::Type::Warning, "The \"extcodehash\" instruction is not supported by the VM version"}, })); } -BOOST_AUTO_TEST_CASE(builtin_reject_gas) -{ - char const* text = R"( - contract C { - function f() public { - keccak256.gas(); - } - } - )"; - CHECK_ERROR(text, TypeError, "Member \"gas\" not found or not visible after argument-dependent lookup"); - text = R"( - contract C { - function f() public { - sha256.gas(); - } - } - )"; - CHECK_ERROR(text, TypeError, "Member \"gas\" not found or not visible after argument-dependent lookup"); - text = R"( - contract C { - function f() public { - ripemd160.gas(); - } - } - )"; - CHECK_ERROR(text, TypeError, "Member \"gas\" not found or not visible after argument-dependent lookup"); - text = R"( - contract C { - function f() public { - ecrecover.gas(); - } - } - )"; - CHECK_ERROR(text, TypeError, "Member \"gas\" not found or not visible after argument-dependent lookup"); -} - -BOOST_AUTO_TEST_CASE(gasleft) +BOOST_AUTO_TEST_CASE(getter_is_memory_type) { char const* text = R"( contract C { - function f() public view returns (uint256 val) { return msg.gas; } - } - )"; - CHECK_WARNING(text, "\"msg.gas\" has been deprecated in favor of \"gasleft()\""); - - text = R"( - contract C { - function f() public view returns (uint256 val) { return gasleft(); } + struct S { string m; } + string[] public x; + S[] public y; } )"; CHECK_SUCCESS_NO_WARNINGS(text); - - text = R"( - pragma experimental "v0.5.0"; - contract C { - function f() public returns (uint256 val) { return msg.gas; } - } - )"; - CHECK_ERROR(text, TypeError, "\"msg.gas\" has been deprecated in favor of \"gasleft()\""); -} - -BOOST_AUTO_TEST_CASE(gasleft_shadowing) -{ - char const* text = R"( - contract C { - function gasleft() public pure returns (bytes32 val) { return "abc"; } - function f() public pure returns (bytes32 val) { return gasleft(); } - } - )"; - CHECK_WARNING(text, "This declaration shadows a builtin symbol."); - - text = R"( - contract C { - uint gasleft; - function f() public { gasleft = 42; } - } - )"; - CHECK_WARNING(text, "This declaration shadows a builtin symbol."); + // Check that the getters return a memory strings, not a storage strings. + ContractDefinition const& c = dynamic_cast<ContractDefinition const&>(*m_compiler.ast("").nodes().at(1)); + BOOST_CHECK(c.interfaceFunctions().size() == 2); + for (auto const& f: c.interfaceFunctions()) + { + auto const& retType = f.second->returnParameterTypes().at(0); + BOOST_CHECK(retType->dataStoredIn(DataLocation::Memory)); + } } -BOOST_AUTO_TEST_CASE(builtin_reject_value) +BOOST_AUTO_TEST_CASE(address_staticcall) { - char const* text = R"( - contract C { - function f() public { - keccak256.value(); - } - } - )"; - CHECK_ERROR(text, TypeError, "Member \"value\" not found or not visible after argument-dependent lookup"); - text = R"( - contract C { - function f() public { - sha256.value(); - } - } - )"; - CHECK_ERROR(text, TypeError, "Member \"value\" not found or not visible after argument-dependent lookup"); - text = R"( - contract C { - function f() public { - ripemd160.value(); - } - } - )"; - CHECK_ERROR(text, TypeError, "Member \"value\" not found or not visible after argument-dependent lookup"); - text = R"( + char const* sourceCode = R"( contract C { - function f() public { - ecrecover.value(); + function f() public view returns(bool) { + (bool success,) = address(0x4242).staticcall(""); + return success; } } )"; - CHECK_ERROR(text, TypeError, "Member \"value\" not found or not visible after argument-dependent lookup"); -} -BOOST_AUTO_TEST_CASE(large_storage_array_fine) -{ - char const* text = R"( - contract C { - uint[2**64 - 1] x; - } - )"; - CHECK_SUCCESS_NO_WARNINGS(text); -} - -BOOST_AUTO_TEST_CASE(large_storage_array_simple) -{ - char const* text = R"( - contract C { - uint[2**64] x; - } - )"; - CHECK_WARNING(text, "covers a large part of storage and thus makes collisions likely"); -} - -BOOST_AUTO_TEST_CASE(large_storage_arrays_combined) -{ - char const* text = R"( - contract C { - uint[200][200][2**30][][2**30] x; - } - )"; - CHECK_WARNING(text, "covers a large part of storage and thus makes collisions likely"); -} - -BOOST_AUTO_TEST_CASE(large_storage_arrays_struct) -{ - char const* text = R"( - contract C { - struct S { uint[2**30] x; uint[2**50] y; } - S[2**20] x; - } - )"; - CHECK_WARNING(text, "covers a large part of storage and thus makes collisions likely"); -} - -BOOST_AUTO_TEST_CASE(large_storage_array_mapping) -{ - char const* text = R"( - contract C { - mapping(uint => uint[2**100]) x; - } - )"; - CHECK_WARNING(text, "covers a large part of storage and thus makes collisions likely"); -} - -BOOST_AUTO_TEST_CASE(library_function_without_implementation) -{ - char const* text = R"( - library L { - function f() public; - } - )"; - CHECK_SUCCESS_NO_WARNINGS(text); - text = R"( - library L { - function f() internal; - } - )"; - CHECK_ERROR(text, TypeError, "Internal library function must be implemented if declared."); - text = R"( - library L { - function f() private; - } - )"; - CHECK_ERROR(text, TypeError, "Internal library function must be implemented if declared."); + if (dev::test::Options::get().evmVersion().hasStaticCall()) + CHECK_SUCCESS_NO_WARNINGS(sourceCode); + else + CHECK_ERROR(sourceCode, TypeError, "\"staticcall\" is not supported by the VM version."); } -BOOST_AUTO_TEST_CASE(using_for_with_non_library) +BOOST_AUTO_TEST_CASE(address_staticcall_value) { - // This tests a crash that was resolved by making the first error fatal. - char const* text = R"( - library L { - struct S { uint d; } - using S for S; - function f(S _s) internal { - _s.d = 1; + if (dev::test::Options::get().evmVersion().hasStaticCall()) + { + char const* sourceCode = R"( + contract C { + function f() public view { + address(0x4242).staticcall.value; + } } - } - )"; - CHECK_ERROR(text, TypeError, "Library name expected."); -} - -BOOST_AUTO_TEST_CASE(experimental_pragma) -{ - char const* text = R"( - pragma experimental; - )"; - CHECK_ERROR(text, SyntaxError, "Experimental feature name is missing."); - text = R"( - pragma experimental 123; - )"; - CHECK_ERROR(text, SyntaxError, "Unsupported experimental feature name."); - text = R"( - pragma experimental unsupportedName; - )"; - CHECK_ERROR(text, SyntaxError, "Unsupported experimental feature name."); - text = R"( - pragma experimental "unsupportedName"; - )"; - CHECK_ERROR(text, SyntaxError, "Unsupported experimental feature name."); - text = R"( - pragma experimental ""; - )"; - CHECK_ERROR(text, SyntaxError, "Empty experimental feature name is invalid."); - text = R"( - pragma experimental unsupportedName unsupportedName; - )"; - CHECK_ERROR(text, SyntaxError, "Stray arguments."); - text = R"( - pragma experimental __test; - )"; - CHECK_WARNING(text, "Experimental features are turned on. Do not use experimental features on live deployments."); - text = R"( - pragma experimental __test; - pragma experimental __test; - )"; - CHECK_ERROR_ALLOW_MULTI(text, SyntaxError, (std::vector<std::string>{"Duplicate experimental feature name."})); + )"; + CHECK_ERROR(sourceCode, TypeError, "Member \"value\" not found or not visible after argument-dependent lookup"); + } } -BOOST_AUTO_TEST_CASE(reject_interface_creation) +BOOST_AUTO_TEST_CASE(address_call_full_return_type) { - char const* text = R"( - interface I {} + char const* sourceCode = R"( contract C { function f() public { - new I(); + (bool success, bytes memory m) = address(0x4242).call(""); + success; m; } } )"; - CHECK_ERROR(text, TypeError, "Cannot instantiate an interface."); -} -BOOST_AUTO_TEST_CASE(accept_library_creation) -{ - char const* text = R"( - library L {} - contract C { - function f() public { - new L(); - } - } - )"; - CHECK_SUCCESS(text); -} - -BOOST_AUTO_TEST_CASE(reject_interface_constructors) -{ - char const* text = R"( - interface I {} - contract C is I(2) {} - )"; - CHECK_ERROR(text, TypeError, "Wrong argument count for constructor call: 1 arguments given but expected 0."); -} - -BOOST_AUTO_TEST_CASE(non_external_fallback) -{ - char const* text = R"( - pragma experimental "v0.5.0"; - contract C { - function () external { } - } - )"; - CHECK_SUCCESS_NO_WARNINGS(text); - text = R"( - pragma experimental "v0.5.0"; - contract C { - function () internal { } - } - )"; - CHECK_ERROR(text, TypeError, "Fallback function must be defined as \"external\"."); - text = R"( - pragma experimental "v0.5.0"; - contract C { - function () private { } - } - )"; - CHECK_ERROR(text, TypeError, "Fallback function must be defined as \"external\"."); - text = R"( - pragma experimental "v0.5.0"; - contract C { - function () public { } - } - )"; - CHECK_ERROR(text, TypeError, "Fallback function must be defined as \"external\"."); -} - -BOOST_AUTO_TEST_CASE(invalid_literal_in_tuple) -{ - char const* text = R"( - contract C { - function f() pure public { - uint x; - (x, ) = (1E111); - } - } - )"; - CHECK_ERROR(text, TypeError, "is not implicitly convertible to expected type"); - text = R"( - contract C { - function f() pure public { - uint x; - (x, ) = (1, 1E111); - } - } - )"; - CHECK_ERROR(text, TypeError, "Invalid rational number."); - text = R"( - contract C { - function f() pure public { - uint x; - (x, ) = (1E111, 1); - } - } - )"; - CHECK_ERROR(text, TypeError, "Invalid rational number."); - text = R"( - contract C { - function f() pure public { - (2**270, 1); - } - } - )"; - CHECK_ERROR(text, TypeError, "Invalid rational number."); - text = R"( - contract C { - function f() pure public { - ((2**270) / 2**100, 1); - } - } - )"; - CHECK_SUCCESS(text); -} - -BOOST_AUTO_TEST_CASE(address_overload_resolution) -{ - char const* text = R"( - contract C { - function balance() returns (uint) { - this.balance; // to avoid pureness warning - return 1; - } - function transfer(uint amount) { - address(this).transfer(amount); // to avoid pureness warning - } - } - contract D { - function f() { - var x = (new C()).balance(); - x; - (new C()).transfer(5); - } - } - )"; - CHECK_SUCCESS(text); -} - -BOOST_AUTO_TEST_CASE(array_length_invalid_expression) -{ - char const* text = R"( - contract C { - uint[-true] ids; - } - )"; - CHECK_ERROR(text, TypeError, "Invalid array length, expected integer literal or constant expression."); - text = R"( - contract C { - uint[true/1] ids; - } - )"; - CHECK_ERROR(text, TypeError, "Invalid array length, expected integer literal or constant expression."); - text = R"( - contract C { - uint[1/true] ids; - } - )"; - CHECK_ERROR(text, TypeError, "Invalid array length, expected integer literal or constant expression."); - text = R"( - contract C { - uint[1.111111E1111111111111] ids; - } - )"; - CHECK_ERROR(text, TypeError, "Invalid array length, expected integer literal or constant expression."); - text = R"( - contract C { - uint[3/0] ids; - } - )"; - CHECK_ERROR(text, TypeError, "Operator / not compatible with types int_const 3 and int_const 0"); -} - -BOOST_AUTO_TEST_CASE(warn_about_address_members_on_contract) -{ - std::string text = R"( - contract C { - function f() view public { - this.balance; - } - } - )"; - CHECK_WARNING(text, "Using contract member \"balance\" inherited from the address type is deprecated."); - text = R"( - contract C { - function f() view public { - this.transfer; - } - } - )"; - CHECK_ALLOW_MULTI(text, (vector<pair<Error::Type, std::string>>{ - {Error::Type::Warning, "Using contract member \"transfer\" inherited from the address type is deprecated"}, - {Error::Type::TypeError, "Value transfer to a contract without a payable fallback function"} - })); - text = R"( - contract C { - function f() view public { - this.send; - } - } - )"; - CHECK_ALLOW_MULTI(text, (vector<pair<Error::Type, std::string>>{ - {Error::Type::Warning, "Using contract member \"send\" inherited from the address type is deprecated"}, - {Error::Type::TypeError, "Value transfer to a contract without a payable fallback function"} - })); - text = R"( - contract C { - function f() view public { - this.call; - } - } - )"; - CHECK_WARNING(text, "Using contract member \"call\" inherited from the address type is deprecated."); - text = R"( - contract C { - function f() view public { - this.callcode; - } - } - )"; - CHECK_ALLOW_MULTI(text, (vector<pair<Error::Type, std::string>>{ - {Error::Type::Warning, "Using contract member \"callcode\" inherited from the address type is deprecated"}, - {Error::Type::Warning, "\"callcode\" has been deprecated in favour of \"delegatecall\""} - })); - text = R"( - contract C { - function f() view public { - this.delegatecall; - } - } - )"; - CHECK_WARNING(text, "Using contract member \"delegatecall\" inherited from the address type is deprecated."); -} - -BOOST_AUTO_TEST_CASE(warn_about_address_members_on_non_this_contract) -{ - std::string text = R"( - contract C { - function f() view public { - C c; - c.balance; - } - } - )"; - CHECK_WARNING(text, "Using contract member \"balance\" inherited from the address type is deprecated"); - text = R"( - contract C { - function f() view public { - C c; - c.transfer; - } - } - )"; - CHECK_ALLOW_MULTI(text, (vector<pair<Error::Type, std::string>>{ - {Error::Type::Warning, "Using contract member \"transfer\" inherited from the address type is deprecated"}, - {Error::Type::TypeError, "Value transfer to a contract without a payable fallback function"} - })); - text = R"( - contract C { - function f() view public { - C c; - c.send; - } - } - )"; - CHECK_ALLOW_MULTI(text, (vector<pair<Error::Type, std::string>>{ - {Error::Type::Warning, "Using contract member \"send\" inherited from the address type is deprecated"}, - {Error::Type::TypeError, "Value transfer to a contract without a payable fallback function"} - })); - text = R"( - contract C { - function f() pure public { - C c; - c.call; - } - } - )"; - CHECK_WARNING(text, "Using contract member \"call\" inherited from the address type is deprecated"); - text = R"( - contract C { - function f() pure public { - C c; - c.callcode; - } - } - )"; - CHECK_WARNING_ALLOW_MULTI(text, (std::vector<std::string>{ - "Using contract member \"callcode\" inherited from the address type is deprecated", - "\"callcode\" has been deprecated in favour of \"delegatecall\"" - })); - text = R"( - contract C { - function f() pure public { - C c; - c.delegatecall; - } - } - )"; - CHECK_WARNING(text, "Using contract member \"delegatecall\" inherited from the address type is deprecated"); -} - -BOOST_AUTO_TEST_CASE(no_address_members_on_contract) -{ - char const* text = R"( - pragma experimental "v0.5.0"; - contract C { - function f() public { - this.balance; - } - } - )"; - CHECK_ERROR(text, TypeError, "Member \"balance\" not found or not visible after argument-dependent lookup in contract"); - text = R"( - pragma experimental "v0.5.0"; - contract C { - function f() public { - this.transfer; - } - } - )"; - CHECK_ERROR(text, TypeError, "Member \"transfer\" not found or not visible after argument-dependent lookup in contract"); - text = R"( - pragma experimental "v0.5.0"; - contract C { - function f() public { - this.send; - } - } - )"; - CHECK_ERROR(text, TypeError, "Member \"send\" not found or not visible after argument-dependent lookup in contract"); - text = R"( - pragma experimental "v0.5.0"; - contract C { - function f() public { - this.call; - } - } - )"; - CHECK_ERROR(text, TypeError, "Member \"call\" not found or not visible after argument-dependent lookup in contract"); - text = R"( - pragma experimental "v0.5.0"; - contract C { - function f() public { - this.callcode; - } - } - )"; - CHECK_ERROR(text, TypeError, "Member \"callcode\" not found or not visible after argument-dependent lookup in contract"); - text = R"( - pragma experimental "v0.5.0"; - contract C { - function f() public { - this.delegatecall; - } - } - )"; - CHECK_ERROR(text, TypeError, "Member \"delegatecall\" not found or not visible after argument-dependent lookup in contract"); + if (dev::test::Options::get().evmVersion().supportsReturndata()) + CHECK_SUCCESS_NO_WARNINGS(sourceCode); + else + CHECK_ERROR(sourceCode, TypeError, "Type inaccessible dynamic type is not implicitly convertible to expected type bytes memory."); } -BOOST_AUTO_TEST_CASE(no_warning_for_using_members_that_look_like_address_members) +BOOST_AUTO_TEST_CASE(address_delegatecall_full_return_type) { - char const* text = R"( - pragma experimental "v0.5.0"; + char const* sourceCode = R"( contract C { - function transfer(uint) public; function f() public { - this.transfer(10); + (bool success, bytes memory m) = address(0x4242).delegatecall(""); + success; m; } } )"; - CHECK_SUCCESS_NO_WARNINGS(text); -} -BOOST_AUTO_TEST_CASE(emit_events) -{ - char const* text = R"( - contract C { - event e(); - function f() public { - emit e(); - } - } - )"; - CHECK_SUCCESS_NO_WARNINGS(text); - text = R"( - contract C { - event e(uint a, string b); - function f() public { - emit e(2, "abc"); - emit e({b: "abc", a: 8}); - } - } - )"; - CHECK_SUCCESS_NO_WARNINGS(text); - text = R"( - contract A { event e(uint a, string b); } - contract C is A { - function f() public { - emit A.e(2, "abc"); - emit A.e({b: "abc", a: 8}); - } - } - )"; - CHECK_SUCCESS_NO_WARNINGS(text); + if (dev::test::Options::get().evmVersion().supportsReturndata()) + CHECK_SUCCESS_NO_WARNINGS(sourceCode); + else + CHECK_ERROR(sourceCode, TypeError, "Type inaccessible dynamic type is not implicitly convertible to expected type bytes memory."); } -BOOST_AUTO_TEST_CASE(old_style_events_050) -{ - char const* text = R"( - contract C { - event e(); - function f() public { - e(); - } - } - )"; - CHECK_WARNING(text, "without \"emit\" prefix"); - text = R"( - pragma experimental "v0.5.0"; - contract C { - event e(); - function f() public { - e(); - } - } - )"; - CHECK_ERROR(text, TypeError, "have to be prefixed"); -} -BOOST_AUTO_TEST_CASE(getter_is_memory_type) +BOOST_AUTO_TEST_CASE(address_staticcall_full_return_type) { - char const* text = R"( - contract C { - struct S { string m; } - string[] public x; - S[] public y; - } - )"; - CHECK_SUCCESS_NO_WARNINGS(text); - // Check that the getters return a memory strings, not a storage strings. - ContractDefinition const& c = dynamic_cast<ContractDefinition const&>(*m_compiler.ast("").nodes().at(1)); - BOOST_CHECK(c.interfaceFunctions().size() == 2); - for (auto const& f: c.interfaceFunctions()) + if (dev::test::Options::get().evmVersion().hasStaticCall()) { - auto const& retType = f.second->returnParameterTypes().at(0); - BOOST_CHECK(retType->dataStoredIn(DataLocation::Memory)); - } -} - -BOOST_AUTO_TEST_CASE(require_visibility_specifiers) -{ - char const* text = R"( - contract C { - function f() pure { } - } - )"; - CHECK_WARNING(text, "No visibility specified. Defaulting to"); - text = R"( - pragma experimental "v0.5.0"; - contract C { - function f() pure { } - } - )"; - CHECK_ERROR(text, SyntaxError, "No visibility specified."); -} - -BOOST_AUTO_TEST_CASE(blockhash) -{ - char const* code = R"( - contract C { - function f() public view returns (bytes32) { - return block.blockhash(3); + char const* sourceCode = R"( + contract C { + function f() public view { + (bool success, bytes memory m) = address(0x4242).staticcall(""); + success; m; + } } - } - )"; - CHECK_WARNING(code, "\"block.blockhash()\" has been deprecated in favor of \"blockhash()\""); + )"; - code = R"( - contract C { - function f() public view returns (bytes32) { return blockhash(3); } - } - )"; - CHECK_SUCCESS_NO_WARNINGS(code); - - code = R"( - pragma experimental "v0.5.0"; - contract C { - function f() public returns (bytes32) { return block.blockhash(3); } - } - )"; - CHECK_ERROR(code, TypeError, "\"block.blockhash()\" has been deprecated in favor of \"blockhash()\""); + CHECK_SUCCESS_NO_WARNINGS(sourceCode); + } } BOOST_AUTO_TEST_SUITE_END() diff --git a/test/libsolidity/SolidityNatspecJSON.cpp b/test/libsolidity/SolidityNatspecJSON.cpp index eeebeb74..d77050cb 100644 --- a/test/libsolidity/SolidityNatspecJSON.cpp +++ b/test/libsolidity/SolidityNatspecJSON.cpp @@ -41,6 +41,7 @@ public: void checkNatspec( std::string const& _code, + std::string const& _contractName, std::string const& _expectedDocumentationString, bool _userDocumentation ) @@ -52,9 +53,9 @@ public: Json::Value generatedDocumentation; if (_userDocumentation) - generatedDocumentation = m_compilerStack.natspecUser(m_compilerStack.lastContractName()); + generatedDocumentation = m_compilerStack.natspecUser(_contractName); else - generatedDocumentation = m_compilerStack.natspecDev(m_compilerStack.lastContractName()); + generatedDocumentation = m_compilerStack.natspecDev(_contractName); Json::Value expectedDocumentation; jsonParseStrict(_expectedDocumentationString, expectedDocumentation); BOOST_CHECK_MESSAGE( @@ -84,7 +85,7 @@ BOOST_AUTO_TEST_CASE(user_basic_test) char const* sourceCode = R"( contract test { /// @notice Multiplies `a` by 7 - function mul(uint a) returns(uint d) { return a * 7; } + function mul(uint a) public returns(uint d) { return a * 7; } } )"; @@ -93,7 +94,7 @@ BOOST_AUTO_TEST_CASE(user_basic_test) " \"mul(uint256)\":{ \"notice\": \"Multiplies `a` by 7\"}" "}}"; - checkNatspec(sourceCode, natspec, true); + checkNatspec(sourceCode, "test", natspec, true); } BOOST_AUTO_TEST_CASE(dev_and_user_basic_test) @@ -102,7 +103,7 @@ BOOST_AUTO_TEST_CASE(dev_and_user_basic_test) contract test { /// @notice Multiplies `a` by 7 /// @dev Multiplies a number by 7 - function mul(uint a) returns(uint d) { return a * 7; } + function mul(uint a) public returns (uint d) { return a * 7; } } )"; @@ -119,8 +120,8 @@ BOOST_AUTO_TEST_CASE(dev_and_user_basic_test) " \"mul(uint256)\":{ \"notice\": \"Multiplies `a` by 7\"}" "}}"; - checkNatspec(sourceCode, devNatspec, false); - checkNatspec(sourceCode, userNatspec, true); + checkNatspec(sourceCode, "test", devNatspec, false); + checkNatspec(sourceCode, "test", userNatspec, true); } BOOST_AUTO_TEST_CASE(user_multiline_comment) @@ -129,7 +130,7 @@ BOOST_AUTO_TEST_CASE(user_multiline_comment) contract test { /// @notice Multiplies `a` by 7 /// and then adds `b` - function mul_and_add(uint a, uint256 b) returns(uint256 d) { + function mul_and_add(uint a, uint256 b) public returns (uint256 d) { return (a * 7) + b; } } @@ -140,7 +141,7 @@ BOOST_AUTO_TEST_CASE(user_multiline_comment) " \"mul_and_add(uint256,uint256)\":{ \"notice\": \"Multiplies `a` by 7 and then adds `b`\"}" "}}"; - checkNatspec(sourceCode, natspec, true); + checkNatspec(sourceCode, "test", natspec, true); } BOOST_AUTO_TEST_CASE(user_multiple_functions) @@ -148,17 +149,17 @@ BOOST_AUTO_TEST_CASE(user_multiple_functions) char const* sourceCode = R"( contract test { /// @notice Multiplies `a` by 7 and then adds `b` - function mul_and_add(uint a, uint256 b) returns(uint256 d) { + function mul_and_add(uint a, uint256 b) public returns (uint256 d) { return (a * 7) + b; } /// @notice Divides `input` by `div` - function divide(uint input, uint div) returns(uint d) { + function divide(uint input, uint div) public returns (uint d) { return input / div; } /// @notice Subtracts 3 from `input` - function sub(int input) returns(int d) { + function sub(int input) public returns (int d) { return input - 3; } } @@ -171,7 +172,7 @@ BOOST_AUTO_TEST_CASE(user_multiple_functions) " \"sub(int256)\":{ \"notice\": \"Subtracts 3 from `input`\"}" "}}"; - checkNatspec(sourceCode, natspec, true); + checkNatspec(sourceCode, "test", natspec, true); } BOOST_AUTO_TEST_CASE(user_empty_contract) @@ -182,17 +183,17 @@ BOOST_AUTO_TEST_CASE(user_empty_contract) char const* natspec = "{\"methods\":{} }"; - checkNatspec(sourceCode, natspec, true); + checkNatspec(sourceCode, "test", natspec, true); } BOOST_AUTO_TEST_CASE(dev_and_user_no_doc) { char const* sourceCode = R"( contract test { - function mul(uint a) returns(uint d) { + function mul(uint a) public returns (uint d) { return a * 7; } - function sub(int input) returns(int d) { + function sub(int input) public returns (int d) { return input - 3; } } @@ -201,8 +202,8 @@ BOOST_AUTO_TEST_CASE(dev_and_user_no_doc) char const* devNatspec = "{\"methods\":{}}"; char const* userNatspec = "{\"methods\":{}}"; - checkNatspec(sourceCode, devNatspec, false); - checkNatspec(sourceCode, userNatspec, true); + checkNatspec(sourceCode, "test", devNatspec, false); + checkNatspec(sourceCode, "test", userNatspec, true); } BOOST_AUTO_TEST_CASE(dev_desc_after_nl) @@ -213,7 +214,7 @@ BOOST_AUTO_TEST_CASE(dev_desc_after_nl) /// Multiplies a number by 7 and adds second parameter /// @param a Documentation for the first parameter /// @param second Documentation for the second parameter - function mul(uint a, uint second) returns(uint d) { return a * 7 + second; } + function mul(uint a, uint second) public returns (uint d) { return a * 7 + second; } } )"; @@ -228,7 +229,7 @@ BOOST_AUTO_TEST_CASE(dev_desc_after_nl) " }\n" "}}"; - checkNatspec(sourceCode, natspec, false); + checkNatspec(sourceCode, "test", natspec, false); } BOOST_AUTO_TEST_CASE(dev_multiple_params) @@ -238,7 +239,7 @@ BOOST_AUTO_TEST_CASE(dev_multiple_params) /// @dev Multiplies a number by 7 and adds second parameter /// @param a Documentation for the first parameter /// @param second Documentation for the second parameter - function mul(uint a, uint second) returns(uint d) { return a * 7 + second; } + function mul(uint a, uint second) public returns (uint d) { return a * 7 + second; } } )"; @@ -253,7 +254,7 @@ BOOST_AUTO_TEST_CASE(dev_multiple_params) " }\n" "}}"; - checkNatspec(sourceCode, natspec, false); + checkNatspec(sourceCode, "test", natspec, false); } BOOST_AUTO_TEST_CASE(dev_multiple_params_mixed_whitespace) @@ -262,7 +263,7 @@ BOOST_AUTO_TEST_CASE(dev_multiple_params_mixed_whitespace) " /// @dev Multiplies a number by 7 and adds second parameter\n" " /// @param a Documentation for the first parameter\n" " /// @param second Documentation for the second parameter\n" - " function mul(uint a, uint second) returns(uint d) { return a * 7 + second; }\n" + " function mul(uint a, uint second) public returns (uint d) { return a * 7 + second; }\n" "}\n"; char const* natspec = "{" @@ -276,7 +277,7 @@ BOOST_AUTO_TEST_CASE(dev_multiple_params_mixed_whitespace) " }\n" "}}"; - checkNatspec(sourceCode, natspec, false); + checkNatspec(sourceCode, "test", natspec, false); } BOOST_AUTO_TEST_CASE(dev_mutiline_param_description) @@ -287,7 +288,7 @@ BOOST_AUTO_TEST_CASE(dev_mutiline_param_description) /// @param a Documentation for the first parameter starts here. /// Since it's a really complicated parameter we need 2 lines /// @param second Documentation for the second parameter - function mul(uint a, uint second) returns(uint d) { return a * 7 + second; } + function mul(uint a, uint second) public returns (uint d) { return a * 7 + second; } } )"; @@ -302,7 +303,7 @@ BOOST_AUTO_TEST_CASE(dev_mutiline_param_description) " }\n" "}}"; - checkNatspec(sourceCode, natspec, false); + checkNatspec(sourceCode, "test", natspec, false); } BOOST_AUTO_TEST_CASE(dev_multiple_functions) @@ -312,18 +313,18 @@ BOOST_AUTO_TEST_CASE(dev_multiple_functions) /// @dev Multiplies a number by 7 and adds second parameter /// @param a Documentation for the first parameter /// @param second Documentation for the second parameter - function mul(uint a, uint second) returns(uint d) { + function mul(uint a, uint second) public returns (uint d) { return a * 7 + second; } /// @dev Divides 2 numbers /// @param input Documentation for the input parameter /// @param div Documentation for the div parameter - function divide(uint input, uint div) returns(uint d) { + function divide(uint input, uint div) public returns (uint d) { return input / div; } /// @dev Subtracts 3 from `input` /// @param input Documentation for the input parameter - function sub(int input) returns(int d) { + function sub(int input) public returns (int d) { return input - 3; } } @@ -353,7 +354,7 @@ BOOST_AUTO_TEST_CASE(dev_multiple_functions) " }\n" "}}"; - checkNatspec(sourceCode, natspec, false); + checkNatspec(sourceCode, "test", natspec, false); } BOOST_AUTO_TEST_CASE(dev_return) @@ -365,7 +366,7 @@ BOOST_AUTO_TEST_CASE(dev_return) /// Since it's a really complicated parameter we need 2 lines /// @param second Documentation for the second parameter /// @return The result of the multiplication - function mul(uint a, uint second) returns(uint d) { return a * 7 + second; } + function mul(uint a, uint second) public returns (uint d) { return a * 7 + second; } } )"; @@ -381,7 +382,7 @@ BOOST_AUTO_TEST_CASE(dev_return) " }\n" "}}"; - checkNatspec(sourceCode, natspec, false); + checkNatspec(sourceCode, "test", natspec, false); } BOOST_AUTO_TEST_CASE(dev_return_desc_after_nl) { @@ -393,7 +394,7 @@ BOOST_AUTO_TEST_CASE(dev_return_desc_after_nl) /// @param second Documentation for the second parameter /// @return /// The result of the multiplication - function mul(uint a, uint second) returns(uint d) { + function mul(uint a, uint second) public returns (uint d) { return a * 7 + second; } } @@ -411,7 +412,7 @@ BOOST_AUTO_TEST_CASE(dev_return_desc_after_nl) " }\n" "}}"; - checkNatspec(sourceCode, natspec, false); + checkNatspec(sourceCode, "test", natspec, false); } @@ -425,7 +426,7 @@ BOOST_AUTO_TEST_CASE(dev_multiline_return) /// @param second Documentation for the second parameter /// @return The result of the multiplication /// and cookies with nutella - function mul(uint a, uint second) returns(uint d) { + function mul(uint a, uint second) public returns (uint d) { return a * 7 + second; } } @@ -443,7 +444,7 @@ BOOST_AUTO_TEST_CASE(dev_multiline_return) " }\n" "}}"; - checkNatspec(sourceCode, natspec, false); + checkNatspec(sourceCode, "test", natspec, false); } BOOST_AUTO_TEST_CASE(dev_multiline_comment) @@ -458,7 +459,7 @@ BOOST_AUTO_TEST_CASE(dev_multiline_comment) * @return The result of the multiplication * and cookies with nutella */ - function mul(uint a, uint second) returns(uint d) { + function mul(uint a, uint second) public returns (uint d) { return a * 7 + second; } } @@ -476,7 +477,7 @@ BOOST_AUTO_TEST_CASE(dev_multiline_comment) " }\n" "}}"; - checkNatspec(sourceCode, natspec, false); + checkNatspec(sourceCode, "test", natspec, false); } BOOST_AUTO_TEST_CASE(dev_contract_no_doc) @@ -484,7 +485,7 @@ BOOST_AUTO_TEST_CASE(dev_contract_no_doc) char const* sourceCode = R"( contract test { /// @dev Mul function - function mul(uint a, uint second) returns(uint d) { return a * 7 + second; } + function mul(uint a, uint second) public returns (uint d) { return a * 7 + second; } } )"; @@ -496,7 +497,7 @@ BOOST_AUTO_TEST_CASE(dev_contract_no_doc) " }\n" "}"; - checkNatspec(sourceCode, natspec, false); + checkNatspec(sourceCode, "test", natspec, false); } BOOST_AUTO_TEST_CASE(dev_contract_doc) @@ -506,7 +507,7 @@ BOOST_AUTO_TEST_CASE(dev_contract_doc) /// @title Just a test contract contract test { /// @dev Mul function - function mul(uint a, uint second) returns(uint d) { return a * 7 + second; } + function mul(uint a, uint second) public returns (uint d) { return a * 7 + second; } } )"; @@ -520,7 +521,7 @@ BOOST_AUTO_TEST_CASE(dev_contract_doc) " }\n" "}"; - checkNatspec(sourceCode, natspec, false); + checkNatspec(sourceCode, "test", natspec, false); } BOOST_AUTO_TEST_CASE(dev_author_at_function) @@ -531,7 +532,7 @@ BOOST_AUTO_TEST_CASE(dev_author_at_function) contract test { /// @dev Mul function /// @author John Doe - function mul(uint a, uint second) returns(uint d) { return a * 7 + second; } + function mul(uint a, uint second) public returns (uint d) { return a * 7 + second; } } )"; @@ -546,7 +547,7 @@ BOOST_AUTO_TEST_CASE(dev_author_at_function) " }\n" "}"; - checkNatspec(sourceCode, natspec, false); + checkNatspec(sourceCode, "test", natspec, false); } BOOST_AUTO_TEST_CASE(natspec_notice_without_tag) @@ -554,7 +555,7 @@ BOOST_AUTO_TEST_CASE(natspec_notice_without_tag) char const* sourceCode = R"( contract test { /// I do something awesome - function mul(uint a) returns(uint d) { return a * 7; } + function mul(uint a) public returns (uint d) { return a * 7; } } )"; @@ -569,7 +570,7 @@ BOOST_AUTO_TEST_CASE(natspec_notice_without_tag) } )ABCDEF"; - checkNatspec(sourceCode, natspec, true); + checkNatspec(sourceCode, "test", natspec, true); } BOOST_AUTO_TEST_CASE(natspec_multiline_notice_without_tag) @@ -578,7 +579,7 @@ BOOST_AUTO_TEST_CASE(natspec_multiline_notice_without_tag) contract test { /// I do something awesome /// which requires two lines to explain - function mul(uint a) returns(uint d) { return a * 7; } + function mul(uint a) public returns (uint d) { return a * 7; } } )"; @@ -592,7 +593,7 @@ BOOST_AUTO_TEST_CASE(natspec_multiline_notice_without_tag) } )ABCDEF"; - checkNatspec(sourceCode, natspec, true); + checkNatspec(sourceCode, "test", natspec, true); } BOOST_AUTO_TEST_CASE(empty_comment) @@ -608,7 +609,7 @@ BOOST_AUTO_TEST_CASE(empty_comment) } )ABCDEF"; - checkNatspec(sourceCode, natspec, true); + checkNatspec(sourceCode, "test", natspec, true); } BOOST_AUTO_TEST_CASE(dev_title_at_function_error) @@ -619,7 +620,7 @@ BOOST_AUTO_TEST_CASE(dev_title_at_function_error) contract test { /// @dev Mul function /// @title I really should not be here - function mul(uint a, uint second) returns(uint d) { return a * 7 + second; } + function mul(uint a, uint second) public returns (uint d) { return a * 7 + second; } } )"; @@ -633,7 +634,7 @@ BOOST_AUTO_TEST_CASE(dev_documenting_nonexistent_param) /// @dev Multiplies a number by 7 and adds second parameter /// @param a Documentation for the first parameter /// @param not_existing Documentation for the second parameter - function mul(uint a, uint second) returns(uint d) { return a * 7 + second; } + function mul(uint a, uint second) public returns (uint d) { return a * 7 + second; } } )"; @@ -646,8 +647,8 @@ BOOST_AUTO_TEST_CASE(dev_documenting_no_paramname) contract test { /// @dev Multiplies a number by 7 and adds second parameter /// @param a Documentation for the first parameter - /// @param - function mul(uint a, uint second) returns(uint d) { return a * 7 + second; } + /// @param + function mul(uint a, uint second) public returns (uint d) { return a * 7 + second; } } )"; @@ -661,7 +662,7 @@ BOOST_AUTO_TEST_CASE(dev_documenting_no_paramname_end) /// @dev Multiplies a number by 7 and adds second parameter /// @param a Documentation for the first parameter /// @param se - function mul(uint a, uint second) returns(uint d) { return a * 7 + second; } + function mul(uint a, uint second) public returns (uint d) { return a * 7 + second; } } )"; @@ -674,14 +675,139 @@ BOOST_AUTO_TEST_CASE(dev_documenting_no_param_description) contract test { /// @dev Multiplies a number by 7 and adds second parameter /// @param a Documentation for the first parameter - /// @param second - function mul(uint a, uint second) returns(uint d) { return a * 7 + second; } + /// @param second + function mul(uint a, uint second) public returns (uint d) { return a * 7 + second; } } )"; expectNatspecError(sourceCode); } +BOOST_AUTO_TEST_CASE(user_constructor) +{ + char const *sourceCode = R"( + contract test { + /// @notice this is a really nice constructor + constructor(uint a, uint second) public { } + } + )"; + + char const *natspec = R"ABCDEF({ + "methods" : { + "constructor" : "this is a really nice constructor" + } + })ABCDEF"; + + checkNatspec(sourceCode, "test", natspec, true); +} + +BOOST_AUTO_TEST_CASE(user_constructor_and_function) +{ + char const *sourceCode = R"( + contract test { + /// @notice this is a really nice constructor + constructor(uint a, uint second) public { } + /// another multiplier + function mul(uint a, uint second) public returns(uint d) { return a * 7 + second; } + } + )"; + + char const *natspec = R"ABCDEF({ + "methods" : { + "mul(uint256,uint256)" : { + "notice" : "another multiplier" + }, + "constructor" : "this is a really nice constructor" + } + })ABCDEF"; + + checkNatspec(sourceCode, "test", natspec, true); +} + +BOOST_AUTO_TEST_CASE(dev_constructor) +{ + char const *sourceCode = R"( + contract test { + /// @author Alex + /// @param a the parameter a is really nice and very useful + /// @param second the second parameter is not very useful, it just provides additional confusion + constructor(uint a, uint second) public { } + } + )"; + + char const *natspec = R"ABCDEF({ + "methods" : { + "constructor" : { + "author" : "Alex", + "params" : { + "a" : "the parameter a is really nice and very useful", + "second" : "the second parameter is not very useful, it just provides additional confusion" + } + } + } + })ABCDEF"; + + checkNatspec(sourceCode, "test", natspec, false); +} + +BOOST_AUTO_TEST_CASE(dev_constructor_return) +{ + char const* sourceCode = R"( + contract test { + /// @author Alex + /// @param a the parameter a is really nice and very useful + /// @param second the second parameter is not very useful, it just provides additional confusion + /// @return return should not work within constructors + constructor(uint a, uint second) public { } + } + )"; + + expectNatspecError(sourceCode); +} + +BOOST_AUTO_TEST_CASE(dev_constructor_and_function) +{ + char const *sourceCode = R"( + contract test { + /// @author Alex + /// @param a the parameter a is really nice and very useful + /// @param second the second parameter is not very useful, it just provides additional confusion + constructor(uint a, uint second) public { } + /// @dev Multiplies a number by 7 and adds second parameter + /// @param a Documentation for the first parameter starts here. + /// Since it's a really complicated parameter we need 2 lines + /// @param second Documentation for the second parameter + /// @return The result of the multiplication + /// and cookies with nutella + function mul(uint a, uint second) public returns(uint d) { + return a * 7 + second; + } + } + )"; + + char const *natspec = R"ABCDEF({ + "methods" : { + "mul(uint256,uint256)" : { + "details" : "Multiplies a number by 7 and adds second parameter", + "params" : { + "a" : "Documentation for the first parameter starts here. Since it's a really complicated parameter we need 2 lines", + "second" : "Documentation for the second parameter" + }, + "return" : "The result of the multiplication and cookies with nutella" + }, + "constructor" : { + "author" : "Alex", + "params" : { + "a" : "the parameter a is really nice and very useful", + "second" : "the second parameter is not very useful, it just provides additional confusion" + } + } + } + })ABCDEF"; + + checkNatspec(sourceCode, "test", natspec, false); +} + BOOST_AUTO_TEST_SUITE_END() } diff --git a/test/libsolidity/SolidityOptimizer.cpp b/test/libsolidity/SolidityOptimizer.cpp index 5326feaf..f925d36e 100644 --- a/test/libsolidity/SolidityOptimizer.cpp +++ b/test/libsolidity/SolidityOptimizer.cpp @@ -25,7 +25,6 @@ #include <libevmasm/Instruction.h> #include <boost/test/unit_test.hpp> -#include <boost/lexical_cast.hpp> #include <chrono> #include <string> @@ -74,16 +73,16 @@ public: unsigned const _optimizeRuns = 200 ) { - m_nonOptimizedBytecode = compileAndRunWithOptimizer(_sourceCode, _value, _contractName, false, _optimizeRuns); + m_nonOptimizedBytecode = compileAndRunWithOptimizer("pragma solidity >=0.0;\n" + _sourceCode, _value, _contractName, false, _optimizeRuns); m_nonOptimizedContract = m_contractAddress; - m_optimizedBytecode = compileAndRunWithOptimizer(_sourceCode, _value, _contractName, true, _optimizeRuns); + m_optimizedBytecode = compileAndRunWithOptimizer("pragma solidity >=0.0;\n" + _sourceCode, _value, _contractName, true, _optimizeRuns); size_t nonOptimizedSize = numInstructions(m_nonOptimizedBytecode); size_t optimizedSize = numInstructions(m_optimizedBytecode); BOOST_CHECK_MESSAGE( _optimizeRuns < 50 || optimizedSize < nonOptimizedSize, string("Optimizer did not reduce bytecode size. Non-optimized size: ") + - std::to_string(nonOptimizedSize) + " - optimized size: " + - std::to_string(optimizedSize) + to_string(nonOptimizedSize) + " - optimized size: " + + to_string(optimizedSize) ); m_optimizedContract = m_contractAddress; } @@ -104,7 +103,7 @@ public: "\nOptimized: " + toHex(optimizedOutput)); } - /// @returns the number of intructions in the given bytecode, not taking the metadata hash + /// @returns the number of instructions in the given bytecode, not taking the metadata hash /// into account. size_t numInstructions(bytes const& _bytecode, boost::optional<Instruction> _which = boost::optional<Instruction>{}) { @@ -136,10 +135,11 @@ BOOST_AUTO_TEST_CASE(smoke_test) { char const* sourceCode = R"( contract test { - function f(uint a) returns (uint b) { + function f(uint a) public returns (uint b) { return a; } - })"; + } + )"; compileBothVersions(sourceCode); compareVersions("f(uint256)", u256(7)); } @@ -148,10 +148,11 @@ BOOST_AUTO_TEST_CASE(identities) { char const* sourceCode = R"( contract test { - function f(int a) returns (int b) { + function f(int a) public returns (int b) { return int(0) | (int(1) * (int(0) ^ (0 + a))); } - })"; + } + )"; compileBothVersions(sourceCode); compareVersions("f(int256)", u256(0x12334664)); } @@ -161,11 +162,12 @@ BOOST_AUTO_TEST_CASE(unused_expressions) char const* sourceCode = R"( contract test { uint data; - function f() returns (uint a, uint b) { + function f() public returns (uint a, uint b) { 10 + 20; data; } - })"; + } + )"; compileBothVersions(sourceCode); compareVersions("f()"); } @@ -177,10 +179,11 @@ BOOST_AUTO_TEST_CASE(constant_folding_both_sides) // literals as late as possible char const* sourceCode = R"( contract test { - function f(uint x) returns (uint y) { + function f(uint x) public returns (uint y) { return 98 ^ (7 * ((1 | (x | 1000)) * 40) ^ 102); } - })"; + } + )"; compileBothVersions(sourceCode); compareVersions("f(uint256)", 7); } @@ -190,7 +193,7 @@ BOOST_AUTO_TEST_CASE(storage_access) char const* sourceCode = R"( contract test { uint8[40] data; - function f(uint x) returns (uint y) { + function f(uint x) public returns (uint y) { data[2] = data[7] = uint8(x); data[4] = data[2] * 10 + data[3]; } @@ -206,13 +209,13 @@ BOOST_AUTO_TEST_CASE(array_copy) contract test { bytes2[] data1; bytes5[] data2; - function f(uint x) returns (uint l, uint y) { + function f(uint x) public returns (uint l, uint y) { data1.length = msg.data.length; for (uint i = 0; i < msg.data.length; ++i) data1[i] = msg.data[i]; data2 = data1; l = data2.length; - y = uint(data2[x]); + y = uint(uint40(data2[x])); } } )"; @@ -226,8 +229,8 @@ BOOST_AUTO_TEST_CASE(function_calls) { char const* sourceCode = R"( contract test { - function f1(uint x) returns (uint) { return x*x; } - function f(uint x) returns (uint) { return f1(7+x) - this.f1(x**9); } + function f1(uint x) public returns (uint) { return x*x; } + function f(uint x) public returns (uint) { return f1(7+x) - this.f1(x**9); } } )"; compileBothVersions(sourceCode); @@ -241,8 +244,8 @@ BOOST_AUTO_TEST_CASE(storage_write_in_loops) char const* sourceCode = R"( contract test { uint d; - function f(uint a) returns (uint r) { - var x = d; + function f(uint a) public returns (uint r) { + uint x = d; for (uint i = 1; i < a * a; i++) { r = d; d = i; @@ -261,18 +264,18 @@ BOOST_AUTO_TEST_CASE(storage_write_in_loops) // Information in joining branches is not retained anymore. BOOST_AUTO_TEST_CASE(retain_information_in_branches) { - // This tests that the optimizer knows that we already have "z == keccak256(y)" inside both branches. + // This tests that the optimizer knows that we already have "z == keccak256(abi.encodePacked(y))" inside both branches. char const* sourceCode = R"( contract c { bytes32 d; uint a; - function f(uint x, bytes32 y) returns (uint r_a, bytes32 r_d) { - bytes32 z = keccak256(y); + function f(uint x, bytes32 y) public returns (uint r_a, bytes32 r_d) { + bytes32 z = keccak256(abi.encodePacked(y)); if (x > 8) { - z = keccak256(y); + z = keccak256(abi.encodePacked(y)); a = x; } else { - z = keccak256(y); + z = keccak256(abi.encodePacked(y)); a = x; } r_a = a; @@ -309,19 +312,19 @@ BOOST_AUTO_TEST_CASE(store_tags_as_unions) contract test { bytes32 data; function f(uint x, bytes32 y) external returns (uint r_a, bytes32 r_d) { - r_d = keccak256(y); + r_d = keccak256(abi.encodePacked(y)); shared(y); - r_d = keccak256(y); + r_d = keccak256(abi.encodePacked(y)); r_a = 5; } function g(uint x, bytes32 y) external returns (uint r_a, bytes32 r_d) { - r_d = keccak256(y); + r_d = keccak256(abi.encodePacked(y)); shared(y); - r_d = bytes32(uint(keccak256(y)) + 2); + r_d = bytes32(uint(keccak256(abi.encodePacked(y))) + 2); r_a = 7; } function shared(bytes32 y) internal { - data = keccak256(y); + data = keccak256(abi.encodePacked(y)); } } )"; @@ -347,9 +350,9 @@ BOOST_AUTO_TEST_CASE(incorrect_storage_access_bug) contract C { mapping(uint => uint) data; - function f() returns (uint) + function f() public returns (uint) { - if(data[now] == 0) + if (data[now] == 0) data[uint(-7)] = 5; return data[now]; } @@ -366,7 +369,7 @@ BOOST_AUTO_TEST_CASE(sequence_number_for_calls) // to storage), so the sequence number should be incremented. char const* sourceCode = R"( contract test { - function f(string a, string b) returns (bool) { return sha256(a) == sha256(b); } + function f(string memory a, string memory b) public returns (bool) { return sha256(bytes(a)) == sha256(bytes(b)); } } )"; compileBothVersions(sourceCode); @@ -381,24 +384,24 @@ BOOST_AUTO_TEST_CASE(computing_constants) uint m_b; uint m_c; uint m_d; - function C() { + constructor() public { set(); } - function set() returns (uint) { + function set() public returns (uint) { m_a = 0x77abc0000000000000000000000000000000000000000000000000000000001; m_b = 0x817416927846239487123469187231298734162934871263941234127518276; g(); return 1; } - function g() { + function g() public { m_b = 0x817416927846239487123469187231298734162934871263941234127518276; m_c = 0x817416927846239487123469187231298734162934871263941234127518276; h(); } - function h() { + function h() public { m_d = 0xff05694900000000000000000000000000000000000000000000000000000000; } - function get() returns (uint ra, uint rb, uint rc, uint rd) { + function get() public returns (uint ra, uint rb, uint rc, uint rd) { ra = m_a; rb = m_b; rc = m_c; @@ -437,10 +440,8 @@ BOOST_AUTO_TEST_CASE(constant_optimization_early_exit) // This tests that the constant optimizer does not try to find the best representation // indefinitely but instead stops after some number of iterations. char const* sourceCode = R"( - pragma solidity ^0.4.0; - contract HexEncoding { - function hexEncodeTest(address addr) returns (bytes32 ret) { + function hexEncodeTest(address addr) public returns (bytes32 ret) { uint x = uint(addr) / 2**32; // Nibble interleave @@ -514,8 +515,8 @@ BOOST_AUTO_TEST_CASE(inconsistency) // Called with params: containerIndex=0, valueIndex=0 function levelIII(uint containerIndex, uint valueIndex) private { - Container container = containers[containerIndex]; - Value value = container.values[valueIndex]; + Container storage container = containers[containerIndex]; + Value storage value = container.values[valueIndex]; debug = container.valueIndices[value.number]; } function levelII() private { @@ -526,7 +527,7 @@ BOOST_AUTO_TEST_CASE(inconsistency) function trigger() public returns (uint) { containers.length++; - Container container = containers[0]; + Container storage container = containers[0]; container.values.push(Value({ badnum: 9000, @@ -557,11 +558,11 @@ BOOST_AUTO_TEST_CASE(dead_code_elimination_across_assemblies) char const* sourceCode = R"( contract DCE { function () internal returns (uint) stored; - function DCE() { + constructor() public { stored = f; } function f() internal returns (uint) { return 7; } - function test() returns (uint) { return stored(); } + function test() public returns (uint) { return stored(); } } )"; compileBothVersions(sourceCode); @@ -573,12 +574,12 @@ BOOST_AUTO_TEST_CASE(invalid_state_at_control_flow_join) char const* sourceCode = R"( contract Test { uint256 public totalSupply = 100; - function f() returns (uint r) { + function f() public returns (uint r) { if (false) r = totalSupply; totalSupply -= 10; } - function test() returns (uint) { + function test() public returns (uint) { f(); return this.totalSupply(); } @@ -596,7 +597,7 @@ BOOST_AUTO_TEST_CASE(init_empty_dynamic_arrays) // not use any memory. char const* sourceCode = R"( contract Test { - function f() pure returns (uint r) { + function f() public pure returns (uint r) { uint[][] memory x = new uint[][](20000); return x.length; } @@ -615,7 +616,7 @@ BOOST_AUTO_TEST_CASE(optimise_multi_stores) struct S { uint16 a; uint16 b; uint16[3] c; uint[] dyn; } uint padding; S[] s; - function f() public returns (uint16, uint16, uint16[3], uint) { + function f() public returns (uint16, uint16, uint16[3] memory, uint) { uint16[3] memory c; c[0] = 7; c[1] = 8; diff --git a/test/libsolidity/SolidityParser.cpp b/test/libsolidity/SolidityParser.cpp index 0c801cf6..5432e9b5 100644 --- a/test/libsolidity/SolidityParser.cpp +++ b/test/libsolidity/SolidityParser.cpp @@ -112,59 +112,13 @@ while(0) BOOST_AUTO_TEST_SUITE(SolidityParser) -BOOST_AUTO_TEST_CASE(multiple_return_param_trailing_comma) -{ - char const* text = R"( - contract test { - function() returns (uint a, uint b,) {} - } - )"; - CHECK_PARSE_ERROR(text, "Unexpected trailing comma in parameter list."); -} - -BOOST_AUTO_TEST_CASE(multiple_modifier_arg_trailing_comma) -{ - char const* text = R"( - contract test { - modifier modTest(uint a, uint b,) { _; } - function(uint a) {} - } - )"; - CHECK_PARSE_ERROR(text, "Unexpected trailing comma in parameter list."); -} - -BOOST_AUTO_TEST_CASE(multiple_event_arg_trailing_comma) -{ - char const* text = R"( - contract test { - event Test(uint a, uint b,); - function(uint a) {} - } - )"; - CHECK_PARSE_ERROR(text, "Unexpected trailing comma in parameter list."); -} - -BOOST_AUTO_TEST_CASE(two_exact_functions) -{ - char const* text = R"( - contract test { - function fun(uint a) returns(uint r) { return a; } - function fun(uint a) returns(uint r) { return a; } - } - )"; - // with support of overloaded functions, during parsing, - // we can't determine whether they match exactly, however - // it will throw DeclarationError in following stage. - BOOST_CHECK(successParse(text)); -} - BOOST_AUTO_TEST_CASE(function_natspec_documentation) { char const* text = R"( contract test { uint256 stateVar; /// This is a test function - function functionName(bytes32 input) returns (bytes32 out) {} + function functionName(bytes32 input) public returns (bytes32 out) {} } )"; BOOST_CHECK(successParse(text)); @@ -184,7 +138,7 @@ BOOST_AUTO_TEST_CASE(function_normal_comments) contract test { uint256 stateVar; // We won't see this comment - function functionName(bytes32 input) returns (bytes32 out) {} + function functionName(bytes32 input) public returns (bytes32 out) {} } )"; BOOST_CHECK(successParse(text)); @@ -203,13 +157,13 @@ BOOST_AUTO_TEST_CASE(multiple_functions_natspec_documentation) contract test { uint256 stateVar; /// This is test function 1 - function functionName1(bytes32 input) returns (bytes32 out) {} + function functionName1(bytes32 input) public returns (bytes32 out) {} /// This is test function 2 - function functionName2(bytes32 input) returns (bytes32 out) {} + function functionName2(bytes32 input) public returns (bytes32 out) {} // nothing to see here - function functionName3(bytes32 input) returns (bytes32 out) {} + function functionName3(bytes32 input) public returns (bytes32 out) {} /// This is test function 4 - function functionName4(bytes32 input) returns (bytes32 out) {} + function functionName4(bytes32 input) public returns (bytes32 out) {} } )"; BOOST_CHECK(successParse(text)); @@ -239,7 +193,7 @@ BOOST_AUTO_TEST_CASE(multiline_function_documentation) uint256 stateVar; /// This is a test function /// and it has 2 lines - function functionName1(bytes32 input) returns (bytes32 out) {} + function functionName1(bytes32 input) public returns (bytes32 out) {} } )"; BOOST_CHECK(successParse(text)); @@ -266,7 +220,7 @@ BOOST_AUTO_TEST_CASE(natspec_comment_in_function_body) } /// This is a test function /// and it has 2 lines - function fun(bytes32 input) returns (bytes32 out) {} + function fun(bytes32 input) public returns (bytes32 out) {} } )"; BOOST_CHECK(successParse(text)); @@ -334,57 +288,6 @@ BOOST_AUTO_TEST_CASE(natspec_docstring_after_signature) "Shouldn't get natspec docstring for this function"); } -BOOST_AUTO_TEST_CASE(struct_definition) -{ - char const* text = R"( - contract test { - uint256 stateVar; - struct MyStructName { - address addr; - uint256 count; - } - } - )"; - BOOST_CHECK(successParse(text)); -} - -BOOST_AUTO_TEST_CASE(mapping) -{ - char const* text = R"( - contract test { - mapping(address => bytes32) names; - } - )"; - BOOST_CHECK(successParse(text)); -} - -BOOST_AUTO_TEST_CASE(mapping_in_struct) -{ - char const* text = R"( - contract test { - struct test_struct { - address addr; - uint256 count; - mapping(bytes32 => test_struct) self_reference; - } - } - )"; - BOOST_CHECK(successParse(text)); -} - -BOOST_AUTO_TEST_CASE(mapping_to_mapping_in_struct) -{ - char const* text = R"( - contract test { - struct test_struct { - address addr; - mapping (uint64 => mapping (bytes32 => uint)) complex_mapping; - } - } - )"; - BOOST_CHECK(successParse(text)); -} - BOOST_AUTO_TEST_CASE(variable_definition) { char const* text = R"( @@ -440,112 +343,6 @@ BOOST_AUTO_TEST_CASE(complex_expression) BOOST_CHECK(successParse(text)); } -BOOST_AUTO_TEST_CASE(exp_expression) -{ - char const* text = R"( - contract test { - function fun(uint256 a) { - uint256 x = 3 ** a; - } - } - )"; - BOOST_CHECK(successParse(text)); -} - -BOOST_AUTO_TEST_CASE(while_loop) -{ - char const* text = R"( - contract test { - function fun(uint256 a) { - while (true) { uint256 x = 1; break; continue; } x = 9; - } - } - )"; - BOOST_CHECK(successParse(text)); -} - -BOOST_AUTO_TEST_CASE(for_loop_vardef_initexpr) -{ - char const* text = R"( - contract test { - function fun(uint256 a) { - for (uint256 i = 0; i < 10; i++) { - uint256 x = i; break; continue; - } - } - } - )"; - BOOST_CHECK(successParse(text)); -} - -BOOST_AUTO_TEST_CASE(for_loop_simple_initexpr) -{ - char const* text = R"( - contract test { - function fun(uint256 a) { - uint256 i =0; - for (i = 0; i < 10; i++) { - uint256 x = i; break; continue; - } - } - } - )"; - BOOST_CHECK(successParse(text)); -} - -BOOST_AUTO_TEST_CASE(for_loop_simple_noexpr) -{ - char const* text = R"( - contract test { - function fun(uint256 a) { - uint256 i =0; - for (;;) { - uint256 x = i; break; continue; - } - } - } - )"; - BOOST_CHECK(successParse(text)); -} - -BOOST_AUTO_TEST_CASE(for_loop_single_stmt_body) -{ - char const* text = R"( - contract test { - function fun(uint256 a) { - uint256 i = 0; - for (i = 0; i < 10; i++) - continue; - } - } - )"; - BOOST_CHECK(successParse(text)); -} - -BOOST_AUTO_TEST_CASE(if_statement) -{ - char const* text = R"( - contract test { - function fun(uint256 a) { - if (a >= 8) { return 2; } else { var b = 7; } - } - } - )"; - BOOST_CHECK(successParse(text)); -} - -BOOST_AUTO_TEST_CASE(else_if_statement) -{ - char const* text = R"( - contract test { - function fun(uint256 a) returns (address b) { - if (a < 0) b = 0x67; else if (a == 0) b = 0x12; else b = 0x78; - } - } - )"; - BOOST_CHECK(successParse(text)); -} - BOOST_AUTO_TEST_CASE(statement_starting_with_type_conversion) { char const* text = R"( @@ -673,275 +470,70 @@ BOOST_AUTO_TEST_CASE(contract_multiple_inheritance_with_arguments) BOOST_CHECK(successParse(text)); } -BOOST_AUTO_TEST_CASE(placeholder_in_function_context) -{ - char const* text = R"( - contract c { - function fun() returns (uint r) { - var _ = 8; - return _ + 1; - } - } - )"; - BOOST_CHECK(successParse(text)); -} - -BOOST_AUTO_TEST_CASE(modifier) -{ - char const* text = R"( - contract c { - modifier mod { if (msg.sender == 0) _; } - } - )"; - BOOST_CHECK(successParse(text)); -} - -BOOST_AUTO_TEST_CASE(modifier_arguments) -{ - char const* text = R"( - contract c { - modifier mod(address a) { if (msg.sender == a) _; } - } - )"; - BOOST_CHECK(successParse(text)); -} - -BOOST_AUTO_TEST_CASE(modifier_invocation) -{ - char const* text = R"( - contract c { - modifier mod1(uint a) { if (msg.sender == a) _; } - modifier mod2 { if (msg.sender == 2) _; } - function f() mod1(7) mod2 { } - } - )"; - BOOST_CHECK(successParse(text)); -} - -BOOST_AUTO_TEST_CASE(fallback_function) -{ - char const* text = R"( - contract c { - function() { } - } - )"; - BOOST_CHECK(successParse(text)); -} - -BOOST_AUTO_TEST_CASE(event) -{ - char const* text = R"( - contract c { - event e(); - })"; - BOOST_CHECK(successParse(text)); -} - -BOOST_AUTO_TEST_CASE(event_arguments) -{ - char const* text = R"( - contract c { - event e(uint a, bytes32 s); - })"; - BOOST_CHECK(successParse(text)); -} - -BOOST_AUTO_TEST_CASE(event_arguments_indexed) -{ - char const* text = R"( - contract c { - event e(uint a, bytes32 indexed s, bool indexed b); - })"; - BOOST_CHECK(successParse(text)); -} - -BOOST_AUTO_TEST_CASE(visibility_specifiers) -{ - char const* text = R"( - contract c { - uint private a; - uint internal b; - uint public c; - uint d; - function f() {} - function f_priv() private {} - function f_public() public {} - function f_internal() internal {} - })"; - BOOST_CHECK(successParse(text)); -} - BOOST_AUTO_TEST_CASE(multiple_visibility_specifiers) { char const* text = R"( contract c { uint private internal a; - })"; + } + )"; CHECK_PARSE_ERROR(text, "Visibility already specified as \"private\"."); text = R"( contract c { function f() private external {} - })"; + } + )"; CHECK_PARSE_ERROR(text, "Visibility already specified as \"private\"."); } -BOOST_AUTO_TEST_CASE(literal_constants_with_ether_subdenominations) -{ - char const* text = R"( - contract c { - function c () - { - a = 1 wei; - b = 2 szabo; - c = 3 finney; - b = 4 ether; - } - uint256 a; - uint256 b; - uint256 c; - uint256 d; - })"; - BOOST_CHECK(successParse(text)); -} - -BOOST_AUTO_TEST_CASE(literal_constants_with_ether_subdenominations_in_expressions) -{ - char const* text = R"( - contract c { - function c () - { - a = 1 wei * 100 wei + 7 szabo - 3; - } - uint256 a; - })"; - BOOST_CHECK(successParse(text)); -} - -BOOST_AUTO_TEST_CASE(enum_valid_declaration) -{ - char const* text = R"( - contract c { - enum validEnum { Value1, Value2, Value3, Value4 } - function c () - { - a = foo.Value3; - } - uint256 a; - })"; - BOOST_CHECK(successParse(text)); -} - -BOOST_AUTO_TEST_CASE(external_function) -{ - char const* text = R"( - contract c { - function x() external {} - })"; - BOOST_CHECK(successParse(text)); -} - -BOOST_AUTO_TEST_CASE(arrays_in_storage) -{ - char const* text = R"( - contract c { - uint[10] a; - uint[] a2; - struct x { uint[2**20] b; y[0] c; } - struct y { uint d; mapping(uint=>x)[] e; } - })"; - BOOST_CHECK(successParse(text)); -} - -BOOST_AUTO_TEST_CASE(arrays_in_events) -{ - char const* text = R"( - contract c { - event e(uint[10] a, bytes7[8] indexed b, c[3] x); - })"; - BOOST_CHECK(successParse(text)); -} - -BOOST_AUTO_TEST_CASE(arrays_in_expressions) -{ - char const* text = R"( - contract c { - function f() { c[10] a = 7; uint8[10 * 2] x; } - })"; - BOOST_CHECK(successParse(text)); -} - -BOOST_AUTO_TEST_CASE(multi_arrays) -{ - char const* text = R"( - contract c { - mapping(uint => mapping(uint => int8)[8][][9])[] x; - })"; - BOOST_CHECK(successParse(text)); -} - BOOST_AUTO_TEST_CASE(keyword_is_reserved) { auto keywords = { "abstract", "after", + "alias", + "apply", + "auto", "case", "catch", + "copyof", "default", + "define", "final", + "immutable", + "implements", "in", "inline", "let", + "macro", "match", + "mutable", "null", "of", + "override", + "partial", + "promise", + "reference", "relocatable", + "sealed", + "sizeof", "static", + "supports", "switch", "try", "type", - "typeof" + "typedef", + "typeof", + "unchecked" }; for (const auto& keyword: keywords) { auto text = std::string("contract ") + keyword + " {}"; - CHECK_PARSE_ERROR(text.c_str(), "Expected identifier but got reserved keyword"); + CHECK_PARSE_ERROR(text.c_str(), string("Expected identifier but got reserved keyword '") + keyword + "'"); } } -BOOST_AUTO_TEST_CASE(location_specifiers_for_params) -{ - char const* text = R"( - contract Foo { - function f(uint[] storage constant x, uint[] memory y) { } - } - )"; - BOOST_CHECK(successParse(text)); -} - -BOOST_AUTO_TEST_CASE(location_specifiers_for_locals) -{ - char const* text = R"( - contract Foo { - function f() { - uint[] storage x; - uint[] memory y; - } - } - )"; - BOOST_CHECK(successParse(text)); -} - -BOOST_AUTO_TEST_CASE(library_simple) -{ - char const* text = R"( - library Lib { - function f() { } - } - )"; - BOOST_CHECK(successParse(text)); -} - BOOST_AUTO_TEST_CASE(member_access_parser_ambiguity) { char const* text = R"( @@ -986,97 +578,6 @@ BOOST_AUTO_TEST_CASE(complex_import) BOOST_CHECK(successParse(text)); } -BOOST_AUTO_TEST_CASE(from_is_not_keyword) -{ - // "from" is not a keyword although it is used as a keyword in import directives. - char const* text = R"( - contract from { - } - )"; - BOOST_CHECK(successParse(text)); -} - -BOOST_AUTO_TEST_CASE(inline_array_declaration) -{ - char const* text = R"( - contract c { - uint[] a; - function f() returns (uint, uint) { - a = [1,2,3]; - return (a[3], [2,3,4][0]); - } - } - )"; - BOOST_CHECK(successParse(text)); -} - -BOOST_AUTO_TEST_CASE(conditional_true_false_literal) -{ - char const* text = R"( - contract A { - function f() { - uint x = true ? 1 : 0; - uint y = false ? 0 : 1; - } - } - )"; - BOOST_CHECK(successParse(text)); -} - -BOOST_AUTO_TEST_CASE(conditional_with_constants) -{ - char const* text = R"( - contract A { - function f() { - uint x = 3 > 0 ? 3 : 0; - uint y = (3 > 0) ? 3 : 0; - } - } - )"; - BOOST_CHECK(successParse(text)); -} - -BOOST_AUTO_TEST_CASE(conditional_with_variables) -{ - char const* text = R"( - contract A { - function f() { - uint x = 3; - uint y = 1; - uint z = (x > y) ? x : y; - uint w = x > y ? x : y; - } - } - )"; - BOOST_CHECK(successParse(text)); -} - -BOOST_AUTO_TEST_CASE(conditional_multiple) -{ - char const* text = R"( - contract A { - function f() { - uint x = 3 < 0 ? 2 > 1 ? 2 : 1 : 7 > 2 ? 7 : 6; - } - } - )"; - BOOST_CHECK(successParse(text)); -} - -BOOST_AUTO_TEST_CASE(conditional_with_assignment) -{ - char const* text = R"( - contract A { - function f() { - uint y = 1; - uint x = 3 < 0 ? x = 3 : 6; - true ? x = 3 : 4; - } - } - )"; - BOOST_CHECK(successParse(text)); -} - BOOST_AUTO_TEST_CASE(recursion_depth1) { string text("contract C { bytes"); @@ -1113,117 +614,6 @@ BOOST_AUTO_TEST_CASE(recursion_depth4) CHECK_PARSE_ERROR(text, "Maximum recursion depth reached during parsing"); } -BOOST_AUTO_TEST_CASE(declaring_fixed_and_ufixed_variables) -{ - char const* text = R"( - contract A { - fixed40x40 storeMe; - function f(ufixed x, fixed32x32 y) { - ufixed8x8 a; - fixed b; - } - } - )"; - BOOST_CHECK(successParse(text)); -} - -BOOST_AUTO_TEST_CASE(declaring_fixed_literal_variables) -{ - char const* text = R"( - contract A { - fixed40x40 pi = 3.14; - } - )"; - BOOST_CHECK(successParse(text)); -} - -BOOST_AUTO_TEST_CASE(function_type_in_expression) -{ - char const* text = R"( - contract test { - function f(uint x, uint y) returns (uint a) {} - function g() { - function (uint, uint) internal returns (uint) f1 = f; - } - } - )"; - BOOST_CHECK(successParse(text)); -} - -BOOST_AUTO_TEST_CASE(function_type_as_storage_variable) -{ - char const* text = R"( - contract test { - function (uint, uint) internal returns (uint) f1; - } - )"; - BOOST_CHECK(successParse(text)); -} - -BOOST_AUTO_TEST_CASE(function_type_as_storage_variable_with_assignment) -{ - char const* text = R"( - contract test { - function f(uint x, uint y) returns (uint a) {} - function (uint, uint) internal returns (uint) f1 = f; - } - )"; - BOOST_CHECK(successParse(text)); -} - -BOOST_AUTO_TEST_CASE(function_type_in_struct) -{ - char const* text = R"( - contract test { - struct S { - function (uint x, uint y) internal returns (uint a) f; - function (uint, uint) external returns (uint) g; - uint d; - } - } - )"; - BOOST_CHECK(successParse(text)); -} - -BOOST_AUTO_TEST_CASE(function_type_as_parameter) -{ - char const* text = R"( - contract test { - function f(function(uint) external returns (uint) g) internal returns (uint a) { - return g(1); - } - } - )"; - BOOST_CHECK(successParse(text)); -} - -BOOST_AUTO_TEST_CASE(calling_function) -{ - char const* text = R"( - contract test { - function f() { - function() returns(function() returns(function() returns(function() returns(uint)))) x; - uint y; - y = x()()()(); - } - } - )"; - BOOST_CHECK(successParse(text)); -} - -BOOST_AUTO_TEST_CASE(mapping_and_array_of_functions) -{ - char const* text = R"( - contract test { - mapping (address => function() internal returns (uint)) a; - mapping (address => function() external) b; - mapping (address => function() external[]) c; - function() external[] d; - } - )"; - BOOST_CHECK(successParse(text)); -} - BOOST_AUTO_TEST_SUITE_END() } diff --git a/test/libsolidity/SolidityScanner.cpp b/test/libsolidity/SolidityScanner.cpp index 4ccc6788..6965d886 100644 --- a/test/libsolidity/SolidityScanner.cpp +++ b/test/libsolidity/SolidityScanner.cpp @@ -66,6 +66,14 @@ BOOST_AUTO_TEST_CASE(string_escapes) BOOST_CHECK_EQUAL(scanner.currentLiteral(), "aa"); } +BOOST_AUTO_TEST_CASE(string_escapes_all) +{ + Scanner scanner(CharStream(" { \"a\\x61\\b\\f\\n\\r\\t\\v\"")); + BOOST_CHECK_EQUAL(scanner.currentToken(), Token::LBrace); + BOOST_CHECK_EQUAL(scanner.next(), Token::StringLiteral); + BOOST_CHECK_EQUAL(scanner.currentLiteral(), "aa\b\f\n\r\t\v"); +} + BOOST_AUTO_TEST_CASE(string_escapes_with_zero) { Scanner scanner(CharStream(" { \"a\\x61\\x00abc\"")); @@ -97,6 +105,11 @@ BOOST_AUTO_TEST_CASE(hex_numbers) BOOST_CHECK_EQUAL(scanner.currentLiteral(), "0x765432536763762734623472346"); BOOST_CHECK_EQUAL(scanner.next(), Token::Semicolon); BOOST_CHECK_EQUAL(scanner.next(), Token::EOS); + scanner.reset(CharStream("0x1234"), ""); + BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Number); + BOOST_CHECK_EQUAL(scanner.currentLiteral(), "0x1234"); + scanner.reset(CharStream("0X1234"), ""); + BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Illegal); } BOOST_AUTO_TEST_CASE(octal_numbers) @@ -129,6 +142,96 @@ BOOST_AUTO_TEST_CASE(scientific_notation) BOOST_CHECK_EQUAL(scanner.next(), Token::EOS); } +BOOST_AUTO_TEST_CASE(trailing_dot) +{ + Scanner scanner(CharStream("2.5")); + BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Number); + BOOST_CHECK_EQUAL(scanner.next(), Token::EOS); + scanner.reset(CharStream("2.5e10"), ""); + BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Number); + BOOST_CHECK_EQUAL(scanner.next(), Token::EOS); + scanner.reset(CharStream(".5"), ""); + BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Number); + BOOST_CHECK_EQUAL(scanner.next(), Token::EOS); + scanner.reset(CharStream(".5e10"), ""); + BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Number); + BOOST_CHECK_EQUAL(scanner.next(), Token::EOS); + scanner.reset(CharStream("2."), ""); + BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Number); + BOOST_CHECK_EQUAL(scanner.next(), Token::Period); + BOOST_CHECK_EQUAL(scanner.next(), Token::EOS); +} + +BOOST_AUTO_TEST_CASE(leading_underscore_decimal_is_identifier) +{ + // Actual error is cought by SyntaxChecker. + Scanner scanner(CharStream("_1.2")); + BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Identifier); + BOOST_CHECK_EQUAL(scanner.next(), Token::Number); + BOOST_CHECK_EQUAL(scanner.next(), Token::EOS); +} + +BOOST_AUTO_TEST_CASE(leading_underscore_decimal_after_dot_illegal) +{ + // Actual error is cought by SyntaxChecker. + Scanner scanner(CharStream("1._2")); + BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Number); + BOOST_CHECK_EQUAL(scanner.next(), Token::EOS); + + scanner.reset(CharStream("1._"), ""); + BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Number); + BOOST_CHECK_EQUAL(scanner.next(), Token::EOS); +} + +BOOST_AUTO_TEST_CASE(leading_underscore_exp_are_identifier) +{ + // Actual error is cought by SyntaxChecker. + Scanner scanner(CharStream("_1e2")); + BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Identifier); + BOOST_CHECK_EQUAL(scanner.next(), Token::EOS); +} + +BOOST_AUTO_TEST_CASE(leading_underscore_exp_after_e_illegal) +{ + // Actual error is cought by SyntaxChecker. + Scanner scanner(CharStream("1e_2")); + BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Number); + BOOST_CHECK_EQUAL(scanner.currentLiteral(), "1e_2"); + BOOST_CHECK_EQUAL(scanner.next(), Token::EOS); +} + +BOOST_AUTO_TEST_CASE(leading_underscore_hex_illegal) +{ + Scanner scanner(CharStream("0x_abc")); + BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Illegal); + BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier); + BOOST_CHECK_EQUAL(scanner.next(), Token::EOS); +} + +BOOST_AUTO_TEST_CASE(fixed_number_invalid_underscore_front) +{ + // Actual error is cought by SyntaxChecker. + Scanner scanner(CharStream("12._1234_1234")); + BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Number); + BOOST_CHECK_EQUAL(scanner.next(), Token::EOS); +} + +BOOST_AUTO_TEST_CASE(number_literals_with_trailing_underscore_at_eos) +{ + // Actual error is cought by SyntaxChecker. + Scanner scanner(CharStream("0x123_")); + BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Number); + BOOST_CHECK_EQUAL(scanner.next(), Token::EOS); + + scanner.reset(CharStream("123_"), ""); + BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Number); + BOOST_CHECK_EQUAL(scanner.next(), Token::EOS); + + scanner.reset(CharStream("12.34_"), ""); + BOOST_CHECK_EQUAL(scanner.currentToken(), Token::Number); + BOOST_CHECK_EQUAL(scanner.next(), Token::EOS); +} + BOOST_AUTO_TEST_CASE(negative_numbers) { Scanner scanner(CharStream("var x = -.2 + -0x78 + -7.3 + 8.9 + 2e-2;")); @@ -181,7 +284,7 @@ BOOST_AUTO_TEST_CASE(locations) BOOST_AUTO_TEST_CASE(ambiguities) { // test scanning of some operators which need look-ahead - Scanner scanner(CharStream("<=""<""+ +=a++ =>""<<")); + Scanner scanner(CharStream("<=" "<" "+ +=a++ =>" "<<" ">>" " >>=" ">>>" ">>>=" " >>>>>=><<=")); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::LessThanOrEqual); BOOST_CHECK_EQUAL(scanner.next(), Token::LessThan); BOOST_CHECK_EQUAL(scanner.next(), Token::Add); @@ -190,6 +293,15 @@ BOOST_AUTO_TEST_CASE(ambiguities) BOOST_CHECK_EQUAL(scanner.next(), Token::Inc); BOOST_CHECK_EQUAL(scanner.next(), Token::Arrow); BOOST_CHECK_EQUAL(scanner.next(), Token::SHL); + BOOST_CHECK_EQUAL(scanner.next(), Token::SAR); + BOOST_CHECK_EQUAL(scanner.next(), Token::AssignSar); + BOOST_CHECK_EQUAL(scanner.next(), Token::SHR); + BOOST_CHECK_EQUAL(scanner.next(), Token::AssignShr); + // the last "monster" token combination + BOOST_CHECK_EQUAL(scanner.next(), Token::SHR); + BOOST_CHECK_EQUAL(scanner.next(), Token::AssignSar); + BOOST_CHECK_EQUAL(scanner.next(), Token::GreaterThan); + BOOST_CHECK_EQUAL(scanner.next(), Token::AssignShl); } BOOST_AUTO_TEST_CASE(documentation_comments_parsed_begin) @@ -359,6 +471,8 @@ BOOST_AUTO_TEST_CASE(invalid_short_unicode_string_escape) BOOST_CHECK_EQUAL(scanner.next(), Token::Illegal); } +// HEX STRING LITERAL + BOOST_AUTO_TEST_CASE(valid_hex_literal) { Scanner scanner(CharStream("{ hex\"00112233FF\"")); @@ -371,30 +485,32 @@ BOOST_AUTO_TEST_CASE(invalid_short_hex_literal) { Scanner scanner(CharStream("{ hex\"00112233F\"")); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::LBrace); - BOOST_CHECK_EQUAL(scanner.next(), Token::Illegal); + BOOST_CHECK_EQUAL(scanner.next(), Token::IllegalHex); } BOOST_AUTO_TEST_CASE(invalid_hex_literal_with_space) { Scanner scanner(CharStream("{ hex\"00112233FF \"")); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::LBrace); - BOOST_CHECK_EQUAL(scanner.next(), Token::Illegal); + BOOST_CHECK_EQUAL(scanner.next(), Token::IllegalHex); } BOOST_AUTO_TEST_CASE(invalid_hex_literal_with_wrong_quotes) { Scanner scanner(CharStream("{ hex\"00112233FF'")); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::LBrace); - BOOST_CHECK_EQUAL(scanner.next(), Token::Illegal); + BOOST_CHECK_EQUAL(scanner.next(), Token::IllegalHex); } BOOST_AUTO_TEST_CASE(invalid_hex_literal_nonhex_string) { Scanner scanner(CharStream("{ hex\"hello\"")); BOOST_CHECK_EQUAL(scanner.currentToken(), Token::LBrace); - BOOST_CHECK_EQUAL(scanner.next(), Token::Illegal); + BOOST_CHECK_EQUAL(scanner.next(), Token::IllegalHex); } +// COMMENTS + BOOST_AUTO_TEST_CASE(invalid_multiline_comment_close) { // This used to parse as "comment", "identifier" diff --git a/test/libsolidity/SolidityTypes.cpp b/test/libsolidity/SolidityTypes.cpp index 738b24bc..6f9d4ce8 100644 --- a/test/libsolidity/SolidityTypes.cpp +++ b/test/libsolidity/SolidityTypes.cpp @@ -22,7 +22,7 @@ #include <libsolidity/ast/Types.h> #include <libsolidity/ast/AST.h> -#include <libdevcore/SHA3.h> +#include <libdevcore/Keccak256.h> #include <boost/test/unit_test.hpp> using namespace std; @@ -36,6 +36,47 @@ namespace test BOOST_AUTO_TEST_SUITE(SolidityTypes) +BOOST_AUTO_TEST_CASE(int_types) +{ + BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::Int, 0, 0)) == *make_shared<IntegerType>(256, IntegerType::Modifier::Signed)); + for (unsigned i = 8; i <= 256; i += 8) + BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::IntM, i, 0)) == *make_shared<IntegerType>(i, IntegerType::Modifier::Signed)); +} + +BOOST_AUTO_TEST_CASE(uint_types) +{ + BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::UInt, 0, 0)) == *make_shared<IntegerType>(256, IntegerType::Modifier::Unsigned)); + for (unsigned i = 8; i <= 256; i += 8) + BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::UIntM, i, 0)) == *make_shared<IntegerType>(i, IntegerType::Modifier::Unsigned)); +} + +BOOST_AUTO_TEST_CASE(byte_types) +{ + BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::Byte, 0, 0)) == *make_shared<FixedBytesType>(1)); + for (unsigned i = 1; i <= 32; i++) + BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::BytesM, i, 0)) == *make_shared<FixedBytesType>(i)); +} + +BOOST_AUTO_TEST_CASE(fixed_types) +{ + BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::Fixed, 0, 0)) == *make_shared<FixedPointType>(128, 18, FixedPointType::Modifier::Signed)); + for (unsigned i = 8; i <= 256; i += 8) + { + BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::FixedMxN, i, 0)) == *make_shared<FixedPointType>(i, 0, FixedPointType::Modifier::Signed)); + BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::FixedMxN, i, 2)) == *make_shared<FixedPointType>(i, 2, FixedPointType::Modifier::Signed)); + } +} + +BOOST_AUTO_TEST_CASE(ufixed_types) +{ + BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::UFixed, 0, 0)) == *make_shared<FixedPointType>(128, 18, FixedPointType::Modifier::Unsigned)); + for (unsigned i = 8; i <= 256; i += 8) + { + BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::UFixedMxN, i, 0)) == *make_shared<FixedPointType>(i, 0, FixedPointType::Modifier::Unsigned)); + BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::UFixedMxN, i, 2)) == *make_shared<FixedPointType>(i, 2, FixedPointType::Modifier::Unsigned)); + } +} + BOOST_AUTO_TEST_CASE(storage_layout_simple) { MemberList members(MemberList::MemberMap({ @@ -88,12 +129,13 @@ BOOST_AUTO_TEST_CASE(storage_layout_arrays) BOOST_CHECK(ArrayType(DataLocation::Storage, make_shared<FixedBytesType>(32), 9).storageSize() == 9); } -BOOST_AUTO_TEST_CASE(type_escaping) +BOOST_AUTO_TEST_CASE(type_identifier_escaping) { BOOST_CHECK_EQUAL(Type::escapeIdentifier("("), "$_"); BOOST_CHECK_EQUAL(Type::escapeIdentifier(")"), "_$"); BOOST_CHECK_EQUAL(Type::escapeIdentifier(","), "_$_"); BOOST_CHECK_EQUAL(Type::escapeIdentifier("$"), "$$$"); + BOOST_CHECK_EQUAL(Type::escapeIdentifier(")$("), "_$$$$$_"); BOOST_CHECK_EQUAL(Type::escapeIdentifier("()"), "$__$"); BOOST_CHECK_EQUAL(Type::escapeIdentifier("(,)"), "$__$__$"); BOOST_CHECK_EQUAL(Type::escapeIdentifier("(,$,)"), "$__$_$$$_$__$"); @@ -110,21 +152,28 @@ BOOST_AUTO_TEST_CASE(type_identifiers) BOOST_CHECK_EQUAL(Type::fromElementaryTypeName("int128")->identifier(), "t_int128"); BOOST_CHECK_EQUAL(Type::fromElementaryTypeName("address")->identifier(), "t_address"); BOOST_CHECK_EQUAL(Type::fromElementaryTypeName("uint8")->identifier(), "t_uint8"); - BOOST_CHECK_EQUAL(Type::fromElementaryTypeName("ufixed8x64")->identifier(), "t_ufixed8x64"); + BOOST_CHECK_EQUAL(Type::fromElementaryTypeName("ufixed64x2")->identifier(), "t_ufixed64x2"); BOOST_CHECK_EQUAL(Type::fromElementaryTypeName("fixed128x8")->identifier(), "t_fixed128x8"); BOOST_CHECK_EQUAL(RationalNumberType(rational(7, 1)).identifier(), "t_rational_7_by_1"); BOOST_CHECK_EQUAL(RationalNumberType(rational(200, 77)).identifier(), "t_rational_200_by_77"); BOOST_CHECK_EQUAL(RationalNumberType(rational(2 * 200, 2 * 77)).identifier(), "t_rational_200_by_77"); + BOOST_CHECK_EQUAL(RationalNumberType(rational(-2 * 200, 2 * 77)).identifier(), "t_rational_minus_200_by_77"); BOOST_CHECK_EQUAL( StringLiteralType(Literal(SourceLocation{}, Token::StringLiteral, make_shared<string>("abc - def"))).identifier(), "t_stringliteral_196a9142ee0d40e274a6482393c762b16dd8315713207365e1e13d8d85b74fc4" ); + BOOST_CHECK_EQUAL(Type::fromElementaryTypeName("byte")->identifier(), "t_bytes1"); BOOST_CHECK_EQUAL(Type::fromElementaryTypeName("bytes8")->identifier(), "t_bytes8"); BOOST_CHECK_EQUAL(Type::fromElementaryTypeName("bytes32")->identifier(), "t_bytes32"); BOOST_CHECK_EQUAL(Type::fromElementaryTypeName("bool")->identifier(), "t_bool"); BOOST_CHECK_EQUAL(Type::fromElementaryTypeName("bytes")->identifier(), "t_bytes_storage_ptr"); BOOST_CHECK_EQUAL(Type::fromElementaryTypeName("bytes memory")->identifier(), "t_bytes_memory_ptr"); + BOOST_CHECK_EQUAL(Type::fromElementaryTypeName("bytes storage")->identifier(), "t_bytes_storage_ptr"); + BOOST_CHECK_EQUAL(Type::fromElementaryTypeName("bytes calldata")->identifier(), "t_bytes_calldata_ptr"); BOOST_CHECK_EQUAL(Type::fromElementaryTypeName("string")->identifier(), "t_string_storage_ptr"); + BOOST_CHECK_EQUAL(Type::fromElementaryTypeName("string memory")->identifier(), "t_string_memory_ptr"); + BOOST_CHECK_EQUAL(Type::fromElementaryTypeName("string storage")->identifier(), "t_string_storage_ptr"); + BOOST_CHECK_EQUAL(Type::fromElementaryTypeName("string calldata")->identifier(), "t_string_calldata_ptr"); ArrayType largeintArray(DataLocation::Memory, Type::fromElementaryTypeName("int128"), u256("2535301200456458802993406410752")); BOOST_CHECK_EQUAL(largeintArray.identifier(), "t_array$_t_int128_$2535301200456458802993406410752_memory_ptr"); TypePointer stringArray = make_shared<ArrayType>(DataLocation::Storage, Type::fromElementaryTypeName("string"), u256("20")); @@ -144,11 +193,11 @@ BOOST_AUTO_TEST_CASE(type_identifiers) TupleType t({e.type(), s.type(), stringArray, nullptr}); BOOST_CHECK_EQUAL(t.identifier(), "t_tuple$_t_type$_t_enum$_Enum_$4_$_$_t_type$_t_struct$_Struct_$3_storage_ptr_$_$_t_array$_t_string_storage_$20_storage_ptr_$__$"); - TypePointer sha3fun = make_shared<FunctionType>(strings{}, strings{}, FunctionType::Kind::SHA3); - BOOST_CHECK_EQUAL(sha3fun->identifier(), "t_function_sha3_nonpayable$__$returns$__$"); + TypePointer keccak256fun = make_shared<FunctionType>(strings{}, strings{}, FunctionType::Kind::KECCAK256); + BOOST_CHECK_EQUAL(keccak256fun->identifier(), "t_function_keccak256_nonpayable$__$returns$__$"); - FunctionType metaFun(TypePointers{sha3fun}, TypePointers{s.type()}); - BOOST_CHECK_EQUAL(metaFun.identifier(), "t_function_internal_nonpayable$_t_function_sha3_nonpayable$__$returns$__$_$returns$_t_type$_t_struct$_Struct_$3_storage_ptr_$_$"); + FunctionType metaFun(TypePointers{keccak256fun}, TypePointers{s.type()}); + BOOST_CHECK_EQUAL(metaFun.identifier(), "t_function_internal_nonpayable$_t_function_keccak256_nonpayable$__$returns$__$_$returns$_t_type$_t_struct$_Struct_$3_storage_ptr_$_$"); TypePointer m = make_shared<MappingType>(Type::fromElementaryTypeName("bytes32"), s.type()); MappingType m2(Type::fromElementaryTypeName("uint64"), m); diff --git a/test/libsolidity/StandardCompiler.cpp b/test/libsolidity/StandardCompiler.cpp index 63c03881..1570a9d2 100644 --- a/test/libsolidity/StandardCompiler.cpp +++ b/test/libsolidity/StandardCompiler.cpp @@ -225,6 +225,71 @@ BOOST_AUTO_TEST_CASE(smoke_test) BOOST_CHECK(containsAtMostWarnings(result)); } +BOOST_AUTO_TEST_CASE(optimizer_enabled_not_boolean) +{ + char const* input = R"( + { + "language": "Solidity", + "settings": { + "optimizer": { + "enabled": "wrong" + } + }, + "sources": { + "empty": { + "content": "" + } + } + } + )"; + Json::Value result = compile(input); + BOOST_CHECK(containsError(result, "JSONError", "The \"enabled\" setting must be a boolean.")); +} + +BOOST_AUTO_TEST_CASE(optimizer_runs_not_a_number) +{ + char const* input = R"( + { + "language": "Solidity", + "settings": { + "optimizer": { + "enabled": true, + "runs": "not a number" + } + }, + "sources": { + "empty": { + "content": "" + } + } + } + )"; + Json::Value result = compile(input); + BOOST_CHECK(containsError(result, "JSONError", "The \"runs\" setting must be an unsigned number.")); +} + +BOOST_AUTO_TEST_CASE(optimizer_runs_not_an_unsigned_number) +{ + char const* input = R"( + { + "language": "Solidity", + "settings": { + "optimizer": { + "enabled": true, + "runs": -1 + } + }, + "sources": { + "empty": { + "content": "" + } + } + } + )"; + Json::Value result = compile(input); + BOOST_CHECK(containsError(result, "JSONError", "The \"runs\" setting must be an unsigned number.")); +} + BOOST_AUTO_TEST_CASE(basic_compilation) { char const* input = R"( @@ -238,7 +303,7 @@ BOOST_AUTO_TEST_CASE(basic_compilation) "settings": { "outputSelection": { "fileA": { - "A": [ "abi", "devdoc", "userdoc", "evm.bytecode", "evm.assembly", "evm.gasEstimates", "metadata" ], + "A": [ "abi", "devdoc", "userdoc", "evm.bytecode", "evm.assembly", "evm.gasEstimates", "evm.legacyAssembly", "metadata" ], "": [ "legacyAST" ] } } @@ -261,7 +326,7 @@ BOOST_AUTO_TEST_CASE(basic_compilation) BOOST_CHECK(contract["evm"]["bytecode"]["object"].isString()); BOOST_CHECK_EQUAL( dev::test::bytecodeSansMetadata(contract["evm"]["bytecode"]["object"].asString()), - "6080604052348015600f57600080fd5b50603580601d6000396000f3006080604052600080fd00" + "6080604052348015600f57600080fd5b50603580601d6000396000f3fe6080604052600080fdfe" ); BOOST_CHECK(contract["evm"]["assembly"].isString()); BOOST_CHECK(contract["evm"]["assembly"].asString().find( @@ -280,6 +345,34 @@ BOOST_AUTO_TEST_CASE(basic_compilation) dev::jsonCompactPrint(contract["evm"]["gasEstimates"]), "{\"creation\":{\"codeDepositCost\":\"10600\",\"executionCost\":\"66\",\"totalCost\":\"10666\"}}" ); + // Lets take the top level `.code` section (the "deployer code"), that should expose most of the features of + // the assembly JSON. What we want to check here is Operation, Push, PushTag, PushSub, PushSubSize and Tag. + BOOST_CHECK(contract["evm"]["legacyAssembly"].isObject()); + BOOST_CHECK(contract["evm"]["legacyAssembly"][".code"].isArray()); + BOOST_CHECK_EQUAL( + dev::jsonCompactPrint(contract["evm"]["legacyAssembly"][".code"]), + "[{\"begin\":0,\"end\":14,\"name\":\"PUSH\",\"value\":\"80\"}," + "{\"begin\":0,\"end\":14,\"name\":\"PUSH\",\"value\":\"40\"}," + "{\"begin\":0,\"end\":14,\"name\":\"MSTORE\"}," + "{\"begin\":0,\"end\":14,\"name\":\"CALLVALUE\"}," + "{\"begin\":8,\"end\":17,\"name\":\"DUP1\"}," + "{\"begin\":5,\"end\":7,\"name\":\"ISZERO\"}," + "{\"begin\":5,\"end\":7,\"name\":\"PUSH [tag]\",\"value\":\"1\"}," + "{\"begin\":5,\"end\":7,\"name\":\"JUMPI\"}," + "{\"begin\":30,\"end\":31,\"name\":\"PUSH\",\"value\":\"0\"}," + "{\"begin\":27,\"end\":28,\"name\":\"DUP1\"}," + "{\"begin\":20,\"end\":32,\"name\":\"REVERT\"}," + "{\"begin\":5,\"end\":7,\"name\":\"tag\",\"value\":\"1\"}," + "{\"begin\":5,\"end\":7,\"name\":\"JUMPDEST\"}," + "{\"begin\":0,\"end\":14,\"name\":\"POP\"}," + "{\"begin\":0,\"end\":14,\"name\":\"PUSH #[$]\",\"value\":\"0000000000000000000000000000000000000000000000000000000000000000\"}," + "{\"begin\":0,\"end\":14,\"name\":\"DUP1\"}," + "{\"begin\":0,\"end\":14,\"name\":\"PUSH [$]\",\"value\":\"0000000000000000000000000000000000000000000000000000000000000000\"}," + "{\"begin\":0,\"end\":14,\"name\":\"PUSH\",\"value\":\"0\"}," + "{\"begin\":0,\"end\":14,\"name\":\"CODECOPY\"}," + "{\"begin\":0,\"end\":14,\"name\":\"PUSH\",\"value\":\"0\"}," + "{\"begin\":0,\"end\":14,\"name\":\"RETURN\"}]" + ); BOOST_CHECK(contract["metadata"].isString()); BOOST_CHECK(dev::test::isValidMetadata(contract["metadata"].asString())); BOOST_CHECK(result["sources"].isObject()); @@ -466,7 +559,7 @@ BOOST_AUTO_TEST_CASE(output_selection_dependent_contract) }, "sources": { "fileA": { - "content": "contract B { } contract A { function f() { new B(); } }" + "content": "contract B { } contract A { function f() public { new B(); } }" } } } @@ -495,7 +588,7 @@ BOOST_AUTO_TEST_CASE(output_selection_dependent_contract_with_import) }, "sources": { "fileA": { - "content": "import \"fileB\"; contract A { function f() { new B(); } }" + "content": "import \"fileB\"; contract A { function f() public { new B(); } }" }, "fileB": { "content": "contract B { }" @@ -556,10 +649,10 @@ BOOST_AUTO_TEST_CASE(library_filename_with_colon) }, "sources": { "fileA": { - "content": "import \"git:library.sol\"; contract A { function f() returns (uint) { return L.g(); } }" + "content": "import \"git:library.sol\"; contract A { function f() public returns (uint) { return L.g(); } }" }, "git:library.sol": { - "content": "library L { function g() returns (uint) { return 1; } }" + "content": "library L { function g() public returns (uint) { return 1; } }" } } } @@ -612,7 +705,7 @@ BOOST_AUTO_TEST_CASE(libraries_invalid_entry) } )"; Json::Value result = compile(input); - BOOST_CHECK(containsError(result, "JSONError", "library entry is not a JSON object.")); + BOOST_CHECK(containsError(result, "JSONError", "Library entry is not a JSON object.")); } BOOST_AUTO_TEST_CASE(libraries_invalid_hex) @@ -706,13 +799,13 @@ BOOST_AUTO_TEST_CASE(library_linking) }, "sources": { "fileA": { - "content": "import \"library.sol\"; import \"library2.sol\"; contract A { function f() returns (uint) { L2.g(); return L.g(); } }" + "content": "import \"library.sol\"; import \"library2.sol\"; contract A { function f() public returns (uint) { L2.g(); return L.g(); } }" }, "library.sol": { - "content": "library L { function g() returns (uint) { return 1; } }" + "content": "library L { function g() public returns (uint) { return 1; } }" }, "library2.sol": { - "content": "library L2 { function g() { } }" + "content": "library L2 { function g() public { } }" } } } diff --git a/test/libsolidity/SyntaxTest.cpp b/test/libsolidity/SyntaxTest.cpp index 1c2355d5..91d1681f 100644 --- a/test/libsolidity/SyntaxTest.cpp +++ b/test/libsolidity/SyntaxTest.cpp @@ -20,7 +20,6 @@ #include <boost/algorithm/string.hpp> #include <boost/algorithm/string/predicate.hpp> #include <boost/throw_exception.hpp> -#include <cctype> #include <fstream> #include <memory> #include <stdexcept> @@ -33,28 +32,10 @@ using namespace std; namespace fs = boost::filesystem; using namespace boost::unit_test; -template<typename IteratorType> -void skipWhitespace(IteratorType& _it, IteratorType _end) +namespace { - while (_it != _end && isspace(*_it)) - ++_it; -} -template<typename IteratorType> -void skipSlashes(IteratorType& _it, IteratorType _end) -{ - while (_it != _end && *_it == '/') - ++_it; -} - -void expect(string::iterator& _it, string::iterator _end, string::value_type _c) -{ - if (_it == _end || *_it != _c) - throw runtime_error(string("Invalid test expectation. Expected: \"") + _c + "\"."); - ++_it; -} - -int parseUnsignedInteger(string::iterator &_it, string::iterator _end) +int parseUnsignedInteger(string::iterator& _it, string::iterator _end) { if (_it == _end || !isdigit(*_it)) throw runtime_error("Invalid test expectation. Source location expected."); @@ -68,6 +49,8 @@ int parseUnsignedInteger(string::iterator &_it, string::iterator _end) return result; } +} + SyntaxTest::SyntaxTest(string const& _filename) { ifstream file(_filename); @@ -120,6 +103,55 @@ bool SyntaxTest::run(ostream& _stream, string const& _linePrefix, bool const _fo return true; } +void SyntaxTest::printSource(ostream& _stream, string const& _linePrefix, bool const _formatted) const +{ + if (_formatted) + { + if (m_source.empty()) + return; + + vector<char const*> sourceFormatting(m_source.length(), formatting::RESET); + for (auto const& error: m_errorList) + if (error.locationStart >= 0 && error.locationEnd >= 0) + { + assert(static_cast<size_t>(error.locationStart) <= m_source.length()); + assert(static_cast<size_t>(error.locationEnd) <= m_source.length()); + bool isWarning = error.type == "Warning"; + for (int i = error.locationStart; i < error.locationEnd; i++) + if (isWarning) + { + if (sourceFormatting[i] == formatting::RESET) + sourceFormatting[i] = formatting::ORANGE_BACKGROUND; + } + else + sourceFormatting[i] = formatting::RED_BACKGROUND; + } + + _stream << _linePrefix << sourceFormatting.front() << m_source.front(); + for (size_t i = 1; i < m_source.length(); i++) + { + if (sourceFormatting[i] != sourceFormatting[i - 1]) + _stream << sourceFormatting[i]; + if (m_source[i] != '\n') + _stream << m_source[i]; + else + { + _stream << formatting::RESET << endl; + if (i + 1 < m_source.length()) + _stream << _linePrefix << sourceFormatting[i]; + } + } + _stream << formatting::RESET; + } + else + { + stringstream stream(m_source); + string line; + while (getline(stream, line)) + _stream << _linePrefix << line << endl; + } +} + void SyntaxTest::printErrorList( ostream& _stream, vector<SyntaxTestError> const& _errorList, @@ -159,19 +191,6 @@ string SyntaxTest::errorMessage(Exception const& _e) return "NONE"; } -string SyntaxTest::parseSource(istream& _stream) -{ - string source; - string line; - string const delimiter("// ----"); - while (getline(_stream, line)) - if (boost::algorithm::starts_with(line, delimiter)) - break; - else - source += line + "\n"; - return source; -} - vector<SyntaxTestError> SyntaxTest::parseExpectations(istream& _stream) { vector<SyntaxTestError> expectations; @@ -220,64 +239,3 @@ vector<SyntaxTestError> SyntaxTest::parseExpectations(istream& _stream) } return expectations; } - -#if BOOST_VERSION < 105900 -test_case *make_test_case( - function<void()> const& _fn, - string const& _name, - string const& /* _filename */, - size_t /* _line */ -) -{ - return make_test_case(_fn, _name); -} -#endif - -bool SyntaxTest::isTestFilename(boost::filesystem::path const& _filename) -{ - return _filename.extension().string() == ".sol" && - !boost::starts_with(_filename.string(), "~") && - !boost::starts_with(_filename.string(), "."); -} - -int SyntaxTest::registerTests( - boost::unit_test::test_suite& _suite, - boost::filesystem::path const& _basepath, - boost::filesystem::path const& _path -) -{ - int numTestsAdded = 0; - fs::path fullpath = _basepath / _path; - if (fs::is_directory(fullpath)) - { - test_suite* sub_suite = BOOST_TEST_SUITE(_path.filename().string()); - for (auto const& entry: boost::iterator_range<fs::directory_iterator>( - fs::directory_iterator(fullpath), - fs::directory_iterator() - )) - if (fs::is_directory(entry.path()) || isTestFilename(entry.path().filename())) - numTestsAdded += registerTests(*sub_suite, _basepath, _path / entry.path().filename()); - _suite.add(sub_suite); - } - else - { - static vector<unique_ptr<string>> filenames; - - filenames.emplace_back(new string(_path.string())); - _suite.add(make_test_case( - [fullpath] - { - BOOST_REQUIRE_NO_THROW({ - stringstream errorStream; - if (!SyntaxTest(fullpath.string()).run(errorStream)) - BOOST_ERROR("Test expectation mismatch.\n" + errorStream.str()); - }); - }, - _path.stem().string(), - *filenames.back(), - 0 - )); - numTestsAdded = 1; - } - return numTestsAdded; -} diff --git a/test/libsolidity/SyntaxTest.h b/test/libsolidity/SyntaxTest.h index 6159e789..e9e36aa6 100644 --- a/test/libsolidity/SyntaxTest.h +++ b/test/libsolidity/SyntaxTest.h @@ -19,11 +19,9 @@ #include <test/libsolidity/AnalysisFramework.h> #include <test/libsolidity/FormattedScope.h> +#include <test/libsolidity/TestCase.h> #include <libsolidity/interface/Exceptions.h> -#include <boost/noncopyable.hpp> -#include <boost/test/unit_test.hpp> - #include <iosfwd> #include <string> #include <vector> @@ -52,17 +50,24 @@ struct SyntaxTestError }; -class SyntaxTest: AnalysisFramework +class SyntaxTest: AnalysisFramework, public TestCase { public: + static std::unique_ptr<TestCase> create(std::string const& _filename) + { return std::unique_ptr<TestCase>(new SyntaxTest(_filename)); } SyntaxTest(std::string const& _filename); - bool run(std::ostream& _stream, std::string const& _linePrefix = "", bool const _formatted = false); + virtual bool run(std::ostream& _stream, std::string const& _linePrefix = "", bool const _formatted = false) override; - std::vector<SyntaxTestError> const& expectations() const { return m_expectations; } - std::string const& source() const { return m_source; } - std::vector<SyntaxTestError> const& errorList() const { return m_errorList; } + virtual void printSource(std::ostream &_stream, std::string const &_linePrefix = "", bool const _formatted = false) const override; + virtual void printUpdatedExpectations(std::ostream& _stream, std::string const& _linePrefix) const override + { + if (!m_errorList.empty()) + printErrorList(_stream, m_errorList, _linePrefix, false); + } + static std::string errorMessage(Exception const& _e); +private: static void printErrorList( std::ostream& _stream, std::vector<SyntaxTestError> const& _errors, @@ -70,15 +75,6 @@ public: bool const _formatted = false ); - static int registerTests( - boost::unit_test::test_suite& _suite, - boost::filesystem::path const& _basepath, - boost::filesystem::path const& _path - ); - static bool isTestFilename(boost::filesystem::path const& _filename); - static std::string errorMessage(Exception const& _e); -private: - static std::string parseSource(std::istream& _stream); static std::vector<SyntaxTestError> parseExpectations(std::istream& _stream); std::string m_source; diff --git a/test/libsolidity/TestCase.cpp b/test/libsolidity/TestCase.cpp new file mode 100644 index 00000000..17972269 --- /dev/null +++ b/test/libsolidity/TestCase.cpp @@ -0,0 +1,56 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <test/libsolidity/TestCase.h> + +#include <boost/algorithm/string.hpp> +#include <boost/algorithm/string/predicate.hpp> + +#include <stdexcept> + +using namespace dev; +using namespace solidity; +using namespace dev::solidity::test; +using namespace std; + +bool TestCase::isTestFilename(boost::filesystem::path const& _filename) +{ + string extension = _filename.extension().string(); + return (extension == ".sol" || extension == ".yul") && + !boost::starts_with(_filename.string(), "~") && + !boost::starts_with(_filename.string(), "."); +} + +string TestCase::parseSource(istream& _stream) +{ + string source; + string line; + string const delimiter("// ----"); + while (getline(_stream, line)) + if (boost::algorithm::starts_with(line, delimiter)) + break; + else + source += line + "\n"; + return source; +} + +void TestCase::expect(string::iterator& _it, string::iterator _end, string::value_type _c) +{ + if (_it == _end || *_it != _c) + throw runtime_error(string("Invalid test expectation. Expected: \"") + _c + "\"."); + ++_it; +} diff --git a/test/libsolidity/TestCase.h b/test/libsolidity/TestCase.h new file mode 100644 index 00000000..3c05ae4e --- /dev/null +++ b/test/libsolidity/TestCase.h @@ -0,0 +1,80 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see <http://www.gnu.org/licenses/>. +*/ + +#pragma once + +#include <boost/filesystem.hpp> + +#include <iosfwd> +#include <memory> +#include <string> + +namespace dev +{ +namespace solidity +{ +namespace test +{ + +/** Common superclass of SyntaxTest and SemanticsTest. */ +class TestCase +{ +public: + using TestCaseCreator = std::unique_ptr<TestCase>(*)(std::string const&); + + virtual ~TestCase() {} + + /// Runs the test case. + /// Outputs error messages to @arg _stream. Each line of output is prefixed with @arg _linePrefix. + /// Optionally, color-coding can be enabled (if @arg _formatted is set to true). + /// @returns true, if the test case succeeds, false otherwise + virtual bool run(std::ostream& _stream, std::string const& _linePrefix = "", bool const _formatted = false) = 0; + + /// Outputs the test contract to @arg _stream. + /// Each line of output is prefixed with @arg _linePrefix. + /// If @arg _formatted is true, color-coding may be used to indicate + /// error locations in the contract, if applicable. + virtual void printSource(std::ostream &_stream, std::string const &_linePrefix = "", bool const _formatted = false) const = 0; + /// Outputs test expectations to @arg _stream that match the actual results of the test. + /// Each line of output is prefixed with @arg _linePrefix. + virtual void printUpdatedExpectations(std::ostream& _stream, std::string const& _linePrefix) const = 0; + + static bool isTestFilename(boost::filesystem::path const& _filename); + +protected: + static std::string parseSource(std::istream& _file); + static void expect(std::string::iterator& _it, std::string::iterator _end, std::string::value_type _c); + + template<typename IteratorType> + static void skipWhitespace(IteratorType& _it, IteratorType _end) + { + while (_it != _end && isspace(*_it)) + ++_it; + } + + template<typename IteratorType> + static void skipSlashes(IteratorType& _it, IteratorType _end) + { + while (_it != _end && *_it == '/') + ++_it; + } + +}; + +} +} +} diff --git a/test/libsolidity/ViewPureChecker.cpp b/test/libsolidity/ViewPureChecker.cpp index cd0a0b01..b7ea1efc 100644 --- a/test/libsolidity/ViewPureChecker.cpp +++ b/test/libsolidity/ViewPureChecker.cpp @@ -38,91 +38,27 @@ namespace test BOOST_FIXTURE_TEST_SUITE(ViewPureChecker, AnalysisFramework) -BOOST_AUTO_TEST_CASE(smoke_test) -{ - char const* text = R"( - contract C { - uint x; - function g() pure public {} - function f() view public returns (uint) { return now; } - function h() public { x = 2; } - function i() payable public { x = 2; } - } - )"; - CHECK_SUCCESS_NO_WARNINGS(text); -} - -BOOST_AUTO_TEST_CASE(call_internal_functions_success) -{ - char const* text = R"( - contract C { - function g() pure public { g(); } - function f() view public returns (uint) { f(); g(); } - function h() public { h(); g(); f(); } - function i() payable public { i(); h(); g(); f(); } - } - )"; - CHECK_SUCCESS_NO_WARNINGS(text); -} - -BOOST_AUTO_TEST_CASE(suggest_pure) -{ - char const* text = R"( - contract C { - function g() view public { } - } - )"; - CHECK_WARNING(text, "can be restricted to pure"); -} - -BOOST_AUTO_TEST_CASE(suggest_view) -{ - char const* text = R"( - contract C { - uint x; - function g() public returns (uint) { return x; } - } - )"; - CHECK_WARNING(text, "can be restricted to view"); -} - -BOOST_AUTO_TEST_CASE(call_internal_functions_fail) -{ - CHECK_ERROR( - "contract C{ function f() pure public { g(); } function g() view public {} }", - TypeError, - "Function declared as pure, but this expression (potentially) reads from the environment or state and thus requires \"view\"" - ); -} - -BOOST_AUTO_TEST_CASE(write_storage_fail) -{ - CHECK_WARNING( - "contract C{ uint x; function f() view public { x = 2; } }", - "Function declared as view, but this expression (potentially) modifies the state and thus requires non-payable (the default) or payable." - ); -} - BOOST_AUTO_TEST_CASE(environment_access) { vector<string> view{ "block.coinbase", "block.timestamp", - "block.blockhash(7)", "block.difficulty", "block.number", "block.gaslimit", "blockhash(7)", "gasleft()", - "msg.gas", "msg.value", "msg.sender", "tx.origin", "tx.gasprice", "this", - "address(1).balance" + "address(1).balance", }; - // ``block.blockhash`` and ``blockhash`` are tested seperately below because their usage will + if (dev::test::Options::get().evmVersion().hasStaticCall()) + view.emplace_back("address(0x4242).staticcall(\"\")"); + + // ``block.blockhash`` and ``blockhash`` are tested separately below because their usage will // produce warnings that can't be handled in a generic way. vector<string> pure{ "msg.data", @@ -155,282 +91,29 @@ BOOST_AUTO_TEST_CASE(environment_access) "Statement has no effect." })); - CHECK_WARNING_ALLOW_MULTI( - "contract C { function f() view public { block.blockhash; } }", - (std::vector<std::string>{ - "Function state mutability can be restricted to pure", - "\"block.blockhash()\" has been deprecated in favor of \"blockhash()\"" - })); -} - -BOOST_AUTO_TEST_CASE(view_error_for_050) -{ CHECK_ERROR( - "pragma experimental \"v0.5.0\"; contract C { uint x; function f() view public { x = 2; } }", + "contract C { function f() view public { block.blockhash; } }", TypeError, - "Function declared as view, but this expression (potentially) modifies the state and thus requires non-payable (the default) or payable." + "\"block.blockhash()\" has been deprecated in favor of \"blockhash()\"" ); - -} - -BOOST_AUTO_TEST_CASE(modifiers) -{ - string text = R"( - contract D { - uint x; - modifier purem(uint) { _; } - modifier viewm(uint) { uint a = x; _; a; } - modifier nonpayablem(uint) { x = 2; _; } - } - contract C is D { - function f() purem(0) pure public {} - function g() viewm(0) view public {} - function h() nonpayablem(0) public {} - function i() purem(x) view public {} - function j() viewm(x) view public {} - function k() nonpayablem(x) public {} - function l() purem(x = 2) public {} - function m() viewm(x = 2) public {} - function n() nonpayablem(x = 2) public {} - } - )"; - CHECK_SUCCESS_NO_WARNINGS(text); -} - -BOOST_AUTO_TEST_CASE(interface) -{ - string text = R"( - interface D { - function f() view external; - } - contract C is D { - function f() view external {} - } - )"; - CHECK_SUCCESS_NO_WARNINGS(text); -} - -BOOST_AUTO_TEST_CASE(overriding) -{ - string text = R"( - contract D { - uint x; - function f() public { x = 2; } - } - contract C is D { - function f() public {} - } - )"; - CHECK_SUCCESS_NO_WARNINGS(text); -} - -BOOST_AUTO_TEST_CASE(returning_structs) -{ - string text = R"( - contract C { - struct S { uint x; } - S s; - function f() view internal returns (S storage) { - return s; - } - function g() public { - f().x = 2; - } - function h() view public { - f(); - f().x; - } - } - )"; - CHECK_SUCCESS_NO_WARNINGS(text); -} - -BOOST_AUTO_TEST_CASE(mappings) -{ - string text = R"( - contract C { - mapping(uint => uint) a; - function f() view public { - a; - } - function g() view public { - a[2]; - } - function h() public { - a[2] = 3; - } - } - )"; - CHECK_SUCCESS_NO_WARNINGS(text); -} - -BOOST_AUTO_TEST_CASE(local_storage_variables) -{ - string text = R"( - contract C { - struct S { uint a; } - S s; - function f() view public { - S storage x = s; - x; - } - function g() view public { - S storage x = s; - x = s; - } - function i() public { - s.a = 2; - } - function h() public { - S storage x = s; - x.a = 2; - } - } - )"; - CHECK_SUCCESS_NO_WARNINGS(text); -} - -BOOST_AUTO_TEST_CASE(builtin_functions) -{ - string text = R"( - contract C { - function f() public { - address(this).transfer(1); - require(address(this).send(2)); - selfdestruct(address(this)); - require(address(this).delegatecall()); - require(address(this).call()); - } - function g() pure public { - bytes32 x = keccak256("abc"); - bytes32 y = sha256("abc"); - address z = ecrecover(1, 2, 3, 4); - require(true); - assert(true); - x; y; z; - } - function() payable public {} - } - )"; - CHECK_SUCCESS_NO_WARNINGS(text); -} - -BOOST_AUTO_TEST_CASE(function_types) -{ - string text = R"( - contract C { - function f() pure public { - function () external nonpayFun; - function () external view viewFun; - function () external pure pureFun; - - nonpayFun; - viewFun; - pureFun; - pureFun(); - } - function g() view public { - function () external view viewFun; - - viewFun(); - } - function h() public { - function () external nonpayFun; - - nonpayFun(); - } - } - )"; - CHECK_SUCCESS_NO_WARNINGS(text); -} - -BOOST_AUTO_TEST_CASE(selector) -{ - string text = R"( - contract C { - uint public x; - function f() payable public { - } - function g() pure public returns (bytes4) { - return this.f.selector ^ this.x.selector; - } - } - )"; - CHECK_SUCCESS_NO_WARNINGS(text); -} - -BOOST_AUTO_TEST_CASE(selector_complex) -{ - string text = R"( - contract C { - function f(C c) pure public returns (C) { - return c; - } - function g() pure public returns (bytes4) { - // By passing `this`, we read from the state, even if f itself is pure. - return f(this).f.selector; - } - } - )"; - CHECK_ERROR(text, TypeError, "reads from the environment or state and thus requires \"view\""); } -BOOST_AUTO_TEST_CASE(selector_complex2) +BOOST_AUTO_TEST_CASE(address_staticcall) { string text = R"( contract C { - function f() payable public returns (C) { - return this; - } - function g() pure public returns (bytes4) { - C x = C(0x123); - return x.f.selector; + function i() view public returns (bool) { + (bool success,) = address(0x4242).staticcall(""); + return success; } } )"; - CHECK_SUCCESS_NO_WARNINGS(text); -} - -BOOST_AUTO_TEST_CASE(creation) -{ - string text = R"( - contract D {} - contract C { - function f() public { new D(); } - } - )"; - CHECK_SUCCESS_NO_WARNINGS(text); + if (!dev::test::Options::get().evmVersion().hasStaticCall()) + CHECK_ERROR(text, TypeError, "\"staticcall\" is not supported by the VM version."); + else + CHECK_SUCCESS_NO_WARNINGS(text); } -BOOST_AUTO_TEST_CASE(assembly) -{ - string text = R"( - contract C { - struct S { uint x; } - S s; - function e() pure public { - assembly { mstore(keccak256(0, 20), mul(s_slot, 2)) } - } - function f() pure public { - uint x; - assembly { x := 7 } - } - function g() view public { - assembly { for {} 1 { pop(sload(0)) } { } pop(gas) } - } - function h() view public { - assembly { function g() { pop(blockhash(20)) } } - } - function j() public { - assembly { pop(call(0, 1, 2, 3, 4, 5, 6)) } - } - function k() public { - assembly { pop(call(gas, 1, 2, 3, 4, 5, 6)) } - } - } - )"; - CHECK_SUCCESS_NO_WARNINGS(text); -} BOOST_AUTO_TEST_CASE(assembly_staticcall) { @@ -447,31 +130,6 @@ BOOST_AUTO_TEST_CASE(assembly_staticcall) CHECK_SUCCESS_NO_WARNINGS(text); } -BOOST_AUTO_TEST_CASE(assembly_jump) -{ - string text = R"( - contract C { - function k() public { - assembly { jump(2) } - } - } - )"; - CHECK_WARNING(text, "low-level EVM features"); -} - -BOOST_AUTO_TEST_CASE(constant) -{ - string text = R"( - contract C { - uint constant x = 2; - function k() pure public returns (uint) { - return x; - } - } - )"; - CHECK_SUCCESS_NO_WARNINGS(text); -} - BOOST_AUTO_TEST_SUITE_END() } diff --git a/test/libsolidity/smtCheckerTests/functions/functions_identity_1.sol b/test/libsolidity/smtCheckerTests/functions/functions_identity_1.sol new file mode 100644 index 00000000..25a42db6 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/functions/functions_identity_1.sol @@ -0,0 +1,13 @@ +pragma experimental SMTChecker; +contract C +{ + function h(uint x) public pure returns (uint) { + return x; + } + function g() public pure { + uint x; + x = h(42); + assert(x > 0); + } +} + diff --git a/test/libsolidity/smtCheckerTests/functions/functions_identity_1_fail.sol b/test/libsolidity/smtCheckerTests/functions/functions_identity_1_fail.sol new file mode 100644 index 00000000..a70aaf61 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/functions/functions_identity_1_fail.sol @@ -0,0 +1,15 @@ +pragma experimental SMTChecker; +contract C +{ + function h(uint x) public pure returns (uint) { + return x; + } + function g() public pure { + uint x; + x = h(0); + assert(x > 0); + } +} + +// ---- +// Warning: (161-174): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/functions/functions_identity_2.sol b/test/libsolidity/smtCheckerTests/functions/functions_identity_2.sol new file mode 100644 index 00000000..aff24b03 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/functions/functions_identity_2.sol @@ -0,0 +1,17 @@ +pragma experimental SMTChecker; +contract C +{ + function h(uint x) public pure returns (uint) { + return k(x); + } + + function k(uint x) public pure returns (uint) { + return x; + } + function g() public pure { + uint x; + x = h(2); + assert(x > 0); + } +} + diff --git a/test/libsolidity/smtCheckerTests/functions/functions_identity_2_fail.sol b/test/libsolidity/smtCheckerTests/functions/functions_identity_2_fail.sol new file mode 100644 index 00000000..9baeb935 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/functions/functions_identity_2_fail.sol @@ -0,0 +1,19 @@ +pragma experimental SMTChecker; +contract C +{ + function h(uint x) public pure returns (uint) { + return k(x); + } + + function k(uint x) public pure returns (uint) { + return x; + } + function g() public pure { + uint x; + x = h(0); + assert(x > 0); + } +} + +// ---- +// Warning: (229-242): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/functions/functions_identity_as_tuple.sol b/test/libsolidity/smtCheckerTests/functions/functions_identity_as_tuple.sol new file mode 100644 index 00000000..3793f411 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/functions/functions_identity_as_tuple.sol @@ -0,0 +1,14 @@ +pragma experimental SMTChecker; +contract C +{ + function h(uint x) public pure returns (uint) { + return x; + } + function g() public pure { + uint x; + x = (h)(42); + assert(x > 0); + } +} + +// ---- diff --git a/test/libsolidity/smtCheckerTests/functions/functions_identity_as_tuple_fail.sol b/test/libsolidity/smtCheckerTests/functions/functions_identity_as_tuple_fail.sol new file mode 100644 index 00000000..e3c80594 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/functions/functions_identity_as_tuple_fail.sol @@ -0,0 +1,15 @@ +pragma experimental SMTChecker; +contract C +{ + function h(uint x) public pure returns (uint) { + return x; + } + function g() public pure { + uint x; + x = (h)(0); + assert(x > 0); + } +} + +// ---- +// Warning: (163-176): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/functions/functions_recursive.sol b/test/libsolidity/smtCheckerTests/functions/functions_recursive.sol new file mode 100644 index 00000000..d2f8ab1d --- /dev/null +++ b/test/libsolidity/smtCheckerTests/functions/functions_recursive.sol @@ -0,0 +1,17 @@ +pragma experimental SMTChecker; +contract C +{ + uint a; + function g() public { + if (a > 0) + { + a = a - 1; + g(); + } + else + assert(a == 0); + } +} + +// ---- +// Warning: (111-114): Assertion checker does not support recursive function calls. diff --git a/test/libsolidity/smtCheckerTests/functions/functions_recursive_indirect.sol b/test/libsolidity/smtCheckerTests/functions/functions_recursive_indirect.sol new file mode 100644 index 00000000..d5b83f00 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/functions/functions_recursive_indirect.sol @@ -0,0 +1,26 @@ +pragma experimental SMTChecker; +contract C +{ + uint a; + function f() public { + if (a > 0) + { + a = a - 1; + g(); + } + else + assert(a == 0); + } + function g() public { + if (a > 0) + { + a = a - 1; + f(); + } + else + assert(a == 0); + } +} +// ---- +// Warning: (206-209): Assertion checker does not support recursive function calls. +// Warning: (111-114): Assertion checker does not support recursive function calls. diff --git a/test/libsolidity/smtCheckerTests/functions/functions_storage_var_1.sol b/test/libsolidity/smtCheckerTests/functions/functions_storage_var_1.sol new file mode 100644 index 00000000..2f7563dd --- /dev/null +++ b/test/libsolidity/smtCheckerTests/functions/functions_storage_var_1.sol @@ -0,0 +1,14 @@ +pragma experimental SMTChecker; +contract C +{ + uint a; + function f(uint x) public { + uint y; + a = (y = x); + } + function g() public { + f(1); + assert(a > 0); + } +} + diff --git a/test/libsolidity/smtCheckerTests/functions/functions_storage_var_1_fail.sol b/test/libsolidity/smtCheckerTests/functions/functions_storage_var_1_fail.sol new file mode 100644 index 00000000..ad735768 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/functions/functions_storage_var_1_fail.sol @@ -0,0 +1,16 @@ +pragma experimental SMTChecker; +contract C +{ + uint a; + function f(uint x) public { + uint y; + a = (y = x); + } + function g() public { + f(0); + assert(a > 0); + } +} + +// ---- +// Warning: (144-157): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/functions/functions_storage_var_2.sol b/test/libsolidity/smtCheckerTests/functions/functions_storage_var_2.sol new file mode 100644 index 00000000..2f95d8af --- /dev/null +++ b/test/libsolidity/smtCheckerTests/functions/functions_storage_var_2.sol @@ -0,0 +1,15 @@ +pragma experimental SMTChecker; +contract C +{ + uint a; + function f(uint x) public { + uint y; + a = (y = x); + } + function g() public { + f(1); + f(42); + assert(a > 1); + } +} + diff --git a/test/libsolidity/smtCheckerTests/functions/functions_storage_var_2_fail.sol b/test/libsolidity/smtCheckerTests/functions/functions_storage_var_2_fail.sol new file mode 100644 index 00000000..5d972f96 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/functions/functions_storage_var_2_fail.sol @@ -0,0 +1,17 @@ +pragma experimental SMTChecker; +contract C +{ + uint a; + function f(uint x) public { + uint y; + a = (y = x); + } + function g() public { + f(1); + f(0); + assert(a > 0); + } +} + +// ---- +// Warning: (152-165): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/functions/functions_trivial_condition_for.sol b/test/libsolidity/smtCheckerTests/functions/functions_trivial_condition_for.sol new file mode 100644 index 00000000..7693ad81 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/functions/functions_trivial_condition_for.sol @@ -0,0 +1,8 @@ +pragma experimental SMTChecker; + +contract C +{ + function f(bool x) public pure { require(x); for (;x;) {} } +} +// ---- +// Warning: (98-99): For loop condition is always true. diff --git a/test/libsolidity/smtCheckerTests/functions/functions_trivial_condition_for_only_call.sol b/test/libsolidity/smtCheckerTests/functions/functions_trivial_condition_for_only_call.sol new file mode 100644 index 00000000..ed1ad73a --- /dev/null +++ b/test/libsolidity/smtCheckerTests/functions/functions_trivial_condition_for_only_call.sol @@ -0,0 +1,7 @@ +pragma experimental SMTChecker; + +contract C +{ + function f(bool x) public pure { for (;x;) {} } + function g() public pure { f(true); } +} diff --git a/test/libsolidity/smtCheckerTests/functions/functions_trivial_condition_if.sol b/test/libsolidity/smtCheckerTests/functions/functions_trivial_condition_if.sol new file mode 100644 index 00000000..364fe8d1 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/functions/functions_trivial_condition_if.sol @@ -0,0 +1,7 @@ +pragma experimental SMTChecker; +contract C +{ + function f(bool x) public pure { require(x); if (x) {} } +} +// ---- +// Warning: (95-96): Condition is always true. diff --git a/test/libsolidity/smtCheckerTests/functions/functions_trivial_condition_require.sol b/test/libsolidity/smtCheckerTests/functions/functions_trivial_condition_require.sol new file mode 100644 index 00000000..e76badf2 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/functions/functions_trivial_condition_require.sol @@ -0,0 +1,8 @@ +pragma experimental SMTChecker; + +contract C +{ + function f(bool x) public pure { x = true; require(x); } +} +// ---- +// Warning: (98-99): Condition is always true. diff --git a/test/libsolidity/smtCheckerTests/functions/functions_trivial_condition_require_only_call.sol b/test/libsolidity/smtCheckerTests/functions/functions_trivial_condition_require_only_call.sol new file mode 100644 index 00000000..5cae940b --- /dev/null +++ b/test/libsolidity/smtCheckerTests/functions/functions_trivial_condition_require_only_call.sol @@ -0,0 +1,7 @@ +pragma experimental SMTChecker; + +contract C +{ + function f(bool x) public pure { require(x); } + function g() public pure { f(true); } +} diff --git a/test/libsolidity/smtCheckerTests/functions/functions_trivial_condition_while.sol b/test/libsolidity/smtCheckerTests/functions/functions_trivial_condition_while.sol new file mode 100644 index 00000000..66396dd8 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/functions/functions_trivial_condition_while.sol @@ -0,0 +1,8 @@ +pragma experimental SMTChecker; + +contract C +{ + function f(bool x) public pure { require(x); while (x) {} } +} +// ---- +// Warning: (99-100): While loop condition is always true. diff --git a/test/libsolidity/smtCheckerTests/functions/functions_trivial_condition_while_only_call.sol b/test/libsolidity/smtCheckerTests/functions/functions_trivial_condition_while_only_call.sol new file mode 100644 index 00000000..5000eeb6 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/functions/functions_trivial_condition_while_only_call.sol @@ -0,0 +1,7 @@ +pragma experimental SMTChecker; + +contract C +{ + function f(bool x) public pure { while (x) {} } + function g() public pure { f(true); } +} diff --git a/test/libsolidity/smtCheckerTests/special/blockhash.sol b/test/libsolidity/smtCheckerTests/special/blockhash.sol new file mode 100644 index 00000000..1c693914 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/special/blockhash.sol @@ -0,0 +1,10 @@ +pragma experimental SMTChecker; + +contract C +{ + function f() public payable { + assert(blockhash(2) > 0); + } +} +// ---- +// Warning: (79-103): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/special/difficulty.sol b/test/libsolidity/smtCheckerTests/special/difficulty.sol new file mode 100644 index 00000000..4469d4e5 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/special/difficulty.sol @@ -0,0 +1,10 @@ +pragma experimental SMTChecker; + +contract C +{ + function f(uint difficulty) public view { + assert(block.difficulty == difficulty); + } +} +// ---- +// Warning: (91-129): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/special/gasleft.sol b/test/libsolidity/smtCheckerTests/special/gasleft.sol new file mode 100644 index 00000000..857230fe --- /dev/null +++ b/test/libsolidity/smtCheckerTests/special/gasleft.sol @@ -0,0 +1,14 @@ +pragma experimental SMTChecker; + +contract C +{ + function f() public view { + assert(gasleft() > 0); + uint g = gasleft(); + assert(g < gasleft()); + assert(g >= gasleft()); + } +} +// ---- +// Warning: (76-97): Assertion violation happens here +// Warning: (123-144): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/special/many.sol b/test/libsolidity/smtCheckerTests/special/many.sol new file mode 100644 index 00000000..40e5d987 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/special/many.sol @@ -0,0 +1,25 @@ +pragma experimental SMTChecker; + +contract C +{ + function f() public payable { + 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; + assert(x + 2 > block.number); + assert(now > 10); + assert(gasleft() > 100); + } +} +// ---- +// Warning: (79-115): Assertion violation happens here +// Warning: (119-161): Assertion violation happens here +// Warning: (165-204): Assertion violation happens here +// Warning: (208-240): Assertion violation happens here +// Warning: (244-275): Assertion violation happens here +// Warning: (311-316): Overflow (resulting value larger than 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) happens here +// Warning: (336-352): Assertion violation happens here +// Warning: (356-379): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/special/msg_data.sol b/test/libsolidity/smtCheckerTests/special/msg_data.sol new file mode 100644 index 00000000..7e748f09 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/special/msg_data.sol @@ -0,0 +1,14 @@ +pragma experimental SMTChecker; + +contract C +{ + function f() public payable { + assert(msg.data.length > 0); + } +} +// ---- +// Warning: (86-101): Assertion checker does not yet support this expression. +// Warning: (86-94): Assertion checker does not yet support this special variable. +// Warning: (86-94): Assertion checker does not yet implement this type. +// Warning: (86-101): Internal error: Expression undefined for SMT solver. +// Warning: (79-106): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/special/msg_sender_1.sol b/test/libsolidity/smtCheckerTests/special/msg_sender_1.sol new file mode 100644 index 00000000..dd2366e2 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/special/msg_sender_1.sol @@ -0,0 +1,10 @@ +pragma experimental SMTChecker; + +contract C +{ + function f() public view { + address a = msg.sender; + address b = msg.sender; + assert(a == b); + } +} diff --git a/test/libsolidity/smtCheckerTests/special/msg_sender_2.sol b/test/libsolidity/smtCheckerTests/special/msg_sender_2.sol new file mode 100644 index 00000000..ad45d076 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/special/msg_sender_2.sol @@ -0,0 +1,14 @@ +pragma experimental SMTChecker; + +contract C +{ + function f() public view { + require(msg.sender != address(0)); + address a = msg.sender; + address b = msg.sender; + assert(a == b); + } +} +// ---- +// Warning: (98-108): Assertion checker does not yet implement this expression. +// Warning: (98-108): Internal error: Expression undefined for SMT solver. diff --git a/test/libsolidity/smtCheckerTests/special/msg_sender_fail_1.sol b/test/libsolidity/smtCheckerTests/special/msg_sender_fail_1.sol new file mode 100644 index 00000000..9a4eefd5 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/special/msg_sender_fail_1.sol @@ -0,0 +1,13 @@ +pragma experimental SMTChecker; + +contract C +{ + function f(address c) public view { + address a = msg.sender; + address b = msg.sender; + assert(a == b); + assert(c == msg.sender); + } +} +// ---- +// Warning: (155-178): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/special/msg_sig.sol b/test/libsolidity/smtCheckerTests/special/msg_sig.sol new file mode 100644 index 00000000..109470a8 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/special/msg_sig.sol @@ -0,0 +1,10 @@ +pragma experimental SMTChecker; + +contract C +{ + function f() public payable { + assert(msg.sig == 0x00000000); + } +} +// ---- +// Warning: (79-108): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/types/fixed_bytes_1.sol b/test/libsolidity/smtCheckerTests/types/fixed_bytes_1.sol new file mode 100644 index 00000000..541fff54 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/types/fixed_bytes_1.sol @@ -0,0 +1,16 @@ +pragma experimental SMTChecker; + +contract C +{ + bytes32 x; + function f(bytes8 y) public view { + assert(x != y); + assert(x != g()); + } + function g() public view returns (bytes32) { + return x; + } +} +// ---- +// Warning: (96-110): Assertion violation happens here +// Warning: (114-130): Assertion violation happens here diff --git a/test/libsolidity/syntaxTests/array/array_pop.sol b/test/libsolidity/syntaxTests/array/array_pop.sol new file mode 100644 index 00000000..3804f911 --- /dev/null +++ b/test/libsolidity/syntaxTests/array/array_pop.sol @@ -0,0 +1,7 @@ +contract C { + uint[] data; + function test() public { + data.pop(); + } +} +// ---- diff --git a/test/libsolidity/syntaxTests/array/array_pop_arg.sol b/test/libsolidity/syntaxTests/array/array_pop_arg.sol new file mode 100644 index 00000000..bb7803e2 --- /dev/null +++ b/test/libsolidity/syntaxTests/array/array_pop_arg.sol @@ -0,0 +1,8 @@ +contract C { + uint[] data; + function test() public { + data.pop(5); + } +} +// ---- +// TypeError: (65-76): Wrong argument count for function call: 1 arguments given but expected 0. diff --git a/test/libsolidity/syntaxTests/array/bytes_pop.sol b/test/libsolidity/syntaxTests/array/bytes_pop.sol new file mode 100644 index 00000000..cd5aa0eb --- /dev/null +++ b/test/libsolidity/syntaxTests/array/bytes_pop.sol @@ -0,0 +1,7 @@ +contract C { + bytes data; + function test() public { + data.pop(); + } +} +// ---- diff --git a/test/libsolidity/syntaxTests/array/dynamic_memory_array_pop.sol b/test/libsolidity/syntaxTests/array/dynamic_memory_array_pop.sol new file mode 100644 index 00000000..5a79afc9 --- /dev/null +++ b/test/libsolidity/syntaxTests/array/dynamic_memory_array_pop.sol @@ -0,0 +1,8 @@ +contract C { + function test() public { + uint[] memory data; + data.pop(); + } +} +// ---- +// TypeError: (74-82): Member "pop" is not available in uint256[] memory outside of storage. diff --git a/test/libsolidity/syntaxTests/array/length/array_length_cannot_be_constant_function_parameter.sol b/test/libsolidity/syntaxTests/array/length/array_length_cannot_be_constant_function_parameter.sol new file mode 100644 index 00000000..59328140 --- /dev/null +++ b/test/libsolidity/syntaxTests/array/length/array_length_cannot_be_constant_function_parameter.sol @@ -0,0 +1,8 @@ +contract C { + function f(uint constant LEN) public { + uint[LEN] a; + } +} +// ---- +// DeclarationError: (28-45): The "constant" keyword can only be used for state variables. +// TypeError: (69-72): Invalid array length, expected integer literal or constant expression. diff --git a/test/libsolidity/syntaxTests/array/length/bytes32_too_large.sol b/test/libsolidity/syntaxTests/array/length/bytes32_too_large.sol new file mode 100644 index 00000000..1742c80d --- /dev/null +++ b/test/libsolidity/syntaxTests/array/length/bytes32_too_large.sol @@ -0,0 +1,5 @@ +contract C { + bytes32[8**90] ids; +} +// ---- +// TypeError: (25-30): Invalid array length, expected integer literal or constant expression. diff --git a/test/libsolidity/syntaxTests/array/length/bytes32_too_large_multidim.sol b/test/libsolidity/syntaxTests/array/length/bytes32_too_large_multidim.sol new file mode 100644 index 00000000..1344574c --- /dev/null +++ b/test/libsolidity/syntaxTests/array/length/bytes32_too_large_multidim.sol @@ -0,0 +1,5 @@ +contract C { + bytes32[8**90][500] ids; +} +// ---- +// TypeError: (25-30): Invalid array length, expected integer literal or constant expression. diff --git a/test/libsolidity/syntaxTests/arrayLength/can_be_constant_in_function.sol b/test/libsolidity/syntaxTests/array/length/can_be_constant_in_function.sol index 92536dd5..92536dd5 100644 --- a/test/libsolidity/syntaxTests/arrayLength/can_be_constant_in_function.sol +++ b/test/libsolidity/syntaxTests/array/length/can_be_constant_in_function.sol diff --git a/test/libsolidity/syntaxTests/arrayLength/can_be_constant_in_struct.sol b/test/libsolidity/syntaxTests/array/length/can_be_constant_in_struct.sol index 89e174f2..89e174f2 100644 --- a/test/libsolidity/syntaxTests/arrayLength/can_be_constant_in_struct.sol +++ b/test/libsolidity/syntaxTests/array/length/can_be_constant_in_struct.sol diff --git a/test/libsolidity/syntaxTests/arrayLength/can_be_recursive_constant.sol b/test/libsolidity/syntaxTests/array/length/can_be_recursive_constant.sol index 6810a9d6..6810a9d6 100644 --- a/test/libsolidity/syntaxTests/arrayLength/can_be_recursive_constant.sol +++ b/test/libsolidity/syntaxTests/array/length/can_be_recursive_constant.sol diff --git a/test/libsolidity/syntaxTests/array/length/cannot_be_function.sol b/test/libsolidity/syntaxTests/array/length/cannot_be_function.sol new file mode 100644 index 00000000..2ad97d27 --- /dev/null +++ b/test/libsolidity/syntaxTests/array/length/cannot_be_function.sol @@ -0,0 +1,6 @@ +contract C { + function f() public {} + uint[f] ids; +} +// ---- +// TypeError: (49-50): Invalid array length, expected integer literal or constant expression. diff --git a/test/libsolidity/syntaxTests/array/length/cannot_be_function_call.sol b/test/libsolidity/syntaxTests/array/length/cannot_be_function_call.sol new file mode 100644 index 00000000..bb8cc599 --- /dev/null +++ b/test/libsolidity/syntaxTests/array/length/cannot_be_function_call.sol @@ -0,0 +1,7 @@ +contract C { + function f(uint x) public {} + uint constant LEN = f(); + uint[LEN] ids; +} +// ---- +// TypeError: (84-87): Invalid array length, expected integer literal or constant expression. diff --git a/test/libsolidity/syntaxTests/array/length/complex_cyclic_constant.sol b/test/libsolidity/syntaxTests/array/length/complex_cyclic_constant.sol new file mode 100644 index 00000000..ee107078 --- /dev/null +++ b/test/libsolidity/syntaxTests/array/length/complex_cyclic_constant.sol @@ -0,0 +1,10 @@ +contract C { + uint constant L2 = LEN - 10; + uint constant L1 = L2 / 10; + uint constant LEN = 10 + L1 * 5; + function f() public { + uint[LEN] a; + } +} +// ---- +// TypeError: (36-39): Cyclic constant definition (or maximum recursion depth exhausted). diff --git a/test/libsolidity/syntaxTests/arrayLength/const_cannot_be_fractional.sol b/test/libsolidity/syntaxTests/array/length/const_cannot_be_fractional.sol index 397bbbcd..397bbbcd 100644 --- a/test/libsolidity/syntaxTests/arrayLength/const_cannot_be_fractional.sol +++ b/test/libsolidity/syntaxTests/array/length/const_cannot_be_fractional.sol diff --git a/test/libsolidity/syntaxTests/arrayLength/constant_var.sol b/test/libsolidity/syntaxTests/array/length/constant_var.sol index 41750250..41750250 100644 --- a/test/libsolidity/syntaxTests/arrayLength/constant_var.sol +++ b/test/libsolidity/syntaxTests/array/length/constant_var.sol diff --git a/test/libsolidity/syntaxTests/array/length/cyclic_constant.sol b/test/libsolidity/syntaxTests/array/length/cyclic_constant.sol new file mode 100644 index 00000000..3adc0e9b --- /dev/null +++ b/test/libsolidity/syntaxTests/array/length/cyclic_constant.sol @@ -0,0 +1,8 @@ +contract C { + uint constant LEN = LEN; + function f() public { + uint[LEN] a; + } +} +// ---- +// TypeError: (37-40): Cyclic constant definition (or maximum recursion depth exhausted). diff --git a/test/libsolidity/syntaxTests/array/length/fixed_size_multidim_zero_length.sol b/test/libsolidity/syntaxTests/array/length/fixed_size_multidim_zero_length.sol new file mode 100644 index 00000000..fd8f3078 --- /dev/null +++ b/test/libsolidity/syntaxTests/array/length/fixed_size_multidim_zero_length.sol @@ -0,0 +1,15 @@ +contract C { + function a() public pure returns(int[0][500] memory) {} + function b() public pure returns(uint[0][500] memory) {} + function c() public pure returns(byte[0][500] memory) {} + function d() public pure returns(bytes32[0][500] memory) {} + function e() public pure returns(bytes[0][500] memory) {} + function e() public pure returns(string[0][500] memory) {} +} +// ---- +// TypeError: (52-53): Array with zero length specified. +// TypeError: (111-112): Array with zero length specified. +// TypeError: (170-171): Array with zero length specified. +// TypeError: (232-233): Array with zero length specified. +// TypeError: (292-293): Array with zero length specified. +// TypeError: (353-354): Array with zero length specified. diff --git a/test/libsolidity/syntaxTests/array/length/fixed_size_zero_length.sol b/test/libsolidity/syntaxTests/array/length/fixed_size_zero_length.sol new file mode 100644 index 00000000..b38939e3 --- /dev/null +++ b/test/libsolidity/syntaxTests/array/length/fixed_size_zero_length.sol @@ -0,0 +1,15 @@ +contract C { + int[0] a; + uint[0] b; + byte[0] c; + bytes32[0] d; + bytes[0] e; + string[0] f; +} +// ---- +// TypeError: (19-20): Array with zero length specified. +// TypeError: (32-33): Array with zero length specified. +// TypeError: (45-46): Array with zero length specified. +// TypeError: (61-62): Array with zero length specified. +// TypeError: (75-76): Array with zero length specified. +// TypeError: (90-91): Array with zero length specified. diff --git a/test/libsolidity/syntaxTests/arrayLength/inline_array.sol b/test/libsolidity/syntaxTests/array/length/inline_array.sol index a30745d3..a30745d3 100644 --- a/test/libsolidity/syntaxTests/arrayLength/inline_array.sol +++ b/test/libsolidity/syntaxTests/array/length/inline_array.sol diff --git a/test/libsolidity/syntaxTests/arrayLength/invalid_expression_1.sol b/test/libsolidity/syntaxTests/array/length/invalid_expression_1.sol index c92861eb..c92861eb 100644 --- a/test/libsolidity/syntaxTests/arrayLength/invalid_expression_1.sol +++ b/test/libsolidity/syntaxTests/array/length/invalid_expression_1.sol diff --git a/test/libsolidity/syntaxTests/arrayLength/invalid_expression_2.sol b/test/libsolidity/syntaxTests/array/length/invalid_expression_2.sol index 92e3c3cf..92e3c3cf 100644 --- a/test/libsolidity/syntaxTests/arrayLength/invalid_expression_2.sol +++ b/test/libsolidity/syntaxTests/array/length/invalid_expression_2.sol diff --git a/test/libsolidity/syntaxTests/arrayLength/invalid_expression_3.sol b/test/libsolidity/syntaxTests/array/length/invalid_expression_3.sol index 26add45c..26add45c 100644 --- a/test/libsolidity/syntaxTests/arrayLength/invalid_expression_3.sol +++ b/test/libsolidity/syntaxTests/array/length/invalid_expression_3.sol diff --git a/test/libsolidity/syntaxTests/arrayLength/invalid_expression_4.sol b/test/libsolidity/syntaxTests/array/length/invalid_expression_4.sol index a0d58f4a..a0d58f4a 100644 --- a/test/libsolidity/syntaxTests/arrayLength/invalid_expression_4.sol +++ b/test/libsolidity/syntaxTests/array/length/invalid_expression_4.sol diff --git a/test/libsolidity/syntaxTests/arrayLength/invalid_expression_5.sol b/test/libsolidity/syntaxTests/array/length/invalid_expression_5.sol index 38a80867..38a80867 100644 --- a/test/libsolidity/syntaxTests/arrayLength/invalid_expression_5.sol +++ b/test/libsolidity/syntaxTests/array/length/invalid_expression_5.sol diff --git a/test/libsolidity/syntaxTests/arrayLength/non_integer_constant_var.sol b/test/libsolidity/syntaxTests/array/length/non_integer_constant_var.sol index 7a853a34..7a853a34 100644 --- a/test/libsolidity/syntaxTests/arrayLength/non_integer_constant_var.sol +++ b/test/libsolidity/syntaxTests/array/length/non_integer_constant_var.sol diff --git a/test/libsolidity/syntaxTests/arrayLength/not_convertible_to_integer.sol b/test/libsolidity/syntaxTests/array/length/not_convertible_to_integer.sol index b44ccfe9..b44ccfe9 100644 --- a/test/libsolidity/syntaxTests/arrayLength/not_convertible_to_integer.sol +++ b/test/libsolidity/syntaxTests/array/length/not_convertible_to_integer.sol diff --git a/test/libsolidity/syntaxTests/array/length/parameter_too_large.sol b/test/libsolidity/syntaxTests/array/length/parameter_too_large.sol new file mode 100644 index 00000000..02e0a7cc --- /dev/null +++ b/test/libsolidity/syntaxTests/array/length/parameter_too_large.sol @@ -0,0 +1,5 @@ +contract C { + function f(bytes32[1263941234127518272] memory) public pure {} +} +// ---- +// TypeError: (26-61): Array is too large to be encoded. diff --git a/test/libsolidity/syntaxTests/array/length/parameter_too_large_multidim.sol b/test/libsolidity/syntaxTests/array/length/parameter_too_large_multidim.sol new file mode 100644 index 00000000..5f96ecd5 --- /dev/null +++ b/test/libsolidity/syntaxTests/array/length/parameter_too_large_multidim.sol @@ -0,0 +1,11 @@ +contract C { + function f(bytes32[1263941234127518272][500] memory) public pure {} + function f(uint[2**30][] memory) public pure {} + function f(uint[2**30][2**30][] memory) public pure {} + function f(uint[2**16][2**16][] memory) public pure {} +} +// ---- +// TypeError: (26-66): Array is too large to be encoded. +// TypeError: (96-116): Array is too large to be encoded. +// TypeError: (146-173): Array is too large to be encoded. +// TypeError: (203-230): Array is too large to be encoded. diff --git a/test/libsolidity/syntaxTests/array/length/parameter_too_large_multidim_ABIv2.sol b/test/libsolidity/syntaxTests/array/length/parameter_too_large_multidim_ABIv2.sol new file mode 100644 index 00000000..de1fde3f --- /dev/null +++ b/test/libsolidity/syntaxTests/array/length/parameter_too_large_multidim_ABIv2.sol @@ -0,0 +1,10 @@ +pragma experimental ABIEncoderV2; + +contract C { + function f(bytes32[1263941234127518272][500] memory) public pure {} + function f(uint[2**30][2**30][][] memory) public pure {} +} +// ---- +// Warning: (0-33): Experimental features are turned on. Do not use experimental features on live deployments. +// TypeError: (61-101): Array is too large to be encoded. +// TypeError: (131-160): Array is too large to be encoded. diff --git a/test/libsolidity/syntaxTests/array/length/parentheses.sol b/test/libsolidity/syntaxTests/array/length/parentheses.sol new file mode 100644 index 00000000..8dbcc0a4 --- /dev/null +++ b/test/libsolidity/syntaxTests/array/length/parentheses.sol @@ -0,0 +1,25 @@ +contract C { + uint constant L1 = (2); + uint constant L2 = ((2)); + uint constant L3 = ((((2)))); + uint constant L4 = (2 + 1); + uint constant L5 = ((2 + 1)); + uint constant L6 = (((2) + ((1)))); + uint constant L7 = (2 + 1) / 1; + uint constant L8 = (2 + ((1))) / (1); + uint[L1] a1; + uint[L2] a2; + uint[L3] a3; + uint[L4] a4; + uint[L5] a5; + uint[L6] a6; + uint[L7] a7; + uint[L8] a8; + uint[(2)] a9; + uint[(2 + 1)] a10; + uint[(2 + 1) + 1] a11; + uint[((2) + 1) + 1] a12; + uint[(2 + 1) + ((1))] a13; + uint[(((2) + 1)) + (((1)))] a14; + uint[((((3) + 1)) + (((1))))%2] a15; +} diff --git a/test/libsolidity/syntaxTests/arrayLength/pure_functions.sol b/test/libsolidity/syntaxTests/array/length/pure_functions.sol index b620db76..b620db76 100644 --- a/test/libsolidity/syntaxTests/arrayLength/pure_functions.sol +++ b/test/libsolidity/syntaxTests/array/length/pure_functions.sol diff --git a/test/libsolidity/syntaxTests/arrayLength/too_large.sol b/test/libsolidity/syntaxTests/array/length/too_large.sol index c90a7494..c90a7494 100644 --- a/test/libsolidity/syntaxTests/arrayLength/too_large.sol +++ b/test/libsolidity/syntaxTests/array/length/too_large.sol diff --git a/test/libsolidity/syntaxTests/arrayLength/tuples.sol b/test/libsolidity/syntaxTests/array/length/tuples.sol index bc10b3b5..bc10b3b5 100644 --- a/test/libsolidity/syntaxTests/arrayLength/tuples.sol +++ b/test/libsolidity/syntaxTests/array/length/tuples.sol diff --git a/test/libsolidity/syntaxTests/array/length/uint_too_large_multidim.sol b/test/libsolidity/syntaxTests/array/length/uint_too_large_multidim.sol new file mode 100644 index 00000000..901bc28a --- /dev/null +++ b/test/libsolidity/syntaxTests/array/length/uint_too_large_multidim.sol @@ -0,0 +1,5 @@ +contract C { + uint[8**90][500] ids; +} +// ---- +// TypeError: (22-27): Invalid array length, expected integer literal or constant expression. diff --git a/test/libsolidity/syntaxTests/array/no_array_pop.sol b/test/libsolidity/syntaxTests/array/no_array_pop.sol new file mode 100644 index 00000000..79a68ef1 --- /dev/null +++ b/test/libsolidity/syntaxTests/array/no_array_pop.sol @@ -0,0 +1,8 @@ +contract C { + uint data; + function test() public { + data.pop(); + } +} +// ---- +// TypeError: (63-71): Member "pop" not found or not visible after argument-dependent lookup in uint256. diff --git a/test/libsolidity/syntaxTests/array/static_storage_array_pop.sol b/test/libsolidity/syntaxTests/array/static_storage_array_pop.sol new file mode 100644 index 00000000..8414f43d --- /dev/null +++ b/test/libsolidity/syntaxTests/array/static_storage_array_pop.sol @@ -0,0 +1,8 @@ +contract C { + uint[3] data; + function test() public { + data.pop(); + } +} +// ---- +// TypeError: (66-74): Member "pop" not found or not visible after argument-dependent lookup in uint256[3] storage ref. diff --git a/test/libsolidity/syntaxTests/array/string_pop.sol b/test/libsolidity/syntaxTests/array/string_pop.sol new file mode 100644 index 00000000..700fda16 --- /dev/null +++ b/test/libsolidity/syntaxTests/array/string_pop.sol @@ -0,0 +1,8 @@ +contract C { + string data; + function test() public { + data.pop(); + } +} +// ---- +// TypeError: (65-73): Member "pop" not found or not visible after argument-dependent lookup in string storage ref. diff --git a/test/libsolidity/syntaxTests/array/uninitialized_storage_var.sol b/test/libsolidity/syntaxTests/array/uninitialized_storage_var.sol new file mode 100644 index 00000000..f3be9071 --- /dev/null +++ b/test/libsolidity/syntaxTests/array/uninitialized_storage_var.sol @@ -0,0 +1,9 @@ +contract C { + function f() public { + uint[] storage x; + uint[10] storage y; + } +} +// ---- +// DeclarationError: (38-54): Uninitialized storage pointer. +// DeclarationError: (58-76): Uninitialized storage pointer. diff --git a/test/libsolidity/syntaxTests/arrayLength/array_length_cannot_be_constant_function_parameter.sol b/test/libsolidity/syntaxTests/arrayLength/array_length_cannot_be_constant_function_parameter.sol deleted file mode 100644 index 11d40f26..00000000 --- a/test/libsolidity/syntaxTests/arrayLength/array_length_cannot_be_constant_function_parameter.sol +++ /dev/null @@ -1,7 +0,0 @@ -contract C { - function f(uint constant LEN) { - uint[LEN] a; - } -} -// ---- -// TypeError: (62-65): Invalid array length, expected integer literal or constant expression. diff --git a/test/libsolidity/syntaxTests/arrayLength/cannot_be_function.sol b/test/libsolidity/syntaxTests/arrayLength/cannot_be_function.sol deleted file mode 100644 index ac3abc4c..00000000 --- a/test/libsolidity/syntaxTests/arrayLength/cannot_be_function.sol +++ /dev/null @@ -1,6 +0,0 @@ -contract C { - function f() {} - uint[f] ids; -} -// ---- -// TypeError: (42-43): Invalid array length, expected integer literal or constant expression. diff --git a/test/libsolidity/syntaxTests/arrayLength/cannot_be_function_call.sol b/test/libsolidity/syntaxTests/arrayLength/cannot_be_function_call.sol deleted file mode 100644 index a6863955..00000000 --- a/test/libsolidity/syntaxTests/arrayLength/cannot_be_function_call.sol +++ /dev/null @@ -1,7 +0,0 @@ -contract C { - function f(uint x) {} - uint constant LEN = f(); - uint[LEN] ids; -} -// ---- -// TypeError: (77-80): Invalid array length, expected integer literal or constant expression. diff --git a/test/libsolidity/syntaxTests/arrayLength/complex_cyclic_constant.sol b/test/libsolidity/syntaxTests/arrayLength/complex_cyclic_constant.sol deleted file mode 100644 index 254f9f02..00000000 --- a/test/libsolidity/syntaxTests/arrayLength/complex_cyclic_constant.sol +++ /dev/null @@ -1,10 +0,0 @@ -contract C { - uint constant L2 = LEN - 10; - uint constant L1 = L2 / 10; - uint constant LEN = 10 + L1 * 5; - function f() { - uint[LEN] a; - } -} -// ---- -// TypeError: (36-39): Cyclic constant definition (or maximum recursion depth exhausted). diff --git a/test/libsolidity/syntaxTests/arrayLength/cyclic_constant.sol b/test/libsolidity/syntaxTests/arrayLength/cyclic_constant.sol deleted file mode 100644 index 91ba9045..00000000 --- a/test/libsolidity/syntaxTests/arrayLength/cyclic_constant.sol +++ /dev/null @@ -1,8 +0,0 @@ -contract C { - uint constant LEN = LEN; - function f() { - uint[LEN] a; - } -} -// ---- -// TypeError: (37-40): Cyclic constant definition (or maximum recursion depth exhausted). diff --git a/test/libsolidity/syntaxTests/arrayLength/parentheses.sol b/test/libsolidity/syntaxTests/arrayLength/parentheses.sol deleted file mode 100644 index 40f55ad6..00000000 --- a/test/libsolidity/syntaxTests/arrayLength/parentheses.sol +++ /dev/null @@ -1,25 +0,0 @@ -contract C { - uint constant L1 = (2); - uint constant L2 = ((2)); - uint constant L3 = ((((2)))); - uint constant L4 = (2 + 1); - uint constant L5 = ((2 + 1)); - uint constant L6 = (((2) + ((1)))); - uint constant L7 = (2 + 1) / 1; - uint constant L8 = (2 + ((1))) / (1); - uint[L1] a1; - uint[L2] a2; - uint[L3] a3; - uint[L4] a4; - uint[L5] a5; - uint[L6] a6; - uint[L7] a7; - uint[L8] a8; - uint[(2)] a9; - uint[(2 + 1)] a10; - uint[(2 + 1) + 1] a11; - uint[((2) + 1) + 1] a12; - uint[(2 + 1) + ((1))] a13; - uint[(((2) + 1)) + (((1)))] a14; - uint[((((2) + 1)) + (((1))))%1] a15; -} diff --git a/test/libsolidity/syntaxTests/constants/assign_constant_function_value.sol b/test/libsolidity/syntaxTests/constants/assign_constant_function_value.sol index 88e94e29..0e242b30 100644 --- a/test/libsolidity/syntaxTests/constants/assign_constant_function_value.sol +++ b/test/libsolidity/syntaxTests/constants/assign_constant_function_value.sol @@ -3,4 +3,4 @@ contract C { uint constant y = x(); } // ---- -// Warning: (74-77): Initial value for constant variable has to be compile-time constant. This will fail to compile with the next breaking version change. +// TypeError: (74-77): Initial value for constant variable has to be compile-time constant. diff --git a/test/libsolidity/syntaxTests/constants/assign_constant_function_value_050.sol b/test/libsolidity/syntaxTests/constants/assign_constant_function_value_050.sol deleted file mode 100644 index 2c92899d..00000000 --- a/test/libsolidity/syntaxTests/constants/assign_constant_function_value_050.sol +++ /dev/null @@ -1,8 +0,0 @@ -pragma experimental "v0.5.0"; - -contract C { - function () pure returns (uint) x; - uint constant y = x(); -} -// ---- -// TypeError: (105-108): Initial value for constant variable has to be compile-time constant. diff --git a/test/libsolidity/syntaxTests/constants/cyclic_dependency_2.sol b/test/libsolidity/syntaxTests/constants/cyclic_dependency_2.sol index 08d20c3a..9f1d9722 100644 --- a/test/libsolidity/syntaxTests/constants/cyclic_dependency_2.sol +++ b/test/libsolidity/syntaxTests/constants/cyclic_dependency_2.sol @@ -1,12 +1,10 @@ contract C { uint constant a = b * c; uint constant b = 7; - uint constant c = b + uint(keccak256(d)); + uint constant c = b + uint(keccak256(abi.encodePacked(d))); uint constant d = 2 + a; } // ---- -// Warning: (98-110): This function only accepts a single "bytes" argument. Please use "abi.encodePacked(...)" or a similar function to encode the data. -// Warning: (98-110): The provided argument of type uint256 is not implicitly convertible to expected type bytes memory. // TypeError: (17-40): The value of the constant a has a cyclic dependency via c. -// TypeError: (71-111): The value of the constant c has a cyclic dependency via d. -// TypeError: (117-140): The value of the constant d has a cyclic dependency via a. +// TypeError: (71-129): The value of the constant c has a cyclic dependency via d. +// TypeError: (135-158): The value of the constant d has a cyclic dependency via a. diff --git a/test/libsolidity/syntaxTests/constants/cyclic_dependency_4.sol b/test/libsolidity/syntaxTests/constants/cyclic_dependency_4.sol index df5cd969..cc34fad2 100644 --- a/test/libsolidity/syntaxTests/constants/cyclic_dependency_4.sol +++ b/test/libsolidity/syntaxTests/constants/cyclic_dependency_4.sol @@ -1,9 +1,7 @@ contract C { uint constant a = b * c; uint constant b = 7; - uint constant c = 4 + uint(keccak256(d)); + uint constant c = 4 + uint(keccak256(abi.encode(d))); uint constant d = 2 + b; } // ---- -// Warning: (98-110): This function only accepts a single "bytes" argument. Please use "abi.encodePacked(...)" or a similar function to encode the data. -// Warning: (98-110): The provided argument of type uint256 is not implicitly convertible to expected type bytes memory. diff --git a/test/libsolidity/syntaxTests/constructor/constructible_internal_constructor_new.sol b/test/libsolidity/syntaxTests/constructor/constructible_internal_constructor.sol index 8dee4c71..8dee4c71 100644 --- a/test/libsolidity/syntaxTests/constructor/constructible_internal_constructor_new.sol +++ b/test/libsolidity/syntaxTests/constructor/constructible_internal_constructor.sol diff --git a/test/libsolidity/syntaxTests/constructor/constructible_internal_constructor_old.sol b/test/libsolidity/syntaxTests/constructor/constructible_internal_constructor_old.sol deleted file mode 100644 index 144743e3..00000000 --- a/test/libsolidity/syntaxTests/constructor/constructible_internal_constructor_old.sol +++ /dev/null @@ -1,9 +0,0 @@ -contract C { - function C() internal {} -} -contract D is C { - function D() public {} -} -// ---- -// Warning: (14-38): Defining constructors as functions with the same name as the contract is deprecated. Use "constructor(...) { ... }" instead. -// Warning: (60-82): Defining constructors as functions with the same name as the contract is deprecated. Use "constructor(...) { ... }" instead. diff --git a/test/libsolidity/syntaxTests/constructor/constructor_new.sol b/test/libsolidity/syntaxTests/constructor/constructor.sol index aa3422cc..aa3422cc 100644 --- a/test/libsolidity/syntaxTests/constructor/constructor_new.sol +++ b/test/libsolidity/syntaxTests/constructor/constructor.sol diff --git a/test/libsolidity/syntaxTests/constructor/constructor_no_visibility.sol b/test/libsolidity/syntaxTests/constructor/constructor_no_visibility.sol new file mode 100644 index 00000000..586329b1 --- /dev/null +++ b/test/libsolidity/syntaxTests/constructor/constructor_no_visibility.sol @@ -0,0 +1,3 @@ +contract A { constructor() {} } +// ---- +// SyntaxError: (13-29): No visibility specified. Did you intend to add "public"? diff --git a/test/libsolidity/syntaxTests/constructor/constructor_old.sol b/test/libsolidity/syntaxTests/constructor/constructor_old.sol index 9ec6257d..9ead6858 100644 --- a/test/libsolidity/syntaxTests/constructor/constructor_old.sol +++ b/test/libsolidity/syntaxTests/constructor/constructor_old.sol @@ -1,3 +1,4 @@ contract A { function A() public {} } // ---- -// Warning: (13-35): Defining constructors as functions with the same name as the contract is deprecated. Use "constructor(...) { ... }" instead. +// SyntaxError: (13-35): Functions are not allowed to have the same name as the contract. If you intend this to be a constructor, use "constructor(...) { ... }" to define it. +// Warning: (13-35): This declaration shadows an existing declaration. diff --git a/test/libsolidity/syntaxTests/constructor/constructor_old_050.sol b/test/libsolidity/syntaxTests/constructor/constructor_old_050.sol deleted file mode 100644 index 19e46e79..00000000 --- a/test/libsolidity/syntaxTests/constructor/constructor_old_050.sol +++ /dev/null @@ -1,4 +0,0 @@ -pragma experimental "v0.5.0"; -contract A { function A() public {} } -// ---- -// SyntaxError: (43-65): Functions are not allowed to have the same name as the contract. If you intend this to be a constructor, use "constructor(...) { ... }" to define it. diff --git a/test/libsolidity/syntaxTests/constructor/constructor_state_mutability.sol b/test/libsolidity/syntaxTests/constructor/constructor_state_mutability.sol new file mode 100644 index 00000000..39bf6384 --- /dev/null +++ b/test/libsolidity/syntaxTests/constructor/constructor_state_mutability.sol @@ -0,0 +1,9 @@ +contract test1 { + constructor() public view {} +} +contract test2 { + constructor() public pure {} +} +// ---- +// TypeError: (19-47): Constructor must be payable or non-payable, but is "view". +// TypeError: (69-97): Constructor must be payable or non-payable, but is "pure". diff --git a/test/libsolidity/syntaxTests/constructor/constructor_state_mutability_new.sol b/test/libsolidity/syntaxTests/constructor/constructor_state_mutability_new.sol deleted file mode 100644 index 15ed0e1e..00000000 --- a/test/libsolidity/syntaxTests/constructor/constructor_state_mutability_new.sol +++ /dev/null @@ -1,13 +0,0 @@ -contract test1 { - constructor() constant {} -} -contract test2 { - constructor() view {} -} -contract test3 { - constructor() pure {} -} -// ---- -// TypeError: (19-44): Constructor must be payable or non-payable, but is "view". -// TypeError: (66-87): Constructor must be payable or non-payable, but is "view". -// TypeError: (109-130): Constructor must be payable or non-payable, but is "pure". diff --git a/test/libsolidity/syntaxTests/constructor/constructor_state_mutability_old.sol b/test/libsolidity/syntaxTests/constructor/constructor_state_mutability_old.sol deleted file mode 100644 index 6dbcbc97..00000000 --- a/test/libsolidity/syntaxTests/constructor/constructor_state_mutability_old.sol +++ /dev/null @@ -1,16 +0,0 @@ -contract test1 { - function test1() constant {} -} -contract test2 { - function test2() view {} -} -contract test3 { - function test3() pure {} -} -// ---- -// Warning: (21-49): Defining constructors as functions with the same name as the contract is deprecated. Use "constructor(...) { ... }" instead. -// Warning: (73-97): Defining constructors as functions with the same name as the contract is deprecated. Use "constructor(...) { ... }" instead. -// Warning: (121-145): Defining constructors as functions with the same name as the contract is deprecated. Use "constructor(...) { ... }" instead. -// TypeError: (21-49): Constructor must be payable or non-payable, but is "view". -// TypeError: (73-97): Constructor must be payable or non-payable, but is "view". -// TypeError: (121-145): Constructor must be payable or non-payable, but is "pure". diff --git a/test/libsolidity/syntaxTests/constructor/constructor_visibility.sol b/test/libsolidity/syntaxTests/constructor/constructor_visibility.sol new file mode 100644 index 00000000..f9c4b9b9 --- /dev/null +++ b/test/libsolidity/syntaxTests/constructor/constructor_visibility.sol @@ -0,0 +1,12 @@ +// The constructor of a base class should not be visible in the derived class +contract A { constructor(string memory) public { } } +contract B is A { + function f() pure public { + A x = A(0); // convert from address + string memory y = "ab"; + A(y); // call as a function is invalid + x; + } +} +// ---- +// TypeError: (250-254): Explicit type conversion not allowed from "string memory" to "contract A". diff --git a/test/libsolidity/syntaxTests/constructor/constructor_visibility_new.sol b/test/libsolidity/syntaxTests/constructor/constructor_visibility_new.sol deleted file mode 100644 index 502dc029..00000000 --- a/test/libsolidity/syntaxTests/constructor/constructor_visibility_new.sol +++ /dev/null @@ -1,12 +0,0 @@ -// The constructor of a base class should not be visible in the derived class -contract A { constructor(string) public { } } -contract B is A { - function f() pure public { - A x = A(0); // convert from address - string memory y = "ab"; - A(y); // call as a function is invalid - x; - } -} -// ---- -// TypeError: (243-247): Explicit type conversion not allowed from "string memory" to "contract A". diff --git a/test/libsolidity/syntaxTests/constructor/constructor_visibility_old.sol b/test/libsolidity/syntaxTests/constructor/constructor_visibility_old.sol deleted file mode 100644 index 847ea27b..00000000 --- a/test/libsolidity/syntaxTests/constructor/constructor_visibility_old.sol +++ /dev/null @@ -1,13 +0,0 @@ -// The constructor of a base class should not be visible in the derived class -contract A { function A(string s) public { } } -contract B is A { - function f() pure public { - A x = A(0); // convert from address - string memory y = "ab"; - A(y); // call as a function is invalid - x; - } -} -// ---- -// Warning: (91-122): Defining constructors as functions with the same name as the contract is deprecated. Use "constructor(...) { ... }" instead. -// TypeError: (244-248): Explicit type conversion not allowed from "string memory" to "contract A". diff --git a/test/libsolidity/syntaxTests/constructor/constructor_without_implementation.sol b/test/libsolidity/syntaxTests/constructor/constructor_without_implementation.sol new file mode 100644 index 00000000..6bbb83ce --- /dev/null +++ b/test/libsolidity/syntaxTests/constructor/constructor_without_implementation.sol @@ -0,0 +1,5 @@ +contract C { + constructor() public; +} +// ---- +// TypeError: (14-35): Constructor must be implemented if declared. diff --git a/test/libsolidity/syntaxTests/constructor/constructor_without_implementation_new.sol b/test/libsolidity/syntaxTests/constructor/constructor_without_implementation_new.sol deleted file mode 100644 index 5e619143..00000000 --- a/test/libsolidity/syntaxTests/constructor/constructor_without_implementation_new.sol +++ /dev/null @@ -1,5 +0,0 @@ -contract C { - constructor(); -} -// ---- -// TypeError: (14-28): Constructor must be implemented if declared. diff --git a/test/libsolidity/syntaxTests/constructor/constructor_without_implementation_old.sol b/test/libsolidity/syntaxTests/constructor/constructor_without_implementation_old.sol deleted file mode 100644 index 72458703..00000000 --- a/test/libsolidity/syntaxTests/constructor/constructor_without_implementation_old.sol +++ /dev/null @@ -1,6 +0,0 @@ -contract C { - function C(); -} -// ---- -// Warning: (14-27): Defining constructors as functions with the same name as the contract is deprecated. Use "constructor(...) { ... }" instead. -// TypeError: (14-27): Constructor must be implemented if declared. diff --git a/test/libsolidity/syntaxTests/constructor/external_constructor_new.sol b/test/libsolidity/syntaxTests/constructor/external_constructor.sol index 30cf0668..30cf0668 100644 --- a/test/libsolidity/syntaxTests/constructor/external_constructor_new.sol +++ b/test/libsolidity/syntaxTests/constructor/external_constructor.sol diff --git a/test/libsolidity/syntaxTests/constructor/external_constructor_old.sol b/test/libsolidity/syntaxTests/constructor/external_constructor_old.sol deleted file mode 100644 index 27869361..00000000 --- a/test/libsolidity/syntaxTests/constructor/external_constructor_old.sol +++ /dev/null @@ -1,6 +0,0 @@ -contract test { - function test() external {} -} -// ---- -// Warning: (17-44): Defining constructors as functions with the same name as the contract is deprecated. Use "constructor(...) { ... }" instead. -// TypeError: (17-44): Constructor must be public or internal. diff --git a/test/libsolidity/syntaxTests/constructor/function_named_constructor.sol b/test/libsolidity/syntaxTests/constructor/function_named_constructor.sol index 29784033..68273c0a 100644 --- a/test/libsolidity/syntaxTests/constructor/function_named_constructor.sol +++ b/test/libsolidity/syntaxTests/constructor/function_named_constructor.sol @@ -2,4 +2,4 @@ contract C { function constructor() public; } // ---- -// Warning: (17-47): This function is named "constructor" but is not the constructor of the contract. If you intend this to be a constructor, use "constructor(...) { ... }" without the "function" keyword to define it. +// ParserError: (26-37): This function is named "constructor" but is not the constructor of the contract. If you intend this to be a constructor, use "constructor(...) { ... }" without the "function" keyword to define it. diff --git a/test/libsolidity/syntaxTests/constructor/inconstructible_internal_constructor_new.sol b/test/libsolidity/syntaxTests/constructor/inconstructible_internal_constructor.sol index 2511c751..2511c751 100644 --- a/test/libsolidity/syntaxTests/constructor/inconstructible_internal_constructor_new.sol +++ b/test/libsolidity/syntaxTests/constructor/inconstructible_internal_constructor.sol diff --git a/test/libsolidity/syntaxTests/constructor/inconstructible_internal_constructor_inverted.sol b/test/libsolidity/syntaxTests/constructor/inconstructible_internal_constructor_inverted.sol new file mode 100644 index 00000000..17cb701d --- /dev/null +++ b/test/libsolidity/syntaxTests/constructor/inconstructible_internal_constructor_inverted.sol @@ -0,0 +1,13 @@ +// Previously, the type information for A was not yet available at the point of +// "new A". +contract B { + A a; + constructor() public { + a = new A(address(this)); + } +} +contract A { + constructor(address) internal {} +} +// ---- +// TypeError: (141-146): Contract with internal constructor cannot be created directly. diff --git a/test/libsolidity/syntaxTests/constructor/inconstructible_internal_constructor_inverted_new.sol b/test/libsolidity/syntaxTests/constructor/inconstructible_internal_constructor_inverted_new.sol deleted file mode 100644 index 2a199b3a..00000000 --- a/test/libsolidity/syntaxTests/constructor/inconstructible_internal_constructor_inverted_new.sol +++ /dev/null @@ -1,13 +0,0 @@ -// Previously, the type information for A was not yet available at the point of -// "new A". -contract B { - A a; - constructor() public { - a = new A(this); - } -} -contract A { - constructor(address a) internal {} -} -// ---- -// TypeError: (141-146): Contract with internal constructor cannot be created directly. diff --git a/test/libsolidity/syntaxTests/constructor/inconstructible_internal_constructor_inverted_old.sol b/test/libsolidity/syntaxTests/constructor/inconstructible_internal_constructor_inverted_old.sol deleted file mode 100644 index 0a27e9f8..00000000 --- a/test/libsolidity/syntaxTests/constructor/inconstructible_internal_constructor_inverted_old.sol +++ /dev/null @@ -1,15 +0,0 @@ -// Previously, the type information for A was not yet available at the point of -// "new A". -contract B { - A a; - function B() public { - a = new A(this); - } -} -contract A { - function A(address a) internal {} -} -// ---- -// Warning: (112-155): Defining constructors as functions with the same name as the contract is deprecated. Use "constructor(...) { ... }" instead. -// Warning: (172-205): Defining constructors as functions with the same name as the contract is deprecated. Use "constructor(...) { ... }" instead. -// TypeError: (140-145): Contract with internal constructor cannot be created directly. diff --git a/test/libsolidity/syntaxTests/constructor/inconstructible_internal_constructor_old.sol b/test/libsolidity/syntaxTests/constructor/inconstructible_internal_constructor_old.sol deleted file mode 100644 index 2897e6f3..00000000 --- a/test/libsolidity/syntaxTests/constructor/inconstructible_internal_constructor_old.sol +++ /dev/null @@ -1,9 +0,0 @@ -contract C { - function C() internal {} -} -contract D { - function f() public { C x = new C(); x; } -} -// ---- -// Warning: (14-38): Defining constructors as functions with the same name as the contract is deprecated. Use "constructor(...) { ... }" instead. -// TypeError: (83-88): Contract with internal constructor cannot be created directly. diff --git a/test/libsolidity/syntaxTests/constructor/interface_constructor.sol b/test/libsolidity/syntaxTests/constructor/interface_constructor.sol new file mode 100644 index 00000000..87585a62 --- /dev/null +++ b/test/libsolidity/syntaxTests/constructor/interface_constructor.sol @@ -0,0 +1,7 @@ +interface I { + constructor() public; +} +// ---- +// TypeError: (15-36): Functions in interfaces must be declared external. +// TypeError: (15-36): Constructor cannot be defined in interfaces. +// TypeError: (15-36): Constructor must be implemented if declared. diff --git a/test/libsolidity/syntaxTests/constructor/interface_constructor_new.sol b/test/libsolidity/syntaxTests/constructor/interface_constructor_new.sol deleted file mode 100644 index fa5d54c4..00000000 --- a/test/libsolidity/syntaxTests/constructor/interface_constructor_new.sol +++ /dev/null @@ -1,7 +0,0 @@ -interface I { - constructor(); -} -// ---- -// Warning: (15-29): Functions in interfaces should be declared external. -// TypeError: (15-29): Constructor cannot be defined in interfaces. -// TypeError: (15-29): Constructor must be implemented if declared. diff --git a/test/libsolidity/syntaxTests/constructor/interface_constructor_old.sol b/test/libsolidity/syntaxTests/constructor/interface_constructor_old.sol deleted file mode 100644 index ddf54977..00000000 --- a/test/libsolidity/syntaxTests/constructor/interface_constructor_old.sol +++ /dev/null @@ -1,8 +0,0 @@ -interface I { - function I(); -} -// ---- -// Warning: (15-28): Defining constructors as functions with the same name as the contract is deprecated. Use "constructor(...) { ... }" instead. -// Warning: (15-28): Functions in interfaces should be declared external. -// TypeError: (15-28): Constructor cannot be defined in interfaces. -// TypeError: (15-28): Constructor must be implemented if declared. diff --git a/test/libsolidity/syntaxTests/constructor/library_constructor.sol b/test/libsolidity/syntaxTests/constructor/library_constructor.sol new file mode 100644 index 00000000..38934f8d --- /dev/null +++ b/test/libsolidity/syntaxTests/constructor/library_constructor.sol @@ -0,0 +1,6 @@ +library Lib { + constructor() public; +} +// ---- +// TypeError: (15-36): Constructor cannot be defined in libraries. +// TypeError: (15-36): Constructor must be implemented if declared. diff --git a/test/libsolidity/syntaxTests/constructor/library_constructor_new.sol b/test/libsolidity/syntaxTests/constructor/library_constructor_new.sol deleted file mode 100644 index 8db7e62a..00000000 --- a/test/libsolidity/syntaxTests/constructor/library_constructor_new.sol +++ /dev/null @@ -1,6 +0,0 @@ -library Lib { - constructor(); -} -// ---- -// TypeError: (15-29): Constructor cannot be defined in libraries. -// TypeError: (15-29): Constructor must be implemented if declared. diff --git a/test/libsolidity/syntaxTests/constructor/library_constructor_old.sol b/test/libsolidity/syntaxTests/constructor/library_constructor_old.sol deleted file mode 100644 index d4499049..00000000 --- a/test/libsolidity/syntaxTests/constructor/library_constructor_old.sol +++ /dev/null @@ -1,7 +0,0 @@ -library Lib { - function Lib(); -} -// ---- -// Warning: (15-30): Defining constructors as functions with the same name as the contract is deprecated. Use "constructor(...) { ... }" instead. -// TypeError: (15-30): Constructor cannot be defined in libraries. -// TypeError: (15-30): Constructor must be implemented if declared. diff --git a/test/libsolidity/syntaxTests/constructor/overriding_constructor.sol b/test/libsolidity/syntaxTests/constructor/overriding_constructor.sol index 3290a33b..30cf3bce 100644 --- a/test/libsolidity/syntaxTests/constructor/overriding_constructor.sol +++ b/test/libsolidity/syntaxTests/constructor/overriding_constructor.sol @@ -1,6 +1,10 @@ -// It is fine to "override" constructor of a base class since it is invisible -contract A { function A() public { } } -contract B is A { function A() public pure returns (uint8) {} } +contract A { function f() public {} } +contract B is A { + function A() public pure returns (uint8) {} + function g() public { + A.f(); + } +} // ---- -// Warning: (91-114): Defining constructors as functions with the same name as the contract is deprecated. Use "constructor(...) { ... }" instead. -// Warning: (135-178): This declaration shadows an existing declaration. +// Warning: (58-101): This declaration shadows an existing declaration. +// TypeError: (130-133): Member "f" not found or not visible after argument-dependent lookup in function () pure returns (uint8). diff --git a/test/libsolidity/syntaxTests/constructor/returns_in_constructor_new.sol b/test/libsolidity/syntaxTests/constructor/returns_in_constructor.sol index e6a03014..e6a03014 100644 --- a/test/libsolidity/syntaxTests/constructor/returns_in_constructor_new.sol +++ b/test/libsolidity/syntaxTests/constructor/returns_in_constructor.sol diff --git a/test/libsolidity/syntaxTests/constructor/returns_in_constructor_old.sol b/test/libsolidity/syntaxTests/constructor/returns_in_constructor_old.sol deleted file mode 100644 index 00b3974c..00000000 --- a/test/libsolidity/syntaxTests/constructor/returns_in_constructor_old.sol +++ /dev/null @@ -1,6 +0,0 @@ -contract test { - function test() public returns (uint a) { } -} -// ---- -// Warning: (17-60): Defining constructors as functions with the same name as the contract is deprecated. Use "constructor(...) { ... }" instead. -// TypeError: (48-56): Non-empty "returns" directive for constructor. diff --git a/test/libsolidity/syntaxTests/constructor/two_constructors_new.sol b/test/libsolidity/syntaxTests/constructor/two_constructors.sol index 42c0de28..42c0de28 100644 --- a/test/libsolidity/syntaxTests/constructor/two_constructors_new.sol +++ b/test/libsolidity/syntaxTests/constructor/two_constructors.sol diff --git a/test/libsolidity/syntaxTests/constructor/two_constructors_mixed.sol b/test/libsolidity/syntaxTests/constructor/two_constructors_mixed.sol deleted file mode 100644 index c757354e..00000000 --- a/test/libsolidity/syntaxTests/constructor/two_constructors_mixed.sol +++ /dev/null @@ -1,7 +0,0 @@ -contract test { - function test(uint) public { } - constructor() public {} -} -// ---- -// Warning: (17-47): Defining constructors as functions with the same name as the contract is deprecated. Use "constructor(...) { ... }" instead. -// DeclarationError: (49-72): More than one constructor defined. diff --git a/test/libsolidity/syntaxTests/constructor/two_constructors_old.sol b/test/libsolidity/syntaxTests/constructor/two_constructors_old.sol deleted file mode 100644 index db632ced..00000000 --- a/test/libsolidity/syntaxTests/constructor/two_constructors_old.sol +++ /dev/null @@ -1,8 +0,0 @@ -contract test { - function test(uint a) public { } - function test() public {} -} -// ---- -// Warning: (17-49): Defining constructors as functions with the same name as the contract is deprecated. Use "constructor(...) { ... }" instead. -// Warning: (51-76): Defining constructors as functions with the same name as the contract is deprecated. Use "constructor(...) { ... }" instead. -// DeclarationError: (51-76): More than one constructor defined. diff --git a/test/libsolidity/syntaxTests/controlFlow/mappingReturn/named_err.sol b/test/libsolidity/syntaxTests/controlFlow/mappingReturn/named_err.sol new file mode 100644 index 00000000..35420b6d --- /dev/null +++ b/test/libsolidity/syntaxTests/controlFlow/mappingReturn/named_err.sol @@ -0,0 +1,5 @@ +contract C { + function f() internal pure returns (mapping(uint=>uint) storage r) { } +} +// ---- +// TypeError: (53-82): This variable is of storage pointer type and might be returned without assignment and could be used uninitialized. Assign the variable (potentially from itself) to fix this error. diff --git a/test/libsolidity/syntaxTests/controlFlow/mappingReturn/named_fine.sol b/test/libsolidity/syntaxTests/controlFlow/mappingReturn/named_fine.sol new file mode 100644 index 00000000..4146192f --- /dev/null +++ b/test/libsolidity/syntaxTests/controlFlow/mappingReturn/named_fine.sol @@ -0,0 +1,5 @@ +contract C { + mapping(uint=>uint) m; + function f() internal view returns (mapping(uint=>uint) storage r) { r = m; } +} +// ---- diff --git a/test/libsolidity/syntaxTests/controlFlow/mappingReturn/unnamed_err.sol b/test/libsolidity/syntaxTests/controlFlow/mappingReturn/unnamed_err.sol new file mode 100644 index 00000000..52a8b3d7 --- /dev/null +++ b/test/libsolidity/syntaxTests/controlFlow/mappingReturn/unnamed_err.sol @@ -0,0 +1,5 @@ +contract C { + function f() internal pure returns (mapping(uint=>uint) storage) {} +} +// ---- +// TypeError: (53-80): This variable is of storage pointer type and might be returned without assignment and could be used uninitialized. Assign the variable (potentially from itself) to fix this error. diff --git a/test/libsolidity/syntaxTests/controlFlow/mappingReturn/unnamed_fine.sol b/test/libsolidity/syntaxTests/controlFlow/mappingReturn/unnamed_fine.sol new file mode 100644 index 00000000..9c5e3149 --- /dev/null +++ b/test/libsolidity/syntaxTests/controlFlow/mappingReturn/unnamed_fine.sol @@ -0,0 +1,5 @@ +contract C { + mapping(uint=>uint) m; + function f() internal view returns (mapping(uint=>uint) storage) { return m; } +} +// ---- diff --git a/test/libsolidity/syntaxTests/controlFlow/storageReturn/assembly_err.sol b/test/libsolidity/syntaxTests/controlFlow/storageReturn/assembly_err.sol new file mode 100644 index 00000000..cad9b8e8 --- /dev/null +++ b/test/libsolidity/syntaxTests/controlFlow/storageReturn/assembly_err.sol @@ -0,0 +1,10 @@ +contract C { + struct S { bool f; } + S s; + function f() internal pure returns (S storage) { + assembly { + } + } +} +// ---- +// TypeError: (87-96): This variable is of storage pointer type and might be returned without assignment and could be used uninitialized. Assign the variable (potentially from itself) to fix this error. diff --git a/test/libsolidity/syntaxTests/controlFlow/storageReturn/assembly_fine.sol b/test/libsolidity/syntaxTests/controlFlow/storageReturn/assembly_fine.sol index 65902cc8..0d3db856 100644 --- a/test/libsolidity/syntaxTests/controlFlow/storageReturn/assembly_fine.sol +++ b/test/libsolidity/syntaxTests/controlFlow/storageReturn/assembly_fine.sol @@ -8,7 +8,7 @@ contract C { } function g(bool flag) internal returns (S storage c) { // control flow in assembly will not be analyzed for now, - // so this will not issue a warning + // so this will not issue an error assembly { if flag { sstore(c_slot, sload(s_slot)) @@ -17,7 +17,7 @@ contract C { } function h() internal returns (S storage c) { // any reference from assembly will be sufficient for now, - // so this will not issue a warning + // so this will not issue an error assembly { sstore(s_slot, sload(c_slot)) } diff --git a/test/libsolidity/syntaxTests/controlFlow/storageReturn/assembly_warn.sol b/test/libsolidity/syntaxTests/controlFlow/storageReturn/assembly_warn.sol deleted file mode 100644 index 09c13847..00000000 --- a/test/libsolidity/syntaxTests/controlFlow/storageReturn/assembly_warn.sol +++ /dev/null @@ -1,10 +0,0 @@ -contract C { - struct S { bool f; } - S s; - function f() internal pure returns (S storage) { - assembly { - } - } -} -// ---- -// Warning: (87-88): This variable is of storage pointer type and might be returned without assignment. This can cause storage corruption. Assign the variable (potentially from itself) to remove this warning. diff --git a/test/libsolidity/syntaxTests/controlFlow/storageReturn/default_location.sol b/test/libsolidity/syntaxTests/controlFlow/storageReturn/default_location.sol index 9a42192d..ec83c596 100644 --- a/test/libsolidity/syntaxTests/controlFlow/storageReturn/default_location.sol +++ b/test/libsolidity/syntaxTests/controlFlow/storageReturn/default_location.sol @@ -1,18 +1,18 @@ contract C { struct S { bool f; } S s; - function f() internal view returns (S c) { + function f() internal view returns (S memory c) { c = s; } - function g() internal view returns (S) { + function g() internal view returns (S memory) { return s; } - function h() internal pure returns (S) { + function h() internal pure returns (S memory) { } - function i(bool flag) internal view returns (S c) { + function i(bool flag) internal view returns (S memory c) { if (flag) c = s; } - function j(bool flag) internal view returns (S) { + function j(bool flag) internal view returns (S memory) { if (flag) return s; } } diff --git a/test/libsolidity/syntaxTests/controlFlow/storageReturn/dowhile_err.sol b/test/libsolidity/syntaxTests/controlFlow/storageReturn/dowhile_err.sol new file mode 100644 index 00000000..eb574c96 --- /dev/null +++ b/test/libsolidity/syntaxTests/controlFlow/storageReturn/dowhile_err.sol @@ -0,0 +1,52 @@ +contract C { + struct S { bool f; } + S s; + function f() internal view returns (S storage c) { + do { + break; + c = s; + } while(false); + } + function g() internal view returns (S storage c) { + do { + if (s.f) { + continue; + c = s; + } + else { + } + } while(false); + } + function h() internal view returns (S storage c) { + do { + if (s.f) { + break; + } + else { + c = s; + } + } while(false); + } + function i() internal view returns (S storage c) { + do { + if (s.f) { + continue; + } + else { + c = s; + } + } while(false); + } + function j() internal view returns (S storage c) { + do { + continue; + c = s; + } while(false); + } +} +// ---- +// TypeError: (87-98): This variable is of storage pointer type and might be returned without assignment and could be used uninitialized. Assign the variable (potentially from itself) to fix this error. +// TypeError: (223-234): This variable is of storage pointer type and might be returned without assignment and could be used uninitialized. Assign the variable (potentially from itself) to fix this error. +// TypeError: (440-451): This variable is of storage pointer type and might be returned without assignment and could be used uninitialized. Assign the variable (potentially from itself) to fix this error. +// TypeError: (654-665): This variable is of storage pointer type and might be returned without assignment and could be used uninitialized. Assign the variable (potentially from itself) to fix this error. +// TypeError: (871-882): This variable is of storage pointer type and might be returned without assignment and could be used uninitialized. Assign the variable (potentially from itself) to fix this error. diff --git a/test/libsolidity/syntaxTests/controlFlow/storageReturn/dowhile_fine.sol b/test/libsolidity/syntaxTests/controlFlow/storageReturn/dowhile_fine.sol index 6520672c..55c5edd3 100644 --- a/test/libsolidity/syntaxTests/controlFlow/storageReturn/dowhile_fine.sol +++ b/test/libsolidity/syntaxTests/controlFlow/storageReturn/dowhile_fine.sol @@ -23,13 +23,8 @@ contract C { } function k() internal view returns (S storage c) { do { - if (s.f) { - continue; - break; - } - else { - c = s; - } + c = s; + continue; } while(false); } } diff --git a/test/libsolidity/syntaxTests/controlFlow/storageReturn/dowhile_warn.sol b/test/libsolidity/syntaxTests/controlFlow/storageReturn/dowhile_warn.sol deleted file mode 100644 index f1a92e9c..00000000 --- a/test/libsolidity/syntaxTests/controlFlow/storageReturn/dowhile_warn.sol +++ /dev/null @@ -1,35 +0,0 @@ -contract C { - struct S { bool f; } - S s; - function f() internal view returns (S storage c) { - do { - break; - c = s; - } while(false); - } - function g() internal view returns (S storage c) { - do { - if (s.f) { - continue; - c = s; - } - else { - } - } while(false); - } - function h() internal view returns (S storage c) { - do { - if (s.f) { - break; - continue; - } - else { - c = s; - } - } while(false); - } -} -// ---- -// Warning: (87-98): This variable is of storage pointer type and might be returned without assignment. This can cause storage corruption. Assign the variable (potentially from itself) to remove this warning. -// Warning: (223-234): This variable is of storage pointer type and might be returned without assignment. This can cause storage corruption. Assign the variable (potentially from itself) to remove this warning. -// Warning: (440-451): This variable is of storage pointer type and might be returned without assignment. This can cause storage corruption. Assign the variable (potentially from itself) to remove this warning. diff --git a/test/libsolidity/syntaxTests/controlFlow/storageReturn/emptyReturn_fine.sol b/test/libsolidity/syntaxTests/controlFlow/storageReturn/emptyReturn_fine.sol deleted file mode 100644 index 3a0a30ea..00000000 --- a/test/libsolidity/syntaxTests/controlFlow/storageReturn/emptyReturn_fine.sol +++ /dev/null @@ -1,6 +0,0 @@ -contract C { - struct S { bool f; } - S s; - function f() internal view returns (S storage c, S storage d) { c = s; d = s; return; } -} -// ---- diff --git a/test/libsolidity/syntaxTests/controlFlow/storageReturn/emptyReturn_warn.sol b/test/libsolidity/syntaxTests/controlFlow/storageReturn/emptyReturn_warn.sol deleted file mode 100644 index 0a5b2fbf..00000000 --- a/test/libsolidity/syntaxTests/controlFlow/storageReturn/emptyReturn_warn.sol +++ /dev/null @@ -1,15 +0,0 @@ -contract C { - struct S { bool f; } - S s; - function f() internal pure returns (S storage) { return; } - function g() internal view returns (S storage c, S storage) { c = s; return; } - function h() internal view returns (S storage, S storage d) { d = s; return; } - function i() internal pure returns (S storage, S storage) { return; } - function j() internal view returns (S storage, S storage) { return (s,s); } -} -// ---- -// Warning: (87-88): This variable is of storage pointer type and might be returned without assignment. This can cause storage corruption. Assign the variable (potentially from itself) to remove this warning. -// Warning: (163-164): This variable is of storage pointer type and might be returned without assignment. This can cause storage corruption. Assign the variable (potentially from itself) to remove this warning. -// Warning: (233-234): This variable is of storage pointer type and might be returned without assignment. This can cause storage corruption. Assign the variable (potentially from itself) to remove this warning. -// Warning: (316-317): This variable is of storage pointer type and might be returned without assignment. This can cause storage corruption. Assign the variable (potentially from itself) to remove this warning. -// Warning: (327-328): This variable is of storage pointer type and might be returned without assignment. This can cause storage corruption. Assign the variable (potentially from itself) to remove this warning. diff --git a/test/libsolidity/syntaxTests/controlFlow/storageReturn/for_err.sol b/test/libsolidity/syntaxTests/controlFlow/storageReturn/for_err.sol new file mode 100644 index 00000000..9aa580a4 --- /dev/null +++ b/test/libsolidity/syntaxTests/controlFlow/storageReturn/for_err.sol @@ -0,0 +1,16 @@ +contract C { + struct S { bool f; } + S s; + function f() internal view returns (S storage c) { + for(;; c = s) { + } + } + function g() internal view returns (S storage c) { + for(;;) { + c = s; + } + } +} +// ---- +// TypeError: (87-98): This variable is of storage pointer type and might be returned without assignment and could be used uninitialized. Assign the variable (potentially from itself) to fix this error. +// TypeError: (182-193): This variable is of storage pointer type and might be returned without assignment and could be used uninitialized. Assign the variable (potentially from itself) to fix this error. diff --git a/test/libsolidity/syntaxTests/controlFlow/storageReturn/for_warn.sol b/test/libsolidity/syntaxTests/controlFlow/storageReturn/for_warn.sol deleted file mode 100644 index ba9a2440..00000000 --- a/test/libsolidity/syntaxTests/controlFlow/storageReturn/for_warn.sol +++ /dev/null @@ -1,16 +0,0 @@ -contract C { - struct S { bool f; } - S s; - function f() internal view returns (S storage c) { - for(;; c = s) { - } - } - function g() internal view returns (S storage c) { - for(;;) { - c = s; - } - } -} -// ---- -// Warning: (87-98): This variable is of storage pointer type and might be returned without assignment. This can cause storage corruption. Assign the variable (potentially from itself) to remove this warning. -// Warning: (182-193): This variable is of storage pointer type and might be returned without assignment. This can cause storage corruption. Assign the variable (potentially from itself) to remove this warning. diff --git a/test/libsolidity/syntaxTests/controlFlow/storageReturn/if_err.sol b/test/libsolidity/syntaxTests/controlFlow/storageReturn/if_err.sol new file mode 100644 index 00000000..f3e55318 --- /dev/null +++ b/test/libsolidity/syntaxTests/controlFlow/storageReturn/if_err.sol @@ -0,0 +1,18 @@ +contract C { + struct S { bool f; } + S s; + function f(bool flag) internal view returns (S storage c) { + if (flag) c = s; + } + function g(bool flag) internal returns (S storage c) { + if (flag) c = s; + else + { + if (!flag) c = s; + else s.f = true; + } + } +} +// ---- +// TypeError: (96-107): This variable is of storage pointer type and might be returned without assignment and could be used uninitialized. Assign the variable (potentially from itself) to fix this error. +// TypeError: (186-197): This variable is of storage pointer type and might be returned without assignment and could be used uninitialized. Assign the variable (potentially from itself) to fix this error. diff --git a/test/libsolidity/syntaxTests/controlFlow/storageReturn/if_warn.sol b/test/libsolidity/syntaxTests/controlFlow/storageReturn/if_warn.sol deleted file mode 100644 index c257c252..00000000 --- a/test/libsolidity/syntaxTests/controlFlow/storageReturn/if_warn.sol +++ /dev/null @@ -1,18 +0,0 @@ -contract C { - struct S { bool f; } - S s; - function f(bool flag) internal view returns (S storage c) { - if (flag) c = s; - } - function g(bool flag) internal returns (S storage c) { - if (flag) c = s; - else - { - if (!flag) c = s; - else s.f = true; - } - } -} -// ---- -// Warning: (96-107): This variable is of storage pointer type and might be returned without assignment. This can cause storage corruption. Assign the variable (potentially from itself) to remove this warning. -// Warning: (186-197): This variable is of storage pointer type and might be returned without assignment. This can cause storage corruption. Assign the variable (potentially from itself) to remove this warning. diff --git a/test/libsolidity/syntaxTests/controlFlow/storageReturn/modifier_err.sol b/test/libsolidity/syntaxTests/controlFlow/storageReturn/modifier_err.sol new file mode 100644 index 00000000..42342979 --- /dev/null +++ b/test/libsolidity/syntaxTests/controlFlow/storageReturn/modifier_err.sol @@ -0,0 +1,22 @@ +contract C { + modifier revertIfNoReturn() { + _; + revert(); + } + modifier ifFlag(bool flag) { + if (flag) + _; + } + struct S { uint a; } + S s; + function f(bool flag) ifFlag(flag) internal view returns(S storage) { + return s; + } + + function g(bool flag) ifFlag(flag) revertIfNoReturn() internal view returns(S storage) { + return s; + } +} +// ---- +// TypeError: (249-258): This variable is of storage pointer type and might be returned without assignment and could be used uninitialized. Assign the variable (potentially from itself) to fix this error. +// TypeError: (367-376): This variable is of storage pointer type and might be returned without assignment and could be used uninitialized. Assign the variable (potentially from itself) to fix this error. diff --git a/test/libsolidity/syntaxTests/controlFlow/storageReturn/modifier_warn.sol b/test/libsolidity/syntaxTests/controlFlow/storageReturn/modifier_warn.sol deleted file mode 100644 index 50c6dd99..00000000 --- a/test/libsolidity/syntaxTests/controlFlow/storageReturn/modifier_warn.sol +++ /dev/null @@ -1,22 +0,0 @@ -contract C { - modifier revertIfNoReturn() { - _; - revert(); - } - modifier ifFlag(bool flag) { - if (flag) - _; - } - struct S { uint a; } - S s; - function f(bool flag) ifFlag(flag) internal view returns(S storage) { - return s; - } - - function g(bool flag) ifFlag(flag) revertIfNoReturn() internal view returns(S storage) { - return s; - } -} -// ---- -// Warning: (249-250): This variable is of storage pointer type and might be returned without assignment. This can cause storage corruption. Assign the variable (potentially from itself) to remove this warning. -// Warning: (367-368): This variable is of storage pointer type and might be returned without assignment. This can cause storage corruption. Assign the variable (potentially from itself) to remove this warning. diff --git a/test/libsolidity/syntaxTests/controlFlow/storageReturn/short_circuit_err.sol b/test/libsolidity/syntaxTests/controlFlow/storageReturn/short_circuit_err.sol new file mode 100644 index 00000000..d0ad8245 --- /dev/null +++ b/test/libsolidity/syntaxTests/controlFlow/storageReturn/short_circuit_err.sol @@ -0,0 +1,18 @@ +contract C { + struct S { bool f; } + S s; + function f() internal view returns (S storage c) { + false && (c = s).f; + } + function g() internal view returns (S storage c) { + true || (c = s).f; + } + function h() internal view returns (S storage c) { + // expect error, although this is always fine + true && (false || (c = s).f); + } +} +// ---- +// TypeError: (87-98): This variable is of storage pointer type and might be returned without assignment and could be used uninitialized. Assign the variable (potentially from itself) to fix this error. +// TypeError: (176-187): This variable is of storage pointer type and might be returned without assignment and could be used uninitialized. Assign the variable (potentially from itself) to fix this error. +// TypeError: (264-275): This variable is of storage pointer type and might be returned without assignment and could be used uninitialized. Assign the variable (potentially from itself) to fix this error. diff --git a/test/libsolidity/syntaxTests/controlFlow/storageReturn/short_circuit_warn.sol b/test/libsolidity/syntaxTests/controlFlow/storageReturn/short_circuit_warn.sol deleted file mode 100644 index 9f660f11..00000000 --- a/test/libsolidity/syntaxTests/controlFlow/storageReturn/short_circuit_warn.sol +++ /dev/null @@ -1,18 +0,0 @@ -contract C { - struct S { bool f; } - S s; - function f() internal view returns (S storage c) { - false && (c = s).f; - } - function g() internal view returns (S storage c) { - true || (c = s).f; - } - function h() internal view returns (S storage c) { - // expect warning, although this is always fine - true && (false || (c = s).f); - } -} -// ---- -// Warning: (87-98): This variable is of storage pointer type and might be returned without assignment. This can cause storage corruption. Assign the variable (potentially from itself) to remove this warning. -// Warning: (176-187): This variable is of storage pointer type and might be returned without assignment. This can cause storage corruption. Assign the variable (potentially from itself) to remove this warning. -// Warning: (264-275): This variable is of storage pointer type and might be returned without assignment. This can cause storage corruption. Assign the variable (potentially from itself) to remove this warning. diff --git a/test/libsolidity/syntaxTests/controlFlow/storageReturn/ternary_err.sol b/test/libsolidity/syntaxTests/controlFlow/storageReturn/ternary_err.sol new file mode 100644 index 00000000..6d10287b --- /dev/null +++ b/test/libsolidity/syntaxTests/controlFlow/storageReturn/ternary_err.sol @@ -0,0 +1,13 @@ +contract C { + struct S { bool f; } + S s; + function f(bool flag) internal view returns (S storage c) { + flag ? (c = s).f : false; + } + function g(bool flag) internal view returns (S storage c) { + flag ? false : (c = s).f; + } +} +// ---- +// TypeError: (96-107): This variable is of storage pointer type and might be returned without assignment and could be used uninitialized. Assign the variable (potentially from itself) to fix this error. +// TypeError: (200-211): This variable is of storage pointer type and might be returned without assignment and could be used uninitialized. Assign the variable (potentially from itself) to fix this error. diff --git a/test/libsolidity/syntaxTests/controlFlow/storageReturn/ternary_warn.sol b/test/libsolidity/syntaxTests/controlFlow/storageReturn/ternary_warn.sol deleted file mode 100644 index 57561fbb..00000000 --- a/test/libsolidity/syntaxTests/controlFlow/storageReturn/ternary_warn.sol +++ /dev/null @@ -1,13 +0,0 @@ -contract C { - struct S { bool f; } - S s; - function f(bool flag) internal view returns (S storage c) { - flag ? (c = s).f : false; - } - function g(bool flag) internal view returns (S storage c) { - flag ? false : (c = s).f; - } -} -// ---- -// Warning: (96-107): This variable is of storage pointer type and might be returned without assignment. This can cause storage corruption. Assign the variable (potentially from itself) to remove this warning. -// Warning: (200-211): This variable is of storage pointer type and might be returned without assignment. This can cause storage corruption. Assign the variable (potentially from itself) to remove this warning. diff --git a/test/libsolidity/syntaxTests/controlFlow/storageReturn/throw_fine.sol b/test/libsolidity/syntaxTests/controlFlow/storageReturn/throw_fine.sol deleted file mode 100644 index 4cecc27c..00000000 --- a/test/libsolidity/syntaxTests/controlFlow/storageReturn/throw_fine.sol +++ /dev/null @@ -1,9 +0,0 @@ -contract C { - struct S { bool f; } - S s; - function f() internal pure returns (S storage) { - throw; - } -} -// ---- -// Warning: (108-113): "throw" is deprecated in favour of "revert()", "require()" and "assert()". diff --git a/test/libsolidity/syntaxTests/controlFlow/storageReturn/tuple_fine.sol b/test/libsolidity/syntaxTests/controlFlow/storageReturn/tuple_fine.sol index 0b171560..7567f694 100644 --- a/test/libsolidity/syntaxTests/controlFlow/storageReturn/tuple_fine.sol +++ b/test/libsolidity/syntaxTests/controlFlow/storageReturn/tuple_fine.sol @@ -8,5 +8,8 @@ contract C { uint a; (c, a) = f(); } + function h() internal view returns (S storage, S storage) { + return (s,s); + } } // ---- diff --git a/test/libsolidity/syntaxTests/controlFlow/storageReturn/unimplemented_internal.sol b/test/libsolidity/syntaxTests/controlFlow/storageReturn/unimplemented_internal.sol new file mode 100644 index 00000000..8bce0dd2 --- /dev/null +++ b/test/libsolidity/syntaxTests/controlFlow/storageReturn/unimplemented_internal.sol @@ -0,0 +1,4 @@ +contract C { + function f() internal returns(uint[] storage); + function g() internal returns(uint[] storage s); +} diff --git a/test/libsolidity/syntaxTests/controlFlow/storageReturn/unimplemented_library.sol b/test/libsolidity/syntaxTests/controlFlow/storageReturn/unimplemented_library.sol new file mode 100644 index 00000000..818b6a20 --- /dev/null +++ b/test/libsolidity/syntaxTests/controlFlow/storageReturn/unimplemented_library.sol @@ -0,0 +1,4 @@ +library L { + function f() public returns(uint[] storage); + function g() public returns(uint[] storage s); +} diff --git a/test/libsolidity/syntaxTests/controlFlow/storageReturn/while_err.sol b/test/libsolidity/syntaxTests/controlFlow/storageReturn/while_err.sol new file mode 100644 index 00000000..e7b4fae7 --- /dev/null +++ b/test/libsolidity/syntaxTests/controlFlow/storageReturn/while_err.sol @@ -0,0 +1,11 @@ +contract C { + struct S { bool f; } + S s; + function f() internal view returns (S storage c) { + while(false) { + c = s; + } + } +} +// ---- +// TypeError: (87-98): This variable is of storage pointer type and might be returned without assignment and could be used uninitialized. Assign the variable (potentially from itself) to fix this error. diff --git a/test/libsolidity/syntaxTests/controlFlow/storageReturn/while_warn.sol b/test/libsolidity/syntaxTests/controlFlow/storageReturn/while_warn.sol deleted file mode 100644 index 26db892f..00000000 --- a/test/libsolidity/syntaxTests/controlFlow/storageReturn/while_warn.sol +++ /dev/null @@ -1,11 +0,0 @@ -contract C { - struct S { bool f; } - S s; - function f() internal view returns (S storage c) { - while(false) { - c = s; - } - } -} -// ---- -// Warning: (87-98): This variable is of storage pointer type and might be returned without assignment. This can cause storage corruption. Assign the variable (potentially from itself) to remove this warning. diff --git a/test/libsolidity/syntaxTests/conversion/allowed_conversion_to_bytes_array.sol b/test/libsolidity/syntaxTests/conversion/allowed_conversion_to_bytes_array.sol new file mode 100644 index 00000000..78c40e53 --- /dev/null +++ b/test/libsolidity/syntaxTests/conversion/allowed_conversion_to_bytes_array.sol @@ -0,0 +1,9 @@ +contract C { + bytes a; + bytes b; + function f() public view { + bytes storage c = a; + bytes memory d = b; + d = bytes(c); + } +} diff --git a/test/libsolidity/syntaxTests/conversion/allowed_conversion_to_string.sol b/test/libsolidity/syntaxTests/conversion/allowed_conversion_to_string.sol new file mode 100644 index 00000000..f7e96f35 --- /dev/null +++ b/test/libsolidity/syntaxTests/conversion/allowed_conversion_to_string.sol @@ -0,0 +1,9 @@ +contract C { + string a; + string b; + function f() public view { + string storage c = a; + string memory d = b; + d = string(c); + } +} diff --git a/test/libsolidity/syntaxTests/conversion/conversion_to_bytes.sol b/test/libsolidity/syntaxTests/conversion/conversion_to_bytes.sol new file mode 100644 index 00000000..3a6deff1 --- /dev/null +++ b/test/libsolidity/syntaxTests/conversion/conversion_to_bytes.sol @@ -0,0 +1,5 @@ +contract test { + function f() public pure returns (bytes memory) { + return bytes("abc"); + } +} diff --git a/test/libsolidity/syntaxTests/conversion/explicit_conversion_from_storage_array_ref.sol b/test/libsolidity/syntaxTests/conversion/explicit_conversion_from_storage_array_ref.sol new file mode 100644 index 00000000..458adda6 --- /dev/null +++ b/test/libsolidity/syntaxTests/conversion/explicit_conversion_from_storage_array_ref.sol @@ -0,0 +1,10 @@ +contract C { + int[10] x; + function f() public view { + int[](x); + int(x); + } +} +// ---- +// TypeError: (55-63): Explicit type conversion not allowed from "int256[10] storage ref" to "int256[] storage pointer". +// TypeError: (67-73): Explicit type conversion not allowed from "int256[10] storage ref" to "int256". diff --git a/test/libsolidity/syntaxTests/conversion/function_type_nonpayable_payable.sol b/test/libsolidity/syntaxTests/conversion/function_type_nonpayable_payable.sol new file mode 100644 index 00000000..75f7a953 --- /dev/null +++ b/test/libsolidity/syntaxTests/conversion/function_type_nonpayable_payable.sol @@ -0,0 +1,10 @@ +contract C { + function h() external { + } + function f() view external returns (bytes4) { + function () payable external g = this.h; + return g.selector; + } +} +// ---- +// TypeError: (105-144): Type function () external is not implicitly convertible to expected type function () payable external. diff --git a/test/libsolidity/syntaxTests/conversion/function_type_nonpayable_pure.sol b/test/libsolidity/syntaxTests/conversion/function_type_nonpayable_pure.sol new file mode 100644 index 00000000..8d1b08aa --- /dev/null +++ b/test/libsolidity/syntaxTests/conversion/function_type_nonpayable_pure.sol @@ -0,0 +1,10 @@ +contract C { + function h() external { + } + function f() view external returns (bytes4) { + function () pure external g = this.h; + return g.selector; + } +} +// ---- +// TypeError: (105-141): Type function () external is not implicitly convertible to expected type function () pure external. diff --git a/test/libsolidity/syntaxTests/conversion/function_type_nonpayable_view.sol b/test/libsolidity/syntaxTests/conversion/function_type_nonpayable_view.sol new file mode 100644 index 00000000..535d6c77 --- /dev/null +++ b/test/libsolidity/syntaxTests/conversion/function_type_nonpayable_view.sol @@ -0,0 +1,10 @@ +contract C { + function h() external { + } + function f() view external returns (bytes4) { + function () view external g = this.h; + return g.selector; + } +} +// ---- +// TypeError: (105-141): Type function () external is not implicitly convertible to expected type function () view external. diff --git a/test/libsolidity/syntaxTests/conversion/function_type_payable_nonpayable.sol b/test/libsolidity/syntaxTests/conversion/function_type_payable_nonpayable.sol new file mode 100644 index 00000000..299d7e30 --- /dev/null +++ b/test/libsolidity/syntaxTests/conversion/function_type_payable_nonpayable.sol @@ -0,0 +1,8 @@ +contract C { + function h() payable external { + } + function f() view external returns (bytes4) { + function () external g = this.h; + return g.selector; + } +} diff --git a/test/libsolidity/syntaxTests/conversion/function_type_payable_pure.sol b/test/libsolidity/syntaxTests/conversion/function_type_payable_pure.sol new file mode 100644 index 00000000..78bada51 --- /dev/null +++ b/test/libsolidity/syntaxTests/conversion/function_type_payable_pure.sol @@ -0,0 +1,10 @@ +contract C { + function h() payable external { + } + function f() view external returns (bytes4) { + function () pure external g = this.h; + return g.selector; + } +} +// ---- +// TypeError: (113-149): Type function () payable external is not implicitly convertible to expected type function () pure external. diff --git a/test/libsolidity/syntaxTests/conversion/function_type_payable_view.sol b/test/libsolidity/syntaxTests/conversion/function_type_payable_view.sol new file mode 100644 index 00000000..f12cb301 --- /dev/null +++ b/test/libsolidity/syntaxTests/conversion/function_type_payable_view.sol @@ -0,0 +1,10 @@ +contract C { + function h() payable external { + } + function f() view external returns (bytes4) { + function () view external g = this.h; + return g.selector; + } +} +// ---- +// TypeError: (113-149): Type function () payable external is not implicitly convertible to expected type function () view external. diff --git a/test/libsolidity/syntaxTests/conversion/function_type_pure_nonpayable.sol b/test/libsolidity/syntaxTests/conversion/function_type_pure_nonpayable.sol new file mode 100644 index 00000000..7742e0c1 --- /dev/null +++ b/test/libsolidity/syntaxTests/conversion/function_type_pure_nonpayable.sol @@ -0,0 +1,8 @@ +contract C { + function h() pure external { + } + function f() view external returns (bytes4) { + function () external g = this.h; + return g.selector; + } +} diff --git a/test/libsolidity/syntaxTests/conversion/function_type_pure_payable.sol b/test/libsolidity/syntaxTests/conversion/function_type_pure_payable.sol new file mode 100644 index 00000000..cd4e9b4e --- /dev/null +++ b/test/libsolidity/syntaxTests/conversion/function_type_pure_payable.sol @@ -0,0 +1,10 @@ +contract C { + function h() pure external { + } + function f() view external returns (bytes4) { + function () payable external g = this.h; + return g.selector; + } +} +// ---- +// TypeError: (110-149): Type function () pure external is not implicitly convertible to expected type function () payable external. diff --git a/test/libsolidity/syntaxTests/conversion/function_type_pure_view.sol b/test/libsolidity/syntaxTests/conversion/function_type_pure_view.sol new file mode 100644 index 00000000..578ecdbd --- /dev/null +++ b/test/libsolidity/syntaxTests/conversion/function_type_pure_view.sol @@ -0,0 +1,8 @@ +contract C { + function h() pure external { + } + function f() view external returns (bytes4) { + function () view external g = this.h; + return g.selector; + } +} diff --git a/test/libsolidity/syntaxTests/conversion/function_type_same.sol b/test/libsolidity/syntaxTests/conversion/function_type_same.sol new file mode 100644 index 00000000..c5ebe1ca --- /dev/null +++ b/test/libsolidity/syntaxTests/conversion/function_type_same.sol @@ -0,0 +1,14 @@ +contract C { + int dummy; + function h_nonpayable() external { dummy = 1; } + function h_payable() payable external {} + function h_view() view external { dummy; } + function h_pure() pure external {} + function f() view external { + function () external g_nonpayable = this.h_nonpayable; g_nonpayable; + function () payable external g_payable = this.h_payable; g_payable; + function () view external g_view = this.h_view; g_view; + function () pure external g_pure = this.h_pure; g_pure; + } +} +// ---- diff --git a/test/libsolidity/syntaxTests/conversion/function_type_view_nonpayable.sol b/test/libsolidity/syntaxTests/conversion/function_type_view_nonpayable.sol new file mode 100644 index 00000000..f52aece0 --- /dev/null +++ b/test/libsolidity/syntaxTests/conversion/function_type_view_nonpayable.sol @@ -0,0 +1,10 @@ +contract C { + int dummy; + function h() view external { + dummy; + } + function f() view external returns (bytes4) { + function () external g = this.h; + return g.selector; + } +} diff --git a/test/libsolidity/syntaxTests/conversion/function_type_view_payable.sol b/test/libsolidity/syntaxTests/conversion/function_type_view_payable.sol new file mode 100644 index 00000000..3bf4bac2 --- /dev/null +++ b/test/libsolidity/syntaxTests/conversion/function_type_view_payable.sol @@ -0,0 +1,10 @@ +contract C { + function h() view external { + } + function f() view external returns (bytes4) { + function () payable external g = this.h; + return g.selector; + } +} +// ---- +// TypeError: (110-149): Type function () view external is not implicitly convertible to expected type function () payable external. diff --git a/test/libsolidity/syntaxTests/conversion/function_type_view_pure.sol b/test/libsolidity/syntaxTests/conversion/function_type_view_pure.sol new file mode 100644 index 00000000..c567a2c8 --- /dev/null +++ b/test/libsolidity/syntaxTests/conversion/function_type_view_pure.sol @@ -0,0 +1,10 @@ +contract C { + function h() view external { + } + function f() view external returns (bytes4) { + function () pure external g = this.h; + return g.selector; + } +} +// ---- +// TypeError: (110-146): Type function () view external is not implicitly convertible to expected type function () pure external. diff --git a/test/libsolidity/syntaxTests/conversion/implicit_conversion_from_storage_array_ref.sol b/test/libsolidity/syntaxTests/conversion/implicit_conversion_from_storage_array_ref.sol new file mode 100644 index 00000000..31e298d0 --- /dev/null +++ b/test/libsolidity/syntaxTests/conversion/implicit_conversion_from_storage_array_ref.sol @@ -0,0 +1,7 @@ +contract C { + int[10] x; + int[] y; + function f() public { + y = x; + } +} diff --git a/test/libsolidity/syntaxTests/conversion/not_allowed_conversion_to_int_array_pointer1.sol b/test/libsolidity/syntaxTests/conversion/not_allowed_conversion_to_int_array_pointer1.sol new file mode 100644 index 00000000..3aa59612 --- /dev/null +++ b/test/libsolidity/syntaxTests/conversion/not_allowed_conversion_to_int_array_pointer1.sol @@ -0,0 +1,10 @@ +contract C { + uint[] a; + uint[] b; + function f() public view { + uint[] storage c = a; + uint[] storage d = b; + d = uint[](c); + } +} +// ---- diff --git a/test/libsolidity/syntaxTests/conversion/not_allowed_conversion_to_int_array_pointer2.sol b/test/libsolidity/syntaxTests/conversion/not_allowed_conversion_to_int_array_pointer2.sol new file mode 100644 index 00000000..060c9707 --- /dev/null +++ b/test/libsolidity/syntaxTests/conversion/not_allowed_conversion_to_int_array_pointer2.sol @@ -0,0 +1,10 @@ +contract C { + uint[] a; + uint[] b; + function f() public view { + uint[] storage c = a; + uint[] memory d = b; + d = uint[](c); + } +} +// ---- diff --git a/test/libsolidity/syntaxTests/dataLocations/data_location_in_function_type.sol b/test/libsolidity/syntaxTests/dataLocations/data_location_in_function_type.sol new file mode 100644 index 00000000..b23fbb89 --- /dev/null +++ b/test/libsolidity/syntaxTests/dataLocations/data_location_in_function_type.sol @@ -0,0 +1,4 @@ +library L { + struct Nested { uint y; } + function c(function(Nested memory) external returns (uint)[] storage) external pure {} +} diff --git a/test/libsolidity/syntaxTests/dataLocations/data_location_in_function_type_fail.sol b/test/libsolidity/syntaxTests/dataLocations/data_location_in_function_type_fail.sol new file mode 100644 index 00000000..b80849ce --- /dev/null +++ b/test/libsolidity/syntaxTests/dataLocations/data_location_in_function_type_fail.sol @@ -0,0 +1,9 @@ +library L { + struct Nested { uint y; } + function b(function(Nested calldata) external returns (uint)[] storage) external pure {} + function d(function(Nested storage) external returns (uint)[] storage) external pure {} +} + +// ---- +// TypeError: (66-81): Data location must be "memory" for parameter in function, but "calldata" was given. +// TypeError: (159-173): Data location must be "memory" for parameter in function, but "storage" was given. diff --git a/test/libsolidity/syntaxTests/dataLocations/externalFunction/external_function_return_parameters_no_data_location.sol b/test/libsolidity/syntaxTests/dataLocations/externalFunction/external_function_return_parameters_no_data_location.sol new file mode 100644 index 00000000..cbcf2a6e --- /dev/null +++ b/test/libsolidity/syntaxTests/dataLocations/externalFunction/external_function_return_parameters_no_data_location.sol @@ -0,0 +1,5 @@ +contract C { + function i() external pure returns(uint[]) {} +} +// ---- +// TypeError: (52-58): Data location must be "memory" for return parameter in function, but none was given. diff --git a/test/libsolidity/syntaxTests/dataLocations/externalFunction/function_argument_location_specifier_test_external_calldata.sol b/test/libsolidity/syntaxTests/dataLocations/externalFunction/function_argument_location_specifier_test_external_calldata.sol new file mode 100644 index 00000000..781c645a --- /dev/null +++ b/test/libsolidity/syntaxTests/dataLocations/externalFunction/function_argument_location_specifier_test_external_calldata.sol @@ -0,0 +1,4 @@ +contract test { + function f(bytes calldata) external; +} +// ---- diff --git a/test/libsolidity/syntaxTests/dataLocations/externalFunction/function_argument_location_specifier_test_external_memory.sol b/test/libsolidity/syntaxTests/dataLocations/externalFunction/function_argument_location_specifier_test_external_memory.sol new file mode 100644 index 00000000..d30bde3f --- /dev/null +++ b/test/libsolidity/syntaxTests/dataLocations/externalFunction/function_argument_location_specifier_test_external_memory.sol @@ -0,0 +1,5 @@ +contract test { + function f(bytes memory) external; +} +// ---- +// TypeError: (31-43): Data location must be "calldata" for parameter in external function, but "memory" was given. diff --git a/test/libsolidity/syntaxTests/dataLocations/externalFunction/function_argument_location_specifier_test_external_storage.sol b/test/libsolidity/syntaxTests/dataLocations/externalFunction/function_argument_location_specifier_test_external_storage.sol new file mode 100644 index 00000000..7dc5ba6d --- /dev/null +++ b/test/libsolidity/syntaxTests/dataLocations/externalFunction/function_argument_location_specifier_test_external_storage.sol @@ -0,0 +1,5 @@ +contract test { + function f(bytes storage) external; +} +// ---- +// TypeError: (31-44): Data location must be "calldata" for parameter in external function, but "storage" was given. diff --git a/test/libsolidity/syntaxTests/dataLocations/function_argument_location_specifier_test_non_reference_type.sol b/test/libsolidity/syntaxTests/dataLocations/function_argument_location_specifier_test_non_reference_type.sol new file mode 100644 index 00000000..bc14aa1f --- /dev/null +++ b/test/libsolidity/syntaxTests/dataLocations/function_argument_location_specifier_test_non_reference_type.sol @@ -0,0 +1,5 @@ +contract test { + function f(bytes4 memory) public; +} +// ---- +// TypeError: (31-44): Data location can only be specified for array, struct or mapping types, but "memory" was given. diff --git a/test/libsolidity/syntaxTests/dataLocations/function_parameters_with_data_location_fine.sol b/test/libsolidity/syntaxTests/dataLocations/function_parameters_with_data_location_fine.sol new file mode 100644 index 00000000..2bc7b393 --- /dev/null +++ b/test/libsolidity/syntaxTests/dataLocations/function_parameters_with_data_location_fine.sol @@ -0,0 +1,8 @@ +contract C { + function f(uint[] memory, uint[] storage) private pure {} + function g(uint[] memory, uint[] storage) internal pure {} + function h(uint[] memory) public pure {} + function i(uint[] calldata) external pure {} + // No data location for events. + event e(uint[]); +} diff --git a/test/libsolidity/syntaxTests/dataLocations/function_return_parameters_with_data_location_fine.sol b/test/libsolidity/syntaxTests/dataLocations/function_return_parameters_with_data_location_fine.sol new file mode 100644 index 00000000..ea019198 --- /dev/null +++ b/test/libsolidity/syntaxTests/dataLocations/function_return_parameters_with_data_location_fine.sol @@ -0,0 +1,6 @@ +contract C { + function f() private pure returns(uint[] memory, uint[] storage b) { b = b; } + function g() internal pure returns(uint[] memory, uint[] storage b) { b = b; } + function h() public pure returns(uint[] memory) {} + function i() external pure returns(uint[] memory) {} +} diff --git a/test/libsolidity/syntaxTests/dataLocations/function_type_array_as_reference_type.sol b/test/libsolidity/syntaxTests/dataLocations/function_type_array_as_reference_type.sol new file mode 100644 index 00000000..b3856f58 --- /dev/null +++ b/test/libsolidity/syntaxTests/dataLocations/function_type_array_as_reference_type.sol @@ -0,0 +1,8 @@ +contract C { + struct Nested { uint y; } + // ensure that we consider array of function pointers as reference type + function b(function(Nested memory) external returns (uint)[] storage) internal pure {} + function c(function(Nested memory) external returns (uint)[] memory) public pure {} + function d(function(Nested memory) external returns (uint)[] calldata) external pure {} +} +// ---- diff --git a/test/libsolidity/syntaxTests/dataLocations/internalFunction/function_argument_location_specifier_test_internal_calldata.sol b/test/libsolidity/syntaxTests/dataLocations/internalFunction/function_argument_location_specifier_test_internal_calldata.sol new file mode 100644 index 00000000..da3abff4 --- /dev/null +++ b/test/libsolidity/syntaxTests/dataLocations/internalFunction/function_argument_location_specifier_test_internal_calldata.sol @@ -0,0 +1,5 @@ +contract test { + function f(bytes calldata) internal; +} +// ---- +// TypeError: (31-45): Data location must be "storage" or "memory" for parameter in function, but "calldata" was given. diff --git a/test/libsolidity/syntaxTests/dataLocations/internalFunction/function_argument_location_specifier_test_internal_memory.sol b/test/libsolidity/syntaxTests/dataLocations/internalFunction/function_argument_location_specifier_test_internal_memory.sol new file mode 100644 index 00000000..1e5971c4 --- /dev/null +++ b/test/libsolidity/syntaxTests/dataLocations/internalFunction/function_argument_location_specifier_test_internal_memory.sol @@ -0,0 +1,4 @@ +contract test { + function f(bytes memory) internal; +} +// ---- diff --git a/test/libsolidity/syntaxTests/dataLocations/internalFunction/function_argument_location_specifier_test_internal_storage.sol b/test/libsolidity/syntaxTests/dataLocations/internalFunction/function_argument_location_specifier_test_internal_storage.sol new file mode 100644 index 00000000..56f0fe99 --- /dev/null +++ b/test/libsolidity/syntaxTests/dataLocations/internalFunction/function_argument_location_specifier_test_internal_storage.sol @@ -0,0 +1,4 @@ +contract test { + function f(bytes storage) internal; +} +// ---- diff --git a/test/libsolidity/syntaxTests/dataLocations/internalFunction/internal_function_parameters_no_data_location.sol b/test/libsolidity/syntaxTests/dataLocations/internalFunction/internal_function_parameters_no_data_location.sol new file mode 100644 index 00000000..f1c4a550 --- /dev/null +++ b/test/libsolidity/syntaxTests/dataLocations/internalFunction/internal_function_parameters_no_data_location.sol @@ -0,0 +1,5 @@ +contract C { + function g(uint[]) internal pure {} +} +// ---- +// TypeError: (28-34): Data location must be "storage" or "memory" for parameter in function, but none was given. diff --git a/test/libsolidity/syntaxTests/dataLocations/internalFunction/internal_function_return_parameters_no_data_location.sol b/test/libsolidity/syntaxTests/dataLocations/internalFunction/internal_function_return_parameters_no_data_location.sol new file mode 100644 index 00000000..a32995e7 --- /dev/null +++ b/test/libsolidity/syntaxTests/dataLocations/internalFunction/internal_function_return_parameters_no_data_location.sol @@ -0,0 +1,5 @@ +contract C { + function g() internal pure returns(uint[]) {} +} +// ---- +// TypeError: (52-58): Data location must be "storage" or "memory" for return parameter in function, but none was given. diff --git a/test/libsolidity/syntaxTests/dataLocations/libraries/library_external_function_params_no_data_location.sol b/test/libsolidity/syntaxTests/dataLocations/libraries/library_external_function_params_no_data_location.sol new file mode 100644 index 00000000..c20088b7 --- /dev/null +++ b/test/libsolidity/syntaxTests/dataLocations/libraries/library_external_function_params_no_data_location.sol @@ -0,0 +1,12 @@ +library L { + struct S { uint x; } + function g(uint[2]) external pure {} + function h(uint[]) external pure {} + function i(S) external pure {} + function j(mapping(uint => uint)) external pure {} +} +// ---- +// TypeError: (52-59): Data location must be "storage" or "calldata" for parameter in external function, but none was given. +// TypeError: (93-99): Data location must be "storage" or "calldata" for parameter in external function, but none was given. +// TypeError: (133-134): Data location must be "storage" or "calldata" for parameter in external function, but none was given. +// TypeError: (168-189): Data location must be "storage" or "calldata" for parameter in external function, but none was given. diff --git a/test/libsolidity/syntaxTests/dataLocations/libraries/library_external_function_return_no_data_location.sol b/test/libsolidity/syntaxTests/dataLocations/libraries/library_external_function_return_no_data_location.sol new file mode 100644 index 00000000..fa3a7821 --- /dev/null +++ b/test/libsolidity/syntaxTests/dataLocations/libraries/library_external_function_return_no_data_location.sol @@ -0,0 +1,12 @@ +library L { + struct S { uint x; } + function g() external pure returns (uint[2]) {} + function h() external pure returns (uint[]) {} + function i() external pure returns (S) {} + function j() external pure returns (mapping(uint => uint)) {} +} +// ---- +// TypeError: (77-84): Data location must be "storage" or "memory" for return parameter in function, but none was given. +// TypeError: (129-135): Data location must be "storage" or "memory" for return parameter in function, but none was given. +// TypeError: (180-181): Data location must be "storage" or "memory" for return parameter in function, but none was given. +// TypeError: (226-247): Data location must be "storage" or "memory" for return parameter in function, but none was given. diff --git a/test/libsolidity/syntaxTests/dataLocations/libraries/library_function_with_data_location_fine.sol b/test/libsolidity/syntaxTests/dataLocations/libraries/library_function_with_data_location_fine.sol new file mode 100644 index 00000000..7a276f95 --- /dev/null +++ b/test/libsolidity/syntaxTests/dataLocations/libraries/library_function_with_data_location_fine.sol @@ -0,0 +1,10 @@ +library L { + struct S { uint x; } + function f(uint[] memory, uint[] storage, S storage) private pure + returns (mapping(uint => uint) storage a, S memory b, uint[] storage c) { return (a, b, c); } + function g(uint[] memory, uint[] storage) internal pure + returns (mapping(uint => uint) storage a, S memory b, uint[] storage c) { return (a, b, c); } + function h(uint[] memory, uint[] storage) public pure returns (S storage x) { return x; } + function i(uint[] calldata, uint[] storage) external pure returns (S storage x) {return x; } +} +// ---- diff --git a/test/libsolidity/syntaxTests/dataLocations/libraries/library_internal_function_no_data_location.sol b/test/libsolidity/syntaxTests/dataLocations/libraries/library_internal_function_no_data_location.sol new file mode 100644 index 00000000..68c177a8 --- /dev/null +++ b/test/libsolidity/syntaxTests/dataLocations/libraries/library_internal_function_no_data_location.sol @@ -0,0 +1,20 @@ +library L { + struct S { uint x; } + function g() internal pure returns (uint[2]) {} + function h() internal pure returns (uint[]) {} + function i() internal pure returns (S) {} + function j() internal pure returns (mapping(uint => uint)) {} + function gp(uint[2]) internal pure {} + function hp(uint[]) internal pure {} + function ip(S) internal pure {} + function jp(mapping(uint => uint)) internal pure {} +} +// ---- +// TypeError: (77-84): Data location must be "storage" or "memory" for return parameter in function, but none was given. +// TypeError: (129-135): Data location must be "storage" or "memory" for return parameter in function, but none was given. +// TypeError: (180-181): Data location must be "storage" or "memory" for return parameter in function, but none was given. +// TypeError: (226-247): Data location must be "storage" or "memory" for return parameter in function, but none was given. +// TypeError: (268-275): Data location must be "storage" or "memory" for parameter in function, but none was given. +// TypeError: (310-316): Data location must be "storage" or "memory" for parameter in function, but none was given. +// TypeError: (351-352): Data location must be "storage" or "memory" for parameter in function, but none was given. +// TypeError: (387-408): Data location must be "storage" or "memory" for parameter in function, but none was given. diff --git a/test/libsolidity/syntaxTests/dataLocations/libraries/library_private_function_no_data_location.sol b/test/libsolidity/syntaxTests/dataLocations/libraries/library_private_function_no_data_location.sol new file mode 100644 index 00000000..35256eae --- /dev/null +++ b/test/libsolidity/syntaxTests/dataLocations/libraries/library_private_function_no_data_location.sol @@ -0,0 +1,20 @@ +library L { + struct S { uint x; } + function g() private pure returns (uint[2]) {} + function h() private pure returns (uint[]) {} + function i() private pure returns (S) {} + function j() private pure returns (mapping(uint => uint)) {} + function gp(uint[2]) private pure {} + function hp(uint[]) private pure {} + function ip(S) private pure {} + function jp(mapping(uint => uint)) private pure {} +} +// ---- +// TypeError: (76-83): Data location must be "storage" or "memory" for return parameter in function, but none was given. +// TypeError: (127-133): Data location must be "storage" or "memory" for return parameter in function, but none was given. +// TypeError: (177-178): Data location must be "storage" or "memory" for return parameter in function, but none was given. +// TypeError: (222-243): Data location must be "storage" or "memory" for return parameter in function, but none was given. +// TypeError: (264-271): Data location must be "storage" or "memory" for parameter in function, but none was given. +// TypeError: (305-311): Data location must be "storage" or "memory" for parameter in function, but none was given. +// TypeError: (345-346): Data location must be "storage" or "memory" for parameter in function, but none was given. +// TypeError: (380-401): Data location must be "storage" or "memory" for parameter in function, but none was given. diff --git a/test/libsolidity/syntaxTests/dataLocations/libraries/library_public_function_no_data_location.sol b/test/libsolidity/syntaxTests/dataLocations/libraries/library_public_function_no_data_location.sol new file mode 100644 index 00000000..f8f8dcb2 --- /dev/null +++ b/test/libsolidity/syntaxTests/dataLocations/libraries/library_public_function_no_data_location.sol @@ -0,0 +1,19 @@ +library L { + struct S { uint x; } + function g() private pure returns (uint[2]) {} + function h() private pure returns (uint[]) {} + function i() private pure returns (S) {} + function j() private pure returns (mapping(uint => uint)) {} + function gp(uint[2]) private pure {} + function hp(uint[]) private pure {} + function ip(S) private pure {} + function jp(mapping(uint => uint)) private pure {}} +// ---- +// TypeError: (76-83): Data location must be "storage" or "memory" for return parameter in function, but none was given. +// TypeError: (127-133): Data location must be "storage" or "memory" for return parameter in function, but none was given. +// TypeError: (177-178): Data location must be "storage" or "memory" for return parameter in function, but none was given. +// TypeError: (222-243): Data location must be "storage" or "memory" for return parameter in function, but none was given. +// TypeError: (264-271): Data location must be "storage" or "memory" for parameter in function, but none was given. +// TypeError: (305-311): Data location must be "storage" or "memory" for parameter in function, but none was given. +// TypeError: (345-346): Data location must be "storage" or "memory" for parameter in function, but none was given. +// TypeError: (380-401): Data location must be "storage" or "memory" for parameter in function, but none was given. diff --git a/test/libsolidity/syntaxTests/dataLocations/libraryExternalFunction/function_argument_location_specifier_test_external_calldata.sol b/test/libsolidity/syntaxTests/dataLocations/libraryExternalFunction/function_argument_location_specifier_test_external_calldata.sol new file mode 100644 index 00000000..d3ac2acc --- /dev/null +++ b/test/libsolidity/syntaxTests/dataLocations/libraryExternalFunction/function_argument_location_specifier_test_external_calldata.sol @@ -0,0 +1,3 @@ +library test { + function f(bytes calldata) external; +} diff --git a/test/libsolidity/syntaxTests/dataLocations/libraryExternalFunction/function_argument_location_specifier_test_external_memory.sol b/test/libsolidity/syntaxTests/dataLocations/libraryExternalFunction/function_argument_location_specifier_test_external_memory.sol new file mode 100644 index 00000000..2de0082a --- /dev/null +++ b/test/libsolidity/syntaxTests/dataLocations/libraryExternalFunction/function_argument_location_specifier_test_external_memory.sol @@ -0,0 +1,5 @@ +library test { + function f(bytes memory) external; +} +// ---- +// TypeError: (30-42): Data location must be "storage" or "calldata" for parameter in external function, but "memory" was given. diff --git a/test/libsolidity/syntaxTests/dataLocations/libraryExternalFunction/function_argument_location_specifier_test_external_storage.sol b/test/libsolidity/syntaxTests/dataLocations/libraryExternalFunction/function_argument_location_specifier_test_external_storage.sol new file mode 100644 index 00000000..2ee68ef9 --- /dev/null +++ b/test/libsolidity/syntaxTests/dataLocations/libraryExternalFunction/function_argument_location_specifier_test_external_storage.sol @@ -0,0 +1,3 @@ +library test { + function f(bytes storage) external; +} diff --git a/test/libsolidity/syntaxTests/dataLocations/libraryInternalFunction/function_argument_location_specifier_test_internal_calldata.sol b/test/libsolidity/syntaxTests/dataLocations/libraryInternalFunction/function_argument_location_specifier_test_internal_calldata.sol new file mode 100644 index 00000000..c4b81f98 --- /dev/null +++ b/test/libsolidity/syntaxTests/dataLocations/libraryInternalFunction/function_argument_location_specifier_test_internal_calldata.sol @@ -0,0 +1,5 @@ +library test { + function f(bytes calldata) internal pure {} +} +// ---- +// TypeError: (30-44): Data location must be "storage" or "memory" for parameter in function, but "calldata" was given. diff --git a/test/libsolidity/syntaxTests/dataLocations/libraryInternalFunction/function_argument_location_specifier_test_internal_memory.sol b/test/libsolidity/syntaxTests/dataLocations/libraryInternalFunction/function_argument_location_specifier_test_internal_memory.sol new file mode 100644 index 00000000..78a30f5b --- /dev/null +++ b/test/libsolidity/syntaxTests/dataLocations/libraryInternalFunction/function_argument_location_specifier_test_internal_memory.sol @@ -0,0 +1,3 @@ +library test { + function f(bytes memory) internal pure {} +} diff --git a/test/libsolidity/syntaxTests/dataLocations/libraryInternalFunction/function_argument_location_specifier_test_internal_storage.sol b/test/libsolidity/syntaxTests/dataLocations/libraryInternalFunction/function_argument_location_specifier_test_internal_storage.sol new file mode 100644 index 00000000..b51f148b --- /dev/null +++ b/test/libsolidity/syntaxTests/dataLocations/libraryInternalFunction/function_argument_location_specifier_test_internal_storage.sol @@ -0,0 +1,3 @@ +library test { + function f(bytes storage) internal pure {} +} diff --git a/test/libsolidity/syntaxTests/dataLocations/memory_storage_data_location.sol b/test/libsolidity/syntaxTests/dataLocations/memory_storage_data_location.sol new file mode 100644 index 00000000..a441b540 --- /dev/null +++ b/test/libsolidity/syntaxTests/dataLocations/memory_storage_data_location.sol @@ -0,0 +1,12 @@ +contract C { + int[] x; + function f() public { + int[] storage a = x; + int[] memory b; + a = b; + a = int[](b); + } +} +// ---- +// TypeError: (93-94): Type int256[] memory is not implicitly convertible to expected type int256[] storage pointer. +// TypeError: (102-110): Type int256[] memory is not implicitly convertible to expected type int256[] storage pointer. diff --git a/test/libsolidity/syntaxTests/dataLocations/privateFunction/private_function_parameters_no_data_location.sol b/test/libsolidity/syntaxTests/dataLocations/privateFunction/private_function_parameters_no_data_location.sol new file mode 100644 index 00000000..fdd5cbaf --- /dev/null +++ b/test/libsolidity/syntaxTests/dataLocations/privateFunction/private_function_parameters_no_data_location.sol @@ -0,0 +1,5 @@ +contract C { + function f(uint[]) private pure {} +} +// ---- +// TypeError: (28-34): Data location must be "storage" or "memory" for parameter in function, but none was given. diff --git a/test/libsolidity/syntaxTests/dataLocations/privateFunction/private_function_return_parameters_no_data_location.sol b/test/libsolidity/syntaxTests/dataLocations/privateFunction/private_function_return_parameters_no_data_location.sol new file mode 100644 index 00000000..65ec1bce --- /dev/null +++ b/test/libsolidity/syntaxTests/dataLocations/privateFunction/private_function_return_parameters_no_data_location.sol @@ -0,0 +1,5 @@ +contract C { + function f() private pure returns(uint[]) {} +} +// ---- +// TypeError: (51-57): Data location must be "storage" or "memory" for return parameter in function, but none was given. diff --git a/test/libsolidity/syntaxTests/dataLocations/publicFunction/function_argument_location_specifier_test_public_calldata.sol b/test/libsolidity/syntaxTests/dataLocations/publicFunction/function_argument_location_specifier_test_public_calldata.sol new file mode 100644 index 00000000..3aba870f --- /dev/null +++ b/test/libsolidity/syntaxTests/dataLocations/publicFunction/function_argument_location_specifier_test_public_calldata.sol @@ -0,0 +1,5 @@ +contract test { + function f(bytes calldata) public; +} +// ---- +// TypeError: (31-45): Data location must be "memory" for parameter in function, but "calldata" was given. diff --git a/test/libsolidity/syntaxTests/dataLocations/publicFunction/function_argument_location_specifier_test_public_memory.sol b/test/libsolidity/syntaxTests/dataLocations/publicFunction/function_argument_location_specifier_test_public_memory.sol new file mode 100644 index 00000000..4eebf016 --- /dev/null +++ b/test/libsolidity/syntaxTests/dataLocations/publicFunction/function_argument_location_specifier_test_public_memory.sol @@ -0,0 +1,4 @@ +contract test { + function f(bytes memory) public; +} +// ---- diff --git a/test/libsolidity/syntaxTests/dataLocations/publicFunction/function_argument_location_specifier_test_public_storage.sol b/test/libsolidity/syntaxTests/dataLocations/publicFunction/function_argument_location_specifier_test_public_storage.sol new file mode 100644 index 00000000..1c033a69 --- /dev/null +++ b/test/libsolidity/syntaxTests/dataLocations/publicFunction/function_argument_location_specifier_test_public_storage.sol @@ -0,0 +1,5 @@ +contract test { + function f(bytes storage) public; +} +// ---- +// TypeError: (31-44): Data location must be "memory" for parameter in function, but "storage" was given. diff --git a/test/libsolidity/syntaxTests/dataLocations/publicFunction/public_function_parameters_no_data_location.sol b/test/libsolidity/syntaxTests/dataLocations/publicFunction/public_function_parameters_no_data_location.sol new file mode 100644 index 00000000..f76bd631 --- /dev/null +++ b/test/libsolidity/syntaxTests/dataLocations/publicFunction/public_function_parameters_no_data_location.sol @@ -0,0 +1,5 @@ +contract C { + function h(uint[]) public pure {} +} +// ---- +// TypeError: (28-34): Data location must be "memory" for parameter in function, but none was given. diff --git a/test/libsolidity/syntaxTests/dataLocations/publicFunction/public_function_return_parameters_no_data_location.sol b/test/libsolidity/syntaxTests/dataLocations/publicFunction/public_function_return_parameters_no_data_location.sol new file mode 100644 index 00000000..6b087c34 --- /dev/null +++ b/test/libsolidity/syntaxTests/dataLocations/publicFunction/public_function_return_parameters_no_data_location.sol @@ -0,0 +1,5 @@ +contract C { + function h() public pure returns(uint[]) {} +} +// ---- +// TypeError: (50-56): Data location must be "memory" for return parameter in function, but none was given. diff --git a/test/libsolidity/syntaxTests/dataLocations/variable_declaration_location_specifier_test_non_reference_type.sol b/test/libsolidity/syntaxTests/dataLocations/variable_declaration_location_specifier_test_non_reference_type.sol new file mode 100644 index 00000000..5f6daf68 --- /dev/null +++ b/test/libsolidity/syntaxTests/dataLocations/variable_declaration_location_specifier_test_non_reference_type.sol @@ -0,0 +1,13 @@ +contract test { + function f() public { + uint storage a1; + bytes16 storage b1; + uint memory a2; + bytes16 memory b2; + } +} +// ---- +// TypeError: (48-63): Data location can only be specified for array, struct or mapping types, but "storage" was given. +// TypeError: (71-89): Data location can only be specified for array, struct or mapping types, but "storage" was given. +// TypeError: (97-111): Data location can only be specified for array, struct or mapping types, but "memory" was given. +// TypeError: (119-136): Data location can only be specified for array, struct or mapping types, but "memory" was given. diff --git a/test/libsolidity/syntaxTests/dataLocations/variable_declaration_location_specifier_test_reference_type.sol b/test/libsolidity/syntaxTests/dataLocations/variable_declaration_location_specifier_test_reference_type.sol new file mode 100644 index 00000000..0fbad155 --- /dev/null +++ b/test/libsolidity/syntaxTests/dataLocations/variable_declaration_location_specifier_test_reference_type.sol @@ -0,0 +1,13 @@ +contract test { + uint[] a; + uint[] b; + function f() public { + uint[] storage s1 = a; + uint[] memory s2 = new uint[](42); + uint[] storage s3 = b; + s1.push(42); + s2[3] = 12; + s3.push(42); + } +} +// ---- diff --git a/test/libsolidity/syntaxTests/denominations/combining_hex_and_denomination.sol b/test/libsolidity/syntaxTests/denominations/combining_hex_and_denomination.sol index 3571e8a9..f115ac60 100644 --- a/test/libsolidity/syntaxTests/denominations/combining_hex_and_denomination.sol +++ b/test/libsolidity/syntaxTests/denominations/combining_hex_and_denomination.sol @@ -2,4 +2,4 @@ contract C { uint constant x = 0x01 wei; } // ---- -// Warning: (32-40): Hexadecimal numbers with unit denominations are deprecated. You can use an expression of the form "0x1234 * 1 day" instead. +// TypeError: (32-40): Hexadecimal numbers cannot be used with unit denominations. You can use an expression of the form "0x1234 * 1 day" instead. diff --git a/test/libsolidity/syntaxTests/denominations/combining_hex_and_denomination_050.sol b/test/libsolidity/syntaxTests/denominations/combining_hex_and_denomination_050.sol deleted file mode 100644 index 98865999..00000000 --- a/test/libsolidity/syntaxTests/denominations/combining_hex_and_denomination_050.sol +++ /dev/null @@ -1,6 +0,0 @@ -pragma experimental "v0.5.0"; -contract C { - uint constant x = 0x01 wei; -} -// ---- -// TypeError: (62-70): Hexadecimal numbers cannot be used with unit denominations. You can use an expression of the form "0x1234 * 1 day" instead. diff --git a/test/libsolidity/syntaxTests/denominations/denominations.sol b/test/libsolidity/syntaxTests/denominations/denominations.sol index 6d1aa2f3..43049a14 100644 --- a/test/libsolidity/syntaxTests/denominations/denominations.sol +++ b/test/libsolidity/syntaxTests/denominations/denominations.sol @@ -1,7 +1,6 @@ contract C { uint constant a = 1 wei + 2 szabo + 3 finney + 4 ether; - uint constant b = 1 seconds + 2 minutes + 3 hours + 4 days + 5 weeks + 6 years; + uint constant b = 1 seconds + 2 minutes + 3 hours + 4 days + 5 weeks; uint constant c = 2 szabo / 1 seconds + 3 finney * 3 hours; } // ---- -// Warning: (142-149): Using "years" as a unit denomination is deprecated. diff --git a/test/libsolidity/syntaxTests/denominations/deprecated_year.sol b/test/libsolidity/syntaxTests/denominations/deprecated_year.sol index 30e86535..691c0cb0 100644 --- a/test/libsolidity/syntaxTests/denominations/deprecated_year.sol +++ b/test/libsolidity/syntaxTests/denominations/deprecated_year.sol @@ -2,4 +2,4 @@ contract C { uint constant a = 3 years; } // ---- -// Warning: (32-39): Using "years" as a unit denomination is deprecated. +// TypeError: (32-39): Using "years" as a unit denomination is deprecated. diff --git a/test/libsolidity/syntaxTests/denominations/deprecated_year_050.sol b/test/libsolidity/syntaxTests/denominations/deprecated_year_050.sol deleted file mode 100644 index 4baaeaa3..00000000 --- a/test/libsolidity/syntaxTests/denominations/deprecated_year_050.sol +++ /dev/null @@ -1,6 +0,0 @@ -pragma experimental "v0.5.0"; -contract C { - uint constant a = 3 years; -} -// ---- -// TypeError: (62-69): Using "years" as a unit denomination is deprecated. diff --git a/test/libsolidity/syntaxTests/deprecated_functions.sol b/test/libsolidity/syntaxTests/deprecated_functions.sol index 9df2b43c..62dfcff9 100644 --- a/test/libsolidity/syntaxTests/deprecated_functions.sol +++ b/test/libsolidity/syntaxTests/deprecated_functions.sol @@ -1,12 +1,12 @@ contract test { function f() pure public { - bytes32 x = sha3(); + bytes32 x = sha3(""); x; } function g() public { - suicide(1); + suicide(0x0000000000000000000000000000000000000001); } } // ---- -// Warning: (58-64): "sha3" has been deprecated in favour of "keccak256" -// Warning: (99-109): "suicide" has been deprecated in favour of "selfdestruct" +// TypeError: (58-66): "sha3" has been deprecated in favour of "keccak256" +// TypeError: (101-152): "suicide" has been deprecated in favour of "selfdestruct" diff --git a/test/libsolidity/syntaxTests/deprecated_functions_050.sol b/test/libsolidity/syntaxTests/deprecated_functions_050.sol deleted file mode 100644 index b28e5abb..00000000 --- a/test/libsolidity/syntaxTests/deprecated_functions_050.sol +++ /dev/null @@ -1,15 +0,0 @@ -pragma experimental "v0.5.0"; -contract test { - function f() pure public { - bytes32 x = sha3(uint8(1)); - x; - } - function g() public { - suicide(1); - } -} -// ---- -// TypeError: (88-102): "sha3" has been deprecated in favour of "keccak256" -// TypeError: (88-102): This function only accepts a single "bytes" argument. Please use "abi.encodePacked(...)" or a similar function to encode the data. -// TypeError: (88-102): The provided argument of type uint8 is not implicitly convertible to expected type bytes memory. -// TypeError: (137-147): "suicide" has been deprecated in favour of "selfdestruct" diff --git a/test/libsolidity/syntaxTests/double_variable_declaration.sol b/test/libsolidity/syntaxTests/double_variable_declaration.sol index 9ab87959..53c5c9be 100644 --- a/test/libsolidity/syntaxTests/double_variable_declaration.sol +++ b/test/libsolidity/syntaxTests/double_variable_declaration.sol @@ -1,8 +1,9 @@ contract test { function f() pure public { uint256 x; - if (true) { uint256 x; } + x = 1; + if (true) { uint256 x; x = 2; } } } // ---- -// DeclarationError: (71-80): Identifier already declared. +// Warning: (80-89): This declaration shadows an existing declaration. diff --git a/test/libsolidity/syntaxTests/double_variable_declaration_050.sol b/test/libsolidity/syntaxTests/double_variable_declaration_050.sol deleted file mode 100644 index 2f47e6dc..00000000 --- a/test/libsolidity/syntaxTests/double_variable_declaration_050.sol +++ /dev/null @@ -1,11 +0,0 @@ -pragma experimental "v0.5.0"; -contract test { - function f() pure public { - uint256 x; - if (true) { uint256 x; } - } -} -// ---- -// Warning: (101-110): This declaration shadows an existing declaration. -// Warning: (76-85): Unused local variable. -// Warning: (101-110): Unused local variable. diff --git a/test/libsolidity/syntaxTests/emit/emit_non_event.sol b/test/libsolidity/syntaxTests/emit/emit_non_event.sol index 1df6990d..d5045ddf 100644 --- a/test/libsolidity/syntaxTests/emit/emit_non_event.sol +++ b/test/libsolidity/syntaxTests/emit/emit_non_event.sol @@ -1,10 +1,10 @@ contract C { uint256 Test; - function f() { + function f() public { emit Test(); } } // ---- -// TypeError: (56-62): Type is not callable -// TypeError: (56-60): Expression has to be an event invocation. +// TypeError: (63-69): Type is not callable +// TypeError: (63-67): Expression has to be an event invocation. diff --git a/test/libsolidity/syntaxTests/empty_string_var.sol b/test/libsolidity/syntaxTests/empty_string_var.sol deleted file mode 100644 index e9837590..00000000 --- a/test/libsolidity/syntaxTests/empty_string_var.sol +++ /dev/null @@ -1,11 +0,0 @@ -contract C { - function f() { - var a = ""; - bytes1 b = bytes1(a); - bytes memory c = bytes(a); - string memory d = string(a); - } -} -// ---- -// Warning: (34-39): Use of the "var" keyword is deprecated. -// TypeError: (61-70): Explicit type conversion not allowed from "string memory" to "bytes1". diff --git a/test/libsolidity/syntaxTests/empty_struct.sol b/test/libsolidity/syntaxTests/empty_struct.sol index 12655309..0a52fb72 100644 --- a/test/libsolidity/syntaxTests/empty_struct.sol +++ b/test/libsolidity/syntaxTests/empty_struct.sol @@ -2,4 +2,4 @@ contract test { struct A {} } // ---- -// Warning: (17-28): Defining empty structs is deprecated. +// SyntaxError: (17-28): Defining empty structs is disallowed. diff --git a/test/libsolidity/syntaxTests/empty_struct_050.sol b/test/libsolidity/syntaxTests/empty_struct_050.sol deleted file mode 100644 index 886f1f83..00000000 --- a/test/libsolidity/syntaxTests/empty_struct_050.sol +++ /dev/null @@ -1,6 +0,0 @@ -pragma experimental "v0.5.0"; -contract test { - struct A {} -} -// ---- -// SyntaxError: (47-58): Defining empty structs is disallowed. diff --git a/test/libsolidity/syntaxTests/events/event_array_indexed_v2.sol b/test/libsolidity/syntaxTests/events/event_array_indexed_v2.sol index aaf6028a..3f729a6a 100644 --- a/test/libsolidity/syntaxTests/events/event_array_indexed_v2.sol +++ b/test/libsolidity/syntaxTests/events/event_array_indexed_v2.sol @@ -4,4 +4,4 @@ contract c { } // ---- // Warning: (0-33): Experimental features are turned on. Do not use experimental features on live deployments. -// TypeError: (59-65): Indexed reference types cannot yet be used with ABIEncoderV2. +// TypeError: (59-73): Indexed reference types cannot yet be used with ABIEncoderV2. diff --git a/test/libsolidity/syntaxTests/events/event_nested_array_indexed_v2.sol b/test/libsolidity/syntaxTests/events/event_nested_array_indexed_v2.sol index ffae5b9c..f05b884e 100644 --- a/test/libsolidity/syntaxTests/events/event_nested_array_indexed_v2.sol +++ b/test/libsolidity/syntaxTests/events/event_nested_array_indexed_v2.sol @@ -4,4 +4,4 @@ contract c { } // ---- // Warning: (0-33): Experimental features are turned on. Do not use experimental features on live deployments. -// TypeError: (59-67): Indexed reference types cannot yet be used with ABIEncoderV2. +// TypeError: (59-75): Indexed reference types cannot yet be used with ABIEncoderV2. diff --git a/test/libsolidity/syntaxTests/events/event_struct_indexed.sol b/test/libsolidity/syntaxTests/events/event_struct_indexed.sol index 69ee5017..7332cb3b 100644 --- a/test/libsolidity/syntaxTests/events/event_struct_indexed.sol +++ b/test/libsolidity/syntaxTests/events/event_struct_indexed.sol @@ -3,4 +3,4 @@ contract c { event E(S indexed); } // ---- -// TypeError: (51-52): This type is only supported in the new experimental ABI encoder. Use "pragma experimental ABIEncoderV2;" to enable the feature. +// TypeError: (51-60): This type is only supported in the new experimental ABI encoder. Use "pragma experimental ABIEncoderV2;" to enable the feature. diff --git a/test/libsolidity/syntaxTests/events/event_struct_indexed_v2.sol b/test/libsolidity/syntaxTests/events/event_struct_indexed_v2.sol index a8e0837f..a1d8cf04 100644 --- a/test/libsolidity/syntaxTests/events/event_struct_indexed_v2.sol +++ b/test/libsolidity/syntaxTests/events/event_struct_indexed_v2.sol @@ -5,4 +5,4 @@ contract c { } // ---- // Warning: (0-33): Experimental features are turned on. Do not use experimental features on live deployments. -// TypeError: (85-86): Indexed reference types cannot yet be used with ABIEncoderV2. +// TypeError: (85-94): Indexed reference types cannot yet be used with ABIEncoderV2. diff --git a/test/libsolidity/syntaxTests/fallback/default_visibility.sol b/test/libsolidity/syntaxTests/fallback/default_visibility.sol new file mode 100644 index 00000000..6fbb15a5 --- /dev/null +++ b/test/libsolidity/syntaxTests/fallback/default_visibility.sol @@ -0,0 +1,7 @@ +contract C { + // Check that visibility is also enforced for the fallback function. + function() {} +} +// ---- +// SyntaxError: (90-103): No visibility specified. Did you intend to add "external"? +// TypeError: (90-103): Fallback function must be defined as "external". diff --git a/test/libsolidity/syntaxTests/fallback/pure_modifier.sol b/test/libsolidity/syntaxTests/fallback/pure_modifier.sol index 20d5b0ac..12d790d1 100644 --- a/test/libsolidity/syntaxTests/fallback/pure_modifier.sol +++ b/test/libsolidity/syntaxTests/fallback/pure_modifier.sol @@ -1,6 +1,6 @@ contract C { uint x; - function() pure { x = 2; } + function() external pure { x = 2; } } // ---- -// TypeError: (29-55): Fallback function must be payable or non-payable, but is "pure". +// TypeError: (29-64): Fallback function must be payable or non-payable, but is "pure". diff --git a/test/libsolidity/syntaxTests/fallback/view_modifier.sol b/test/libsolidity/syntaxTests/fallback/view_modifier.sol index 44c5d204..2497e9fa 100644 --- a/test/libsolidity/syntaxTests/fallback/view_modifier.sol +++ b/test/libsolidity/syntaxTests/fallback/view_modifier.sol @@ -1,6 +1,6 @@ contract C { uint x; - function() view { x = 2; } + function() external view { x = 2; } } // ---- -// TypeError: (29-55): Fallback function must be payable or non-payable, but is "view". +// TypeError: (29-64): Fallback function must be payable or non-payable, but is "view". diff --git a/test/libsolidity/syntaxTests/functionCalls/named_arguments_for_functions_that_take_arbitrary_parameters.sol b/test/libsolidity/syntaxTests/functionCalls/named_arguments_for_functions_that_take_arbitrary_parameters.sol new file mode 100644 index 00000000..089e1dbf --- /dev/null +++ b/test/libsolidity/syntaxTests/functionCalls/named_arguments_for_functions_that_take_arbitrary_parameters.sol @@ -0,0 +1,7 @@ +contract C { + function f() pure public { + abi.encodeWithSelector({selector:"abc"}); + } +} +// ---- +// TypeError: (52-92): Named arguments cannot be used for functions that take arbitrary parameters. diff --git a/test/libsolidity/syntaxTests/functionTypes/delete_function_type.sol b/test/libsolidity/syntaxTests/functionTypes/delete_function_type.sol index a6fe6c22..2481c455 100644 --- a/test/libsolidity/syntaxTests/functionTypes/delete_function_type.sol +++ b/test/libsolidity/syntaxTests/functionTypes/delete_function_type.sol @@ -3,15 +3,13 @@ contract C { function(uint) internal returns (uint) y; function f() public { delete x; - var a = y; + function(uint) internal returns (uint) a = y; delete a; delete y; - var c = f; + function() internal c = f; delete c; function(uint) internal returns (uint) g; delete g; } } // ---- -// Warning: (157-162): Use of the "var" keyword is deprecated. -// Warning: (212-217): Use of the "var" keyword is deprecated. diff --git a/test/libsolidity/syntaxTests/functionTypes/external_function_to_function_type_calldata_parameter.sol b/test/libsolidity/syntaxTests/functionTypes/external_function_to_function_type_calldata_parameter.sol index eb4f0693..f22afe5e 100644 --- a/test/libsolidity/syntaxTests/functionTypes/external_function_to_function_type_calldata_parameter.sol +++ b/test/libsolidity/syntaxTests/functionTypes/external_function_to_function_type_calldata_parameter.sol @@ -3,7 +3,7 @@ // when converting to a function type. contract C { function f(function(bytes memory) pure external /*g*/) pure public { } - function callback(bytes) pure external {} + function callback(bytes calldata) pure external {} function g() view public { f(this.callback); } diff --git a/test/libsolidity/syntaxTests/functionTypes/external_function_type_to_address_payable.sol b/test/libsolidity/syntaxTests/functionTypes/external_function_type_to_address_payable.sol new file mode 100644 index 00000000..adffb14b --- /dev/null +++ b/test/libsolidity/syntaxTests/functionTypes/external_function_type_to_address_payable.sol @@ -0,0 +1,7 @@ +contract C { + function f() public view returns (address payable) { + return address(this.f); + } +} +// ---- +// TypeError: (85-100): Return argument type address is not implicitly convertible to expected type (type of first return variable) address payable. diff --git a/test/libsolidity/syntaxTests/functionTypes/function_type_constructor.sol b/test/libsolidity/syntaxTests/functionTypes/function_type_constructor.sol index 95ebc179..51f0b10d 100644 --- a/test/libsolidity/syntaxTests/functionTypes/function_type_constructor.sol +++ b/test/libsolidity/syntaxTests/functionTypes/function_type_constructor.sol @@ -1,7 +1,7 @@ contract C { // Fool parser into parsing a constructor as a function type. - constructor() x; + constructor() public x; } // ---- -// Warning: (83-99): Modifiers of functions without implementation are ignored. -// DeclarationError: (97-98): Undeclared identifier. +// SyntaxError: (83-106): Functions without implementation cannot have modifiers. +// DeclarationError: (104-105): Undeclared identifier. diff --git a/test/libsolidity/syntaxTests/functionTypes/function_type_constructor_local.sol b/test/libsolidity/syntaxTests/functionTypes/function_type_constructor_local.sol index b89a3bb4..42697b73 100644 --- a/test/libsolidity/syntaxTests/functionTypes/function_type_constructor_local.sol +++ b/test/libsolidity/syntaxTests/functionTypes/function_type_constructor_local.sol @@ -5,4 +5,4 @@ contract C { } } // ---- -// ParserError: (118-119): Expected ';' but got identifier +// ParserError: (104-115): Expected primary expression. diff --git a/test/libsolidity/syntaxTests/functionTypes/function_type_return_parameters_with_names.sol b/test/libsolidity/syntaxTests/functionTypes/function_type_return_parameters_with_names.sol new file mode 100644 index 00000000..12191530 --- /dev/null +++ b/test/libsolidity/syntaxTests/functionTypes/function_type_return_parameters_with_names.sol @@ -0,0 +1,5 @@ +contract C { + function(uint) returns (bool ret) f; +} +// ---- +// SyntaxError: (41-49): Return parameters in function types may not be named. diff --git a/test/libsolidity/syntaxTests/functionTypes/valid_function_type_variables.sol b/test/libsolidity/syntaxTests/functionTypes/valid_function_type_variables.sol index 10c6767c..e7d2c9a9 100644 --- a/test/libsolidity/syntaxTests/functionTypes/valid_function_type_variables.sol +++ b/test/libsolidity/syntaxTests/functionTypes/valid_function_type_variables.sol @@ -1,5 +1,5 @@ contract test { - function fa(uint) {} + function fa(uint) public {} function fb(uint) internal {} function fc(uint) internal {} function fd(uint) external {} @@ -13,11 +13,14 @@ contract test { function(uint) internal internal c = fc; function(uint) external d = this.fd; function(uint) external internal e = this.fe; - function(uint) internal public f = ff; - function(uint) internal pure public g = fg; - function(uint) pure internal public h = fh; + function(uint) internal f = ff; + function(uint) internal pure g = fg; + function(uint) pure internal h = fh; } // ---- -// TypeError: (545-582): Internal or recursive type is not allowed for public state variables. -// TypeError: (588-630): Internal or recursive type is not allowed for public state variables. -// TypeError: (636-678): Internal or recursive type is not allowed for public state variables. +// Warning: (20-47): Function state mutability can be restricted to pure +// Warning: (52-81): Function state mutability can be restricted to pure +// Warning: (86-115): Function state mutability can be restricted to pure +// Warning: (120-149): Function state mutability can be restricted to pure +// Warning: (154-183): Function state mutability can be restricted to pure +// Warning: (188-217): Function state mutability can be restricted to pure diff --git a/test/libsolidity/syntaxTests/functionTypes/warn_function_type_return_parameters_with_names.sol b/test/libsolidity/syntaxTests/functionTypes/warn_function_type_return_parameters_with_names.sol deleted file mode 100644 index 67a74e54..00000000 --- a/test/libsolidity/syntaxTests/functionTypes/warn_function_type_return_parameters_with_names.sol +++ /dev/null @@ -1,5 +0,0 @@ -contract C { - function(uint) returns (bool ret) f; -} -// ---- -// Warning: (41-49): Naming function type return parameters is deprecated. diff --git a/test/libsolidity/syntaxTests/globalFunctions/call_with_wrong_arg_count.sol b/test/libsolidity/syntaxTests/globalFunctions/call_with_wrong_arg_count.sol new file mode 100644 index 00000000..92ec4eb7 --- /dev/null +++ b/test/libsolidity/syntaxTests/globalFunctions/call_with_wrong_arg_count.sol @@ -0,0 +1,17 @@ +contract C { + function f() public { + (bool success,) = address(this).call(); + require(success); + (success,) = address(this).call(bytes4(0x12345678)); + require(success); + (success,) = address(this).call(uint(1)); + require(success); + (success,) = address(this).call(uint(1), uint(2)); + require(success); + } +} +// ---- +// TypeError: (65-85): Wrong argument count for function call: 0 arguments given but expected 1. This function requires a single bytes argument. Use "" as argument to provide empty calldata. +// TypeError: (153-171): Invalid type for argument in function call. Invalid implicit conversion from bytes4 to bytes memory requested. This function requires a single bytes argument. If all your arguments are value types, you can use abi.encode(...) to properly generate it. +// TypeError: (240-247): Invalid type for argument in function call. Invalid implicit conversion from uint256 to bytes memory requested. This function requires a single bytes argument. If all your arguments are value types, you can use abi.encode(...) to properly generate it. +// TypeError: (297-333): Wrong argument count for function call: 2 arguments given but expected 1. This function requires a single bytes argument. If all your arguments are value types, you can use abi.encode(...) to properly generate it. diff --git a/test/libsolidity/syntaxTests/globalFunctions/callcode_with_wrong_arg_count.sol b/test/libsolidity/syntaxTests/globalFunctions/callcode_with_wrong_arg_count.sol new file mode 100644 index 00000000..655d5f4c --- /dev/null +++ b/test/libsolidity/syntaxTests/globalFunctions/callcode_with_wrong_arg_count.sol @@ -0,0 +1,14 @@ +contract C { + function f() public { + (bool success,) = address(this).callcode(); + require(success); + (success,) = address(this).callcode(uint(1)); + require(success); + (success,) = address(this).callcode(uint(1), uint(2)); + require(success); + } +} +// ---- +// TypeError: (65-89): Wrong argument count for function call: 0 arguments given but expected 1. This function requires a single bytes argument. Use "" as argument to provide empty calldata. +// TypeError: (161-168): Invalid type for argument in function call. Invalid implicit conversion from uint256 to bytes memory requested. This function requires a single bytes argument. If all your arguments are value types, you can use abi.encode(...) to properly generate it. +// TypeError: (218-258): Wrong argument count for function call: 2 arguments given but expected 1. This function requires a single bytes argument. If all your arguments are value types, you can use abi.encode(...) to properly generate it. diff --git a/test/libsolidity/syntaxTests/globalFunctions/delegatecall_with_wrong_arg_count.sol b/test/libsolidity/syntaxTests/globalFunctions/delegatecall_with_wrong_arg_count.sol new file mode 100644 index 00000000..fa524b99 --- /dev/null +++ b/test/libsolidity/syntaxTests/globalFunctions/delegatecall_with_wrong_arg_count.sol @@ -0,0 +1,14 @@ +contract C { + function f() public { + (bool success,) = address(this).delegatecall(); + require(success); + (success,) = address(this).delegatecall(uint(1)); + require(success); + (success,) = address(this).delegatecall(uint(1), uint(2)); + require(success); + } +} +// ---- +// TypeError: (65-93): Wrong argument count for function call: 0 arguments given but expected 1. This function requires a single bytes argument. Use "" as argument to provide empty calldata. +// TypeError: (169-176): Invalid type for argument in function call. Invalid implicit conversion from uint256 to bytes memory requested. This function requires a single bytes argument. If all your arguments are value types, you can use abi.encode(...) to properly generate it. +// TypeError: (226-270): Wrong argument count for function call: 2 arguments given but expected 1. This function requires a single bytes argument. If all your arguments are value types, you can use abi.encode(...) to properly generate it. diff --git a/test/libsolidity/syntaxTests/globalFunctions/keccak256_with_wrong_arg_count.sol b/test/libsolidity/syntaxTests/globalFunctions/keccak256_with_wrong_arg_count.sol new file mode 100644 index 00000000..4857bc2e --- /dev/null +++ b/test/libsolidity/syntaxTests/globalFunctions/keccak256_with_wrong_arg_count.sol @@ -0,0 +1,11 @@ +contract C { + function f() public { + require(keccak256() != 0); + require(keccak256(uint(1)) != 0); + require(keccak256(uint(1), uint(2)) != 0); + } +} +// ---- +// TypeError: (55-66): Wrong argument count for function call: 0 arguments given but expected 1. This function requires a single bytes argument. Use abi.encodePacked(...) to obtain the pre-0.5.0 behaviour or abi.encode(...) to use ABI encoding. +// TypeError: (100-107): Invalid type for argument in function call. Invalid implicit conversion from uint256 to bytes memory requested. This function requires a single bytes argument. Use abi.encodePacked(...) to obtain the pre-0.5.0 behaviour or abi.encode(...) to use ABI encoding. +// TypeError: (132-159): Wrong argument count for function call: 2 arguments given but expected 1. This function requires a single bytes argument. Use abi.encodePacked(...) to obtain the pre-0.5.0 behaviour or abi.encode(...) to use ABI encoding. diff --git a/test/libsolidity/syntaxTests/globalFunctions/ripemd160_with_wrong_arg_count.sol b/test/libsolidity/syntaxTests/globalFunctions/ripemd160_with_wrong_arg_count.sol new file mode 100644 index 00000000..da41fccd --- /dev/null +++ b/test/libsolidity/syntaxTests/globalFunctions/ripemd160_with_wrong_arg_count.sol @@ -0,0 +1,11 @@ +contract C { + function f() public { + require(ripemd160() != 0); + require(ripemd160(uint(1)) != 0); + require(ripemd160(uint(1), uint(2)) != 0); + } +} +// ---- +// TypeError: (55-66): Wrong argument count for function call: 0 arguments given but expected 1. This function requires a single bytes argument. Use abi.encodePacked(...) to obtain the pre-0.5.0 behaviour or abi.encode(...) to use ABI encoding. +// TypeError: (100-107): Invalid type for argument in function call. Invalid implicit conversion from uint256 to bytes memory requested. This function requires a single bytes argument. Use abi.encodePacked(...) to obtain the pre-0.5.0 behaviour or abi.encode(...) to use ABI encoding. +// TypeError: (132-159): Wrong argument count for function call: 2 arguments given but expected 1. This function requires a single bytes argument. Use abi.encodePacked(...) to obtain the pre-0.5.0 behaviour or abi.encode(...) to use ABI encoding. diff --git a/test/libsolidity/syntaxTests/globalFunctions/sha256_with_wrong_arg_count.sol b/test/libsolidity/syntaxTests/globalFunctions/sha256_with_wrong_arg_count.sol new file mode 100644 index 00000000..2939e7fc --- /dev/null +++ b/test/libsolidity/syntaxTests/globalFunctions/sha256_with_wrong_arg_count.sol @@ -0,0 +1,11 @@ +contract C { + function f() public { + require(sha256() != 0); + require(sha256(uint(1)) != 0); + require(sha256(uint(1), uint(2)) != 0); + } +} +// ---- +// TypeError: (55-63): Wrong argument count for function call: 0 arguments given but expected 1. This function requires a single bytes argument. Use abi.encodePacked(...) to obtain the pre-0.5.0 behaviour or abi.encode(...) to use ABI encoding. +// TypeError: (94-101): Invalid type for argument in function call. Invalid implicit conversion from uint256 to bytes memory requested. This function requires a single bytes argument. Use abi.encodePacked(...) to obtain the pre-0.5.0 behaviour or abi.encode(...) to use ABI encoding. +// TypeError: (126-150): Wrong argument count for function call: 2 arguments given but expected 1. This function requires a single bytes argument. Use abi.encodePacked(...) to obtain the pre-0.5.0 behaviour or abi.encode(...) to use ABI encoding. diff --git a/test/libsolidity/syntaxTests/indexing/array_multidim_rational.sol b/test/libsolidity/syntaxTests/indexing/array_multidim_rational.sol new file mode 100644 index 00000000..df9f8223 --- /dev/null +++ b/test/libsolidity/syntaxTests/indexing/array_multidim_rational.sol @@ -0,0 +1,11 @@ +contract C { + function f() public { + bytes[32] memory a; + a[8**90][8**90][8**90*0.1]; + } +} +// ---- +// TypeError: (67-72): Type int_const 1897...(74 digits omitted)...1424 is not implicitly convertible to expected type uint256. +// TypeError: (74-79): Type int_const 1897...(74 digits omitted)...1424 is not implicitly convertible to expected type uint256. +// TypeError: (81-90): Type rational_const 9485...(73 digits omitted)...5712 / 5 is not implicitly convertible to expected type uint256. +// TypeError: (65-91): Index expression cannot be represented as an unsigned integer. diff --git a/test/libsolidity/syntaxTests/indexing/array_multim_overflow_index.sol b/test/libsolidity/syntaxTests/indexing/array_multim_overflow_index.sol new file mode 100644 index 00000000..9c98ad45 --- /dev/null +++ b/test/libsolidity/syntaxTests/indexing/array_multim_overflow_index.sol @@ -0,0 +1,11 @@ +contract C { + function f() public { + bytes[32] memory a; + a[8**90][8**90][1 - 8**90]; + } +} +// ---- +// TypeError: (67-72): Type int_const 1897...(74 digits omitted)...1424 is not implicitly convertible to expected type uint256. +// TypeError: (74-79): Type int_const 1897...(74 digits omitted)...1424 is not implicitly convertible to expected type uint256. +// TypeError: (81-90): Type int_const -189...(75 digits omitted)...1423 is not implicitly convertible to expected type uint256. +// TypeError: (65-91): Index expression cannot be represented as an unsigned integer. diff --git a/test/libsolidity/syntaxTests/indexing/array_negative_index.sol b/test/libsolidity/syntaxTests/indexing/array_negative_index.sol new file mode 100644 index 00000000..019d023b --- /dev/null +++ b/test/libsolidity/syntaxTests/indexing/array_negative_index.sol @@ -0,0 +1,8 @@ +contract C { + function f() public { + bytes[32] memory a; + a[-1]; + } +} +// ---- +// TypeError: (67-69): Type int_const -1 is not implicitly convertible to expected type uint256. diff --git a/test/libsolidity/syntaxTests/indexing/array_noninteger_index.sol b/test/libsolidity/syntaxTests/indexing/array_noninteger_index.sol new file mode 100644 index 00000000..7c0ac9fe --- /dev/null +++ b/test/libsolidity/syntaxTests/indexing/array_noninteger_index.sol @@ -0,0 +1,8 @@ +contract C { + function f() public { + bytes[32] memory a; + a[888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888]; + } +} +// ---- +// TypeError: (67-178): Type int_const 8888...(103 digits omitted)...8888 is not implicitly convertible to expected type uint256. diff --git a/test/libsolidity/syntaxTests/indexing/array_out_of_bounds_index.sol b/test/libsolidity/syntaxTests/indexing/array_out_of_bounds_index.sol new file mode 100644 index 00000000..b0079857 --- /dev/null +++ b/test/libsolidity/syntaxTests/indexing/array_out_of_bounds_index.sol @@ -0,0 +1,8 @@ +contract C { + function f() public { + bytes[32] memory a; + a[64]; + } +} +// ---- +// TypeError: (65-70): Out of bounds array access. diff --git a/test/libsolidity/syntaxTests/indexing/array_without_index.sol b/test/libsolidity/syntaxTests/indexing/array_without_index.sol new file mode 100644 index 00000000..6b1c2778 --- /dev/null +++ b/test/libsolidity/syntaxTests/indexing/array_without_index.sol @@ -0,0 +1,8 @@ +contract C { + function f() public { + bytes memory a; + a[]; + } +} +// ---- +// TypeError: (61-64): Index expression cannot be omitted. diff --git a/test/libsolidity/syntaxTests/indexing/fixedbytes_negative_index.sol b/test/libsolidity/syntaxTests/indexing/fixedbytes_negative_index.sol new file mode 100644 index 00000000..12399317 --- /dev/null +++ b/test/libsolidity/syntaxTests/indexing/fixedbytes_negative_index.sol @@ -0,0 +1,9 @@ +contract C { + function f() public { + bytes32 b; + b[-1]; + } +} +// ---- +// TypeError: (58-60): Type int_const -1 is not implicitly convertible to expected type uint256. +// TypeError: (56-61): Index expression cannot be represented as an unsigned integer. diff --git a/test/libsolidity/syntaxTests/indexing/fixedbytes_noninteger_index.sol b/test/libsolidity/syntaxTests/indexing/fixedbytes_noninteger_index.sol new file mode 100644 index 00000000..adf7db61 --- /dev/null +++ b/test/libsolidity/syntaxTests/indexing/fixedbytes_noninteger_index.sol @@ -0,0 +1,9 @@ +contract C { + function f() public { + bytes32 b; + b[888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888]; + } +} +// ---- +// TypeError: (58-169): Type int_const 8888...(103 digits omitted)...8888 is not implicitly convertible to expected type uint256. +// TypeError: (56-170): Index expression cannot be represented as an unsigned integer. diff --git a/test/libsolidity/syntaxTests/indexing/fixedbytes_out_of_bounds_index.sol b/test/libsolidity/syntaxTests/indexing/fixedbytes_out_of_bounds_index.sol new file mode 100644 index 00000000..8264a8b2 --- /dev/null +++ b/test/libsolidity/syntaxTests/indexing/fixedbytes_out_of_bounds_index.sol @@ -0,0 +1,8 @@ +contract C { + function f() public { + bytes32 b; + b[64]; + } +} +// ---- +// TypeError: (56-61): Out of bounds array access. diff --git a/test/libsolidity/syntaxTests/indexing/fixedbytes_without_index.sol b/test/libsolidity/syntaxTests/indexing/fixedbytes_without_index.sol new file mode 100644 index 00000000..979ac8a7 --- /dev/null +++ b/test/libsolidity/syntaxTests/indexing/fixedbytes_without_index.sol @@ -0,0 +1,8 @@ +contract C { + function f() public { + bytes32 b; + b[]; + } +} +// ---- +// TypeError: (56-59): Index expression cannot be omitted. diff --git a/test/libsolidity/syntaxTests/indexing/function_type.sol b/test/libsolidity/syntaxTests/indexing/function_type.sol new file mode 100644 index 00000000..6c6c06a9 --- /dev/null +++ b/test/libsolidity/syntaxTests/indexing/function_type.sol @@ -0,0 +1,7 @@ +contract C { + function f() public { + f[0]; + } +} +// ---- +// TypeError: (41-42): Indexed expression has to be a type, mapping or array (is function ()) diff --git a/test/libsolidity/syntaxTests/indexing/function_type_without_index.sol b/test/libsolidity/syntaxTests/indexing/function_type_without_index.sol new file mode 100644 index 00000000..bf511bc9 --- /dev/null +++ b/test/libsolidity/syntaxTests/indexing/function_type_without_index.sol @@ -0,0 +1,7 @@ +contract C { + function f() public { + f[]; + } +} +// ---- +// TypeError: (41-42): Indexed expression has to be a type, mapping or array (is function ()) diff --git a/test/libsolidity/syntaxTests/inheritance/base_arguments_empty_parentheses.sol b/test/libsolidity/syntaxTests/inheritance/base_arguments_empty_parentheses.sol index 0b18b995..692b1827 100644 --- a/test/libsolidity/syntaxTests/inheritance/base_arguments_empty_parentheses.sol +++ b/test/libsolidity/syntaxTests/inheritance/base_arguments_empty_parentheses.sol @@ -4,4 +4,4 @@ contract Base { contract Derived is Base(2) { } contract Derived2 is Base(), Derived() { } // ---- -// Warning: (101-107): Wrong argument count for constructor call: 0 arguments given but expected 1. +// TypeError: (101-107): Wrong argument count for constructor call: 0 arguments given but expected 1. Remove parentheses if you do not want to provide arguments here. diff --git a/test/libsolidity/syntaxTests/inheritance/base_arguments_empty_parentheses_V050.sol b/test/libsolidity/syntaxTests/inheritance/base_arguments_empty_parentheses_V050.sol deleted file mode 100644 index db04ab8c..00000000 --- a/test/libsolidity/syntaxTests/inheritance/base_arguments_empty_parentheses_V050.sol +++ /dev/null @@ -1,9 +0,0 @@ -pragma experimental "v0.5.0"; - -contract Base { - constructor(uint) public {} -} -contract Derived is Base(2) { } -contract Derived2 is Base(), Derived() { } -// ---- -// TypeError: (132-138): Wrong argument count for constructor call: 0 arguments given but expected 1. diff --git a/test/libsolidity/syntaxTests/inheritance/base_arguments_multiple_inheritance.sol b/test/libsolidity/syntaxTests/inheritance/base_arguments_multiple_inheritance.sol index 015b33e5..96be62f2 100644 --- a/test/libsolidity/syntaxTests/inheritance/base_arguments_multiple_inheritance.sol +++ b/test/libsolidity/syntaxTests/inheritance/base_arguments_multiple_inheritance.sol @@ -6,4 +6,4 @@ contract Derived is Base, Base1 { constructor(uint i) Base(i) public {} } // ---- -// Warning: (138-145): Base constructor arguments given twice. +// DeclarationError: (138-145): Base constructor arguments given twice. diff --git a/test/libsolidity/syntaxTests/inheritance/disallow_modifier_style_without_parentheses.sol b/test/libsolidity/syntaxTests/inheritance/disallow_modifier_style_without_parentheses.sol index 6cf68d2a..8f5ceef8 100644 --- a/test/libsolidity/syntaxTests/inheritance/disallow_modifier_style_without_parentheses.sol +++ b/test/libsolidity/syntaxTests/inheritance/disallow_modifier_style_without_parentheses.sol @@ -1,4 +1,4 @@ contract A { constructor() public { } } contract B is A { constructor() A public { } } // ---- -// Warning: (72-73): Modifier-style base constructor call without arguments. +// DeclarationError: (72-73): Modifier-style base constructor call without arguments. diff --git a/test/libsolidity/syntaxTests/inheritance/duplicated_constructor_call/ancestor.sol b/test/libsolidity/syntaxTests/inheritance/duplicated_constructor_call/ancestor.sol index 24cff54d..76cc937b 100644 --- a/test/libsolidity/syntaxTests/inheritance/duplicated_constructor_call/ancestor.sol +++ b/test/libsolidity/syntaxTests/inheritance/duplicated_constructor_call/ancestor.sol @@ -2,4 +2,4 @@ contract A { constructor(uint) public { } } contract B is A(2) { constructor() public { } } contract C is B { constructor() A(3) public { } } // ---- -// Warning: (125-129): Base constructor arguments given twice. +// DeclarationError: (125-129): Base constructor arguments given twice. diff --git a/test/libsolidity/syntaxTests/inheritance/duplicated_constructor_call/ancestor_V050.sol b/test/libsolidity/syntaxTests/inheritance/duplicated_constructor_call/ancestor_V050.sol deleted file mode 100644 index 8d5df5bf..00000000 --- a/test/libsolidity/syntaxTests/inheritance/duplicated_constructor_call/ancestor_V050.sol +++ /dev/null @@ -1,7 +0,0 @@ -pragma experimental "v0.5.0"; - -contract A { constructor(uint) public { } } -contract B is A(2) { constructor() public { } } -contract C is B { constructor() A(3) public { } } -// ---- -// DeclarationError: (156-160): Base constructor arguments given twice. diff --git a/test/libsolidity/syntaxTests/inheritance/duplicated_constructor_call/base.sol b/test/libsolidity/syntaxTests/inheritance/duplicated_constructor_call/base.sol index 9ceaea5e..4c7a684f 100644 --- a/test/libsolidity/syntaxTests/inheritance/duplicated_constructor_call/base.sol +++ b/test/libsolidity/syntaxTests/inheritance/duplicated_constructor_call/base.sol @@ -1,4 +1,4 @@ contract A { constructor(uint) public { } } contract B is A(2) { constructor() A(3) public { } } // ---- -// Warning: (79-83): Base constructor arguments given twice. +// DeclarationError: (79-83): Base constructor arguments given twice. diff --git a/test/libsolidity/syntaxTests/inheritance/duplicated_constructor_call/base_V050.sol b/test/libsolidity/syntaxTests/inheritance/duplicated_constructor_call/base_V050.sol deleted file mode 100644 index f9325f99..00000000 --- a/test/libsolidity/syntaxTests/inheritance/duplicated_constructor_call/base_V050.sol +++ /dev/null @@ -1,6 +0,0 @@ -pragma experimental "v0.5.0"; - -contract A { constructor(uint) public { } } -contract B is A(2) { constructor() A(3) public { } } -// ---- -// DeclarationError: (110-114): Base constructor arguments given twice. diff --git a/test/libsolidity/syntaxTests/inheritance/duplicated_constructor_call/base_multi.sol b/test/libsolidity/syntaxTests/inheritance/duplicated_constructor_call/base_multi.sol index e5c2aa36..2e77e077 100644 --- a/test/libsolidity/syntaxTests/inheritance/duplicated_constructor_call/base_multi.sol +++ b/test/libsolidity/syntaxTests/inheritance/duplicated_constructor_call/base_multi.sol @@ -3,5 +3,5 @@ contract A is C(2) {} contract B is C(2) {} contract D is A, B { constructor() C(3) public {} } // ---- -// Warning: (122-126): Base constructor arguments given twice. -// Warning: (122-126): Base constructor arguments given twice. +// DeclarationError: (122-126): Base constructor arguments given twice. +// DeclarationError: (122-126): Base constructor arguments given twice. diff --git a/test/libsolidity/syntaxTests/inheritance/duplicated_constructor_call/base_multi_no_constructor.sol b/test/libsolidity/syntaxTests/inheritance/duplicated_constructor_call/base_multi_no_constructor.sol index 1abf2992..0beb1552 100644 --- a/test/libsolidity/syntaxTests/inheritance/duplicated_constructor_call/base_multi_no_constructor.sol +++ b/test/libsolidity/syntaxTests/inheritance/duplicated_constructor_call/base_multi_no_constructor.sol @@ -3,4 +3,4 @@ contract A is C(2) {} contract B is C(2) {} contract D is A, B {} // ---- -// Warning: (87-108): Base constructor arguments given twice. +// DeclarationError: (87-108): Base constructor arguments given twice. diff --git a/test/libsolidity/syntaxTests/inheritance/duplicated_constructor_call/base_multi_no_constructor_modifier_style.sol b/test/libsolidity/syntaxTests/inheritance/duplicated_constructor_call/base_multi_no_constructor_modifier_style.sol index e15242db..7142840e 100644 --- a/test/libsolidity/syntaxTests/inheritance/duplicated_constructor_call/base_multi_no_constructor_modifier_style.sol +++ b/test/libsolidity/syntaxTests/inheritance/duplicated_constructor_call/base_multi_no_constructor_modifier_style.sol @@ -3,4 +3,4 @@ contract A is C { constructor() C(2) public {} } contract B is C { constructor() C(2) public {} } contract D is A, B { } // ---- -// Warning: (141-163): Base constructor arguments given twice. +// DeclarationError: (141-163): Base constructor arguments given twice. diff --git a/test/libsolidity/syntaxTests/inheritance/modifiers_in_constructor_context.sol b/test/libsolidity/syntaxTests/inheritance/modifiers_in_constructor_context.sol new file mode 100644 index 00000000..d8ce0e48 --- /dev/null +++ b/test/libsolidity/syntaxTests/inheritance/modifiers_in_constructor_context.sol @@ -0,0 +1,9 @@ +// This generated an invalid warning on m1 in some compiler versions. +contract A { + constructor() m1 public { } + modifier m1 { _; } +} +contract B is A { + modifier m2 { _; } + constructor() A() m1 m2 public { } +} diff --git a/test/libsolidity/syntaxTests/inheritance/override/add_view.sol b/test/libsolidity/syntaxTests/inheritance/override/add_view.sol index 9973b23e..21e43792 100644 --- a/test/libsolidity/syntaxTests/inheritance/override/add_view.sol +++ b/test/libsolidity/syntaxTests/inheritance/override/add_view.sol @@ -1,4 +1,4 @@ contract B { function f() public {} } -contract C is B { function f() view {} } +contract C is B { function f() public view {} } // ---- -// TypeError: (56-76): Overriding function changes state mutability from "nonpayable" to "view". +// TypeError: (56-83): Overriding function changes state mutability from "nonpayable" to "view". diff --git a/test/libsolidity/syntaxTests/inheritance/override/function_state_variable.sol b/test/libsolidity/syntaxTests/inheritance/override/function_state_variable.sol new file mode 100644 index 00000000..023a161a --- /dev/null +++ b/test/libsolidity/syntaxTests/inheritance/override/function_state_variable.sol @@ -0,0 +1,2 @@ +interface ERC20 { function x() external returns (uint); } +contract C is ERC20 { uint public x; } diff --git a/test/libsolidity/syntaxTests/inheritance/override/remove_view.sol b/test/libsolidity/syntaxTests/inheritance/override/remove_view.sol index e58f6b20..cc785858 100644 --- a/test/libsolidity/syntaxTests/inheritance/override/remove_view.sol +++ b/test/libsolidity/syntaxTests/inheritance/override/remove_view.sol @@ -1,4 +1,4 @@ -contract B { function f() view {} } +contract B { function f() public view {} } contract C is B { function f() public {} } // ---- -// TypeError: (54-76): Overriding function changes state mutability from "view" to "nonpayable". +// TypeError: (61-83): Overriding function changes state mutability from "view" to "nonpayable". diff --git a/test/libsolidity/syntaxTests/inheritance/override/state_variable_function.sol b/test/libsolidity/syntaxTests/inheritance/override/state_variable_function.sol new file mode 100644 index 00000000..0f05cc8e --- /dev/null +++ b/test/libsolidity/syntaxTests/inheritance/override/state_variable_function.sol @@ -0,0 +1,8 @@ +contract A { + uint public x; +} +contract C is A { + function x() public returns (uint); +} +// ---- +// DeclarationError: (50-85): Identifier already declared. diff --git a/test/libsolidity/syntaxTests/inheritance/too_few_base_arguments.sol b/test/libsolidity/syntaxTests/inheritance/too_few_base_arguments.sol index c55c41f2..1ce48200 100644 --- a/test/libsolidity/syntaxTests/inheritance/too_few_base_arguments.sol +++ b/test/libsolidity/syntaxTests/inheritance/too_few_base_arguments.sol @@ -6,5 +6,5 @@ contract Derived2 is Base { constructor() Base(2) public { } } // ---- -// TypeError: (74-81): Wrong argument count for constructor call: 1 arguments given but expected 2. +// TypeError: (74-81): Wrong argument count for constructor call: 1 arguments given but expected 2. Remove parentheses if you do not want to provide arguments here. // TypeError: (130-137): Wrong argument count for modifier invocation: 1 arguments given but expected 2. diff --git a/test/libsolidity/syntaxTests/inlineAssembly/assignment_from_contract.sol b/test/libsolidity/syntaxTests/inlineAssembly/assignment_from_contract.sol new file mode 100644 index 00000000..a87a3e66 --- /dev/null +++ b/test/libsolidity/syntaxTests/inlineAssembly/assignment_from_contract.sol @@ -0,0 +1,9 @@ +contract C { + function f() public pure { + assembly { + let x := C + } + } +} +// ---- +// TypeError: (72-73): Expected a library. diff --git a/test/libsolidity/syntaxTests/inlineAssembly/assignment_from_functiontype.sol b/test/libsolidity/syntaxTests/inlineAssembly/assignment_from_functiontype.sol new file mode 100644 index 00000000..ecda3e99 --- /dev/null +++ b/test/libsolidity/syntaxTests/inlineAssembly/assignment_from_functiontype.sol @@ -0,0 +1,7 @@ +contract C { + function f() public pure { + assembly { + let x := f + } + } +} diff --git a/test/libsolidity/syntaxTests/inlineAssembly/assignment_from_library.sol b/test/libsolidity/syntaxTests/inlineAssembly/assignment_from_library.sol new file mode 100644 index 00000000..3c551c18 --- /dev/null +++ b/test/libsolidity/syntaxTests/inlineAssembly/assignment_from_library.sol @@ -0,0 +1,10 @@ +library L { +} + +contract C { + function f() public pure { + assembly { + let x := L + } + } +} diff --git a/test/libsolidity/syntaxTests/inlineAssembly/assignment_from_super.sol b/test/libsolidity/syntaxTests/inlineAssembly/assignment_from_super.sol new file mode 100644 index 00000000..bd5562d5 --- /dev/null +++ b/test/libsolidity/syntaxTests/inlineAssembly/assignment_from_super.sol @@ -0,0 +1,9 @@ +contract C { + function f() public pure { + assembly { + let x := super + } + } +} +// ---- +// DeclarationError: (72-77): Identifier not found. diff --git a/test/libsolidity/syntaxTests/inlineAssembly/assignment_to_special.sol b/test/libsolidity/syntaxTests/inlineAssembly/assignment_to_special.sol new file mode 100644 index 00000000..db28e507 --- /dev/null +++ b/test/libsolidity/syntaxTests/inlineAssembly/assignment_to_special.sol @@ -0,0 +1,13 @@ +contract C { + function f() public { + assembly { + super := 1 + f := 1 + C := 1 + } + } +} +// ---- +// TypeError: (58-63): Only local variables can be assigned to in inline assembly. +// TypeError: (75-76): Only local variables can be assigned to in inline assembly. +// TypeError: (88-89): Only local variables can be assigned to in inline assembly. diff --git a/test/libsolidity/syntaxTests/inlineAssembly/function_call_invalid_argument_count.sol b/test/libsolidity/syntaxTests/inlineAssembly/function_call_invalid_argument_count.sol new file mode 100644 index 00000000..ac1f541e --- /dev/null +++ b/test/libsolidity/syntaxTests/inlineAssembly/function_call_invalid_argument_count.sol @@ -0,0 +1,16 @@ +contract C { + function f() public pure { + assembly { + function f(a) {} + + f() + f(1) + f(1, 2) + } + } +} +// ---- +// TypeError: (87-88): Expected 1 arguments but got 0. +// SyntaxError: (87-90): Top-level expressions are not supposed to return values (this expression returns -1 values). Use ``pop()`` or assign them. +// TypeError: (108-109): Expected 1 arguments but got 2. +// SyntaxError: (108-115): Top-level expressions are not supposed to return values (this expression returns 1 value). Use ``pop()`` or assign them. diff --git a/test/libsolidity/syntaxTests/inlineAssembly/function_call_not_found.sol b/test/libsolidity/syntaxTests/inlineAssembly/function_call_not_found.sol new file mode 100644 index 00000000..57534bd6 --- /dev/null +++ b/test/libsolidity/syntaxTests/inlineAssembly/function_call_not_found.sol @@ -0,0 +1,9 @@ +contract C { + function f() public pure { + assembly { + k() + } + } +} +// ---- +// DeclarationError: (63-64): Function not found. diff --git a/test/libsolidity/syntaxTests/inlineAssembly/function_call_to_label.sol b/test/libsolidity/syntaxTests/inlineAssembly/function_call_to_label.sol new file mode 100644 index 00000000..150fb938 --- /dev/null +++ b/test/libsolidity/syntaxTests/inlineAssembly/function_call_to_label.sol @@ -0,0 +1,13 @@ +contract C { + function f() public pure { + assembly { + l: + + l() + } + } +} +// ---- +// SyntaxError: (63-64): The use of labels is disallowed. Please use "if", "switch", "for" or function calls instead. +// SyntaxError: (63-64): Jump instructions and labels are low-level EVM features that can lead to incorrect stack access. Because of that they are discouraged. Please consider using "switch", "if" or "for" statements instead. +// TypeError: (73-74): Attempt to call label instead of function. diff --git a/test/libsolidity/syntaxTests/inlineAssembly/function_call_to_variable.sol b/test/libsolidity/syntaxTests/inlineAssembly/function_call_to_variable.sol new file mode 100644 index 00000000..c0071855 --- /dev/null +++ b/test/libsolidity/syntaxTests/inlineAssembly/function_call_to_variable.sol @@ -0,0 +1,11 @@ +contract C { + function f() public pure { + assembly { + let x := 1 + + x() + } + } +} +// ---- +// TypeError: (81-82): Attempt to call variable instead of function. diff --git a/test/libsolidity/syntaxTests/inlineAssembly/function_without_call.sol b/test/libsolidity/syntaxTests/inlineAssembly/function_without_call.sol new file mode 100644 index 00000000..8557e2fa --- /dev/null +++ b/test/libsolidity/syntaxTests/inlineAssembly/function_without_call.sol @@ -0,0 +1,11 @@ +contract C { + function f() public pure { + assembly { + function k() {} + + k + } + } +} +// ---- +// TypeError: (86-87): Function k used without being called. diff --git a/test/libsolidity/syntaxTests/inlineAssembly/invalid/empty_fun_arg.sol b/test/libsolidity/syntaxTests/inlineAssembly/invalid/empty_fun_arg.sol new file mode 100644 index 00000000..e05277de --- /dev/null +++ b/test/libsolidity/syntaxTests/inlineAssembly/invalid/empty_fun_arg.sol @@ -0,0 +1,13 @@ +contract C { + function f() public pure { + assembly { + function f(a, b) {} + f() + f(1,) + f(,1) + } + } +} +// ---- +// ParserError: (113-114): Literal, identifier or instruction expected. +// ParserError: (113-114): Expected primary expression. diff --git a/test/libsolidity/syntaxTests/inlineAssembly/invalid/empty_function_name.sol b/test/libsolidity/syntaxTests/inlineAssembly/invalid/empty_function_name.sol new file mode 100644 index 00000000..17995b09 --- /dev/null +++ b/test/libsolidity/syntaxTests/inlineAssembly/invalid/empty_function_name.sol @@ -0,0 +1,10 @@ +contract C { + function f() public pure { + assembly { + function (a, b) {} + } + } +} +// ---- +// ParserError: (72-73): Expected identifier but got '(' +// ParserError: (79-80): Expected ';' but got '{' diff --git a/test/libsolidity/syntaxTests/inlineAssembly/invalid/invalid_number.sol b/test/libsolidity/syntaxTests/inlineAssembly/invalid/invalid_number.sol new file mode 100644 index 00000000..715913de --- /dev/null +++ b/test/libsolidity/syntaxTests/inlineAssembly/invalid/invalid_number.sol @@ -0,0 +1,10 @@ +contract C { + function f() public pure { + assembly { + let x := 0100 + } + } +} +// ---- +// ParserError: (72-73): Literal, identifier or instruction expected. +// ParserError: (72-73): Expected primary expression. diff --git a/test/libsolidity/syntaxTests/inlineAssembly/invalid/missing_variable_in_assign.sol b/test/libsolidity/syntaxTests/inlineAssembly/invalid/missing_variable_in_assign.sol new file mode 100644 index 00000000..c8984333 --- /dev/null +++ b/test/libsolidity/syntaxTests/inlineAssembly/invalid/missing_variable_in_assign.sol @@ -0,0 +1,11 @@ +contract C { + function f() public pure { + assembly { + let x := mload(0) + := 1 + } + } +} +// ---- +// ParserError: (87-88): Literal, identifier or instruction expected. +// ParserError: (87-88): Expected primary expression. diff --git a/test/libsolidity/syntaxTests/inlineAssembly/overloaded_reference.sol b/test/libsolidity/syntaxTests/inlineAssembly/overloaded_reference.sol new file mode 100644 index 00000000..d1bcc946 --- /dev/null +++ b/test/libsolidity/syntaxTests/inlineAssembly/overloaded_reference.sol @@ -0,0 +1,11 @@ +contract C { + function f() pure public {} + function f(address) pure public {} + function g() pure public { + assembly { + let x := f + } + } +} +// ---- +// DeclarationError: (155-156): Multiple matching identifiers. Resolving overloaded identifiers is not supported. diff --git a/test/libsolidity/syntaxTests/inlineAssembly/storage_reference.sol b/test/libsolidity/syntaxTests/inlineAssembly/storage_reference.sol new file mode 100644 index 00000000..07113093 --- /dev/null +++ b/test/libsolidity/syntaxTests/inlineAssembly/storage_reference.sol @@ -0,0 +1,11 @@ +contract C { + uint[] x; + function() external { + uint[] storage y = x; + assembly { + pop(y) + } + } +} +// ---- +// TypeError: (118-119): You have to use the _slot or _offset suffix to access storage reference variables. diff --git a/test/libsolidity/syntaxTests/inlineAssembly/storage_reference_assignment.sol b/test/libsolidity/syntaxTests/inlineAssembly/storage_reference_assignment.sol new file mode 100644 index 00000000..dc742142 --- /dev/null +++ b/test/libsolidity/syntaxTests/inlineAssembly/storage_reference_assignment.sol @@ -0,0 +1,13 @@ +contract C { + uint[] x; + function() external { + uint[] storage y = x; + assembly { + y_slot := 1 + y_offset := 2 + } + } +} +// ---- +// TypeError: (114-120): Storage variables cannot be assigned to. +// TypeError: (138-146): Storage variables cannot be assigned to. diff --git a/test/libsolidity/syntaxTests/inlineAssembly/storage_reference_empty_offset.sol b/test/libsolidity/syntaxTests/inlineAssembly/storage_reference_empty_offset.sol new file mode 100644 index 00000000..ec23a263 --- /dev/null +++ b/test/libsolidity/syntaxTests/inlineAssembly/storage_reference_empty_offset.sol @@ -0,0 +1,9 @@ +contract C { + function f() public pure { + assembly { + _offset + } + } +} +// ---- +// DeclarationError: (75-82): In variable names _slot and _offset can only be used as a suffix. diff --git a/test/libsolidity/syntaxTests/inlineAssembly/storage_reference_empty_slot.sol b/test/libsolidity/syntaxTests/inlineAssembly/storage_reference_empty_slot.sol new file mode 100644 index 00000000..d493a68a --- /dev/null +++ b/test/libsolidity/syntaxTests/inlineAssembly/storage_reference_empty_slot.sol @@ -0,0 +1,9 @@ +contract C { + function f() public pure { + assembly { + _slot + } + } +} +// ---- +// DeclarationError: (75-80): In variable names _slot and _offset can only be used as a suffix. diff --git a/test/libsolidity/syntaxTests/inlineAssembly/storage_reference_fine.sol b/test/libsolidity/syntaxTests/inlineAssembly/storage_reference_fine.sol new file mode 100644 index 00000000..b01a7705 --- /dev/null +++ b/test/libsolidity/syntaxTests/inlineAssembly/storage_reference_fine.sol @@ -0,0 +1,11 @@ +contract C { + uint[] x; + function() external { + uint[] storage y = x; + assembly { + pop(y_slot) + pop(y_offset) + } + } +} +// ---- diff --git a/test/libsolidity/syntaxTests/inlineAssembly/storage_reference_on_function.sol b/test/libsolidity/syntaxTests/inlineAssembly/storage_reference_on_function.sol new file mode 100644 index 00000000..9165654f --- /dev/null +++ b/test/libsolidity/syntaxTests/inlineAssembly/storage_reference_on_function.sol @@ -0,0 +1,9 @@ +contract C { + function f() pure public { + assembly { + let x := f_slot + } + } +} +// ---- +// TypeError: (84-90): The suffixes _offset and _slot can only be used on storage variables. diff --git a/test/libsolidity/syntaxTests/inlineAssembly/storage_reference_on_memory.sol b/test/libsolidity/syntaxTests/inlineAssembly/storage_reference_on_memory.sol new file mode 100644 index 00000000..704b712d --- /dev/null +++ b/test/libsolidity/syntaxTests/inlineAssembly/storage_reference_on_memory.sol @@ -0,0 +1,13 @@ +contract C { + uint[] x; + function() external { + uint[] memory y = x; + assembly { + pop(y_slot) + pop(y_offset) + } + } +} +// ---- +// TypeError: (117-123): The suffixes _offset and _slot can only be used on storage variables. +// TypeError: (141-149): The suffixes _offset and _slot can only be used on storage variables. diff --git a/test/libsolidity/syntaxTests/memberLookup/failed_function_lookup.sol b/test/libsolidity/syntaxTests/memberLookup/failed_function_lookup.sol index c23992e9..119df5d3 100644 --- a/test/libsolidity/syntaxTests/memberLookup/failed_function_lookup.sol +++ b/test/libsolidity/syntaxTests/memberLookup/failed_function_lookup.sol @@ -1,7 +1,7 @@ contract C { - function f(uint, uint) {} - function f(uint) {} - function g() { f(1, 2, 3); } + function f(uint, uint) public {} + function f(uint) public {} + function g() public { f(1, 2, 3); } } // ---- -// TypeError: (80-81): No matching declaration found after argument-dependent lookup. +// TypeError: (101-102): No matching declaration found after argument-dependent lookup. diff --git a/test/libsolidity/syntaxTests/memberLookup/failed_function_lookup_in_library.sol b/test/libsolidity/syntaxTests/memberLookup/failed_function_lookup_in_library.sol index 310c4a10..d8f2eadd 100644 --- a/test/libsolidity/syntaxTests/memberLookup/failed_function_lookup_in_library.sol +++ b/test/libsolidity/syntaxTests/memberLookup/failed_function_lookup_in_library.sol @@ -1,9 +1,9 @@ library L { - function f(uint, uint) {} - function f(uint) {} + function f(uint, uint) public {} + function f(uint) public {} } contract C { - function g() { L.f(1, 2, 3); } + function g() public { L.f(1, 2, 3); } } // ---- -// TypeError: (94-97): Member "f" not found or not visible after argument-dependent lookup in type(library L) +// TypeError: (115-118): Member "f" not found or not visible after argument-dependent lookup in type(library L). diff --git a/test/libsolidity/syntaxTests/memberLookup/msg_value_modifier_payable.sol b/test/libsolidity/syntaxTests/memberLookup/msg_value_modifier_payable.sol new file mode 100644 index 00000000..6e93626f --- /dev/null +++ b/test/libsolidity/syntaxTests/memberLookup/msg_value_modifier_payable.sol @@ -0,0 +1,4 @@ +contract C { + modifier costs(uint _amount) { require(msg.value >= _amount); _; } + function f() costs(1 ether) public payable {} +} diff --git a/test/libsolidity/syntaxTests/memberLookup/msg_value_modifier_pure.sol b/test/libsolidity/syntaxTests/memberLookup/msg_value_modifier_pure.sol new file mode 100644 index 00000000..398c127d --- /dev/null +++ b/test/libsolidity/syntaxTests/memberLookup/msg_value_modifier_pure.sol @@ -0,0 +1,6 @@ +contract C { + modifier costs(uint _amount) { require(msg.value >= _amount); _; } + function f() costs(1 ether) public pure {} +} +// ---- +// TypeError: (101-115): Function declared as pure, but this expression (potentially) reads from the environment or state and thus requires "view". diff --git a/test/libsolidity/syntaxTests/memberLookup/msg_value_modifier_view.sol b/test/libsolidity/syntaxTests/memberLookup/msg_value_modifier_view.sol new file mode 100644 index 00000000..8430c5c3 --- /dev/null +++ b/test/libsolidity/syntaxTests/memberLookup/msg_value_modifier_view.sol @@ -0,0 +1,6 @@ +contract C { + modifier costs(uint _amount) { require(msg.value >= _amount); _; } + function f() costs(1 ether) public view {} +} +// ---- +// TypeError: (101-115): This modifier uses "msg.value" and thus the function has to be payable or internal. diff --git a/test/libsolidity/syntaxTests/missing_state_variable.sol b/test/libsolidity/syntaxTests/missing_state_variable.sol index 02082a45..8b97220c 100644 --- a/test/libsolidity/syntaxTests/missing_state_variable.sol +++ b/test/libsolidity/syntaxTests/missing_state_variable.sol @@ -4,4 +4,4 @@ contract Scope { } } // ---- -// TypeError: (101-115): Member "stateVar" not found or not visible after argument-dependent lookup in type(contract Scope) +// TypeError: (101-115): Member "stateVar" not found or not visible after argument-dependent lookup in type(contract Scope). diff --git a/test/libsolidity/syntaxTests/modifiers/base_constructor_double_invocation.sol b/test/libsolidity/syntaxTests/modifiers/base_constructor_double_invocation.sol index bdbab5d8..49d0d7bf 100644 --- a/test/libsolidity/syntaxTests/modifiers/base_constructor_double_invocation.sol +++ b/test/libsolidity/syntaxTests/modifiers/base_constructor_double_invocation.sol @@ -3,5 +3,5 @@ contract B is C { constructor() C(2) C(2) public {} } // ---- -// Warning: (81-85): Base constructor arguments given twice. +// DeclarationError: (81-85): Base constructor arguments given twice. // DeclarationError: (86-90): Base constructor already provided. diff --git a/test/libsolidity/syntaxTests/modifiers/function_modifier_invocation_local_variables.sol b/test/libsolidity/syntaxTests/modifiers/function_modifier_invocation_local_variables.sol index 00031924..76bb6fc0 100644 --- a/test/libsolidity/syntaxTests/modifiers/function_modifier_invocation_local_variables.sol +++ b/test/libsolidity/syntaxTests/modifiers/function_modifier_invocation_local_variables.sol @@ -2,3 +2,5 @@ contract B { function f() mod(x) pure public { uint x = 7; } modifier mod(uint a) { if (a > 0) _; } } +// ---- +// DeclarationError: (34-35): Undeclared identifier. diff --git a/test/libsolidity/syntaxTests/modifiers/function_modifier_invocation_local_variables050.sol b/test/libsolidity/syntaxTests/modifiers/function_modifier_invocation_local_variables050.sol deleted file mode 100644 index c19ccf2c..00000000 --- a/test/libsolidity/syntaxTests/modifiers/function_modifier_invocation_local_variables050.sol +++ /dev/null @@ -1,7 +0,0 @@ -pragma experimental "v0.5.0"; -contract B { - function f() mod(x) pure public { uint x = 7; } - modifier mod(uint a) { if (a > 0) _; } -} -// ---- -// DeclarationError: (64-65): Undeclared identifier. diff --git a/test/libsolidity/syntaxTests/modifiers/modifiers_on_abstract_functions_050.sol b/test/libsolidity/syntaxTests/modifiers/modifiers_on_abstract_functions_050.sol deleted file mode 100644 index af1babbc..00000000 --- a/test/libsolidity/syntaxTests/modifiers/modifiers_on_abstract_functions_050.sol +++ /dev/null @@ -1,10 +0,0 @@ -pragma experimental "v0.5.0"; -contract C -{ - modifier only_owner() { _; } - function foo() only_owner public; - function bar() public only_owner; -} -// ---- -// SyntaxError: (80-113): Functions without implementation cannot have modifiers. -// SyntaxError: (118-151): Functions without implementation cannot have modifiers. diff --git a/test/libsolidity/syntaxTests/modifiers/modifiers_on_abstract_functions_no_parser_error.sol b/test/libsolidity/syntaxTests/modifiers/modifiers_on_abstract_functions_no_parser_error.sol index e18c5cf9..2e86fcc1 100644 --- a/test/libsolidity/syntaxTests/modifiers/modifiers_on_abstract_functions_no_parser_error.sol +++ b/test/libsolidity/syntaxTests/modifiers/modifiers_on_abstract_functions_no_parser_error.sol @@ -9,5 +9,5 @@ contract C function bar() public only_owner; } // ---- -// Warning: (203-236): Modifiers of functions without implementation are ignored. -// Warning: (241-274): Modifiers of functions without implementation are ignored. +// SyntaxError: (203-236): Functions without implementation cannot have modifiers. +// SyntaxError: (241-274): Functions without implementation cannot have modifiers. diff --git a/test/libsolidity/syntaxTests/more_than_256_declarationerrors.sol b/test/libsolidity/syntaxTests/more_than_256_declarationerrors.sol index 2d75f29b..307b728d 100644 --- a/test/libsolidity/syntaxTests/more_than_256_declarationerrors.sol +++ b/test/libsolidity/syntaxTests/more_than_256_declarationerrors.sol @@ -1,5 +1,5 @@ contract C { - function f() { + function f() public { b = 5; b = 5; b = 5; @@ -265,260 +265,260 @@ contract C { } } // ---- -// DeclarationError: (34-35): Undeclared identifier. -// DeclarationError: (45-46): Undeclared identifier. -// DeclarationError: (56-57): Undeclared identifier. -// DeclarationError: (67-68): Undeclared identifier. -// DeclarationError: (78-79): Undeclared identifier. -// DeclarationError: (89-90): Undeclared identifier. -// DeclarationError: (100-101): Undeclared identifier. -// DeclarationError: (111-112): Undeclared identifier. -// DeclarationError: (122-123): Undeclared identifier. -// DeclarationError: (133-134): Undeclared identifier. -// DeclarationError: (144-145): Undeclared identifier. -// DeclarationError: (155-156): Undeclared identifier. -// DeclarationError: (166-167): Undeclared identifier. -// DeclarationError: (177-178): Undeclared identifier. -// DeclarationError: (188-189): Undeclared identifier. -// DeclarationError: (199-200): Undeclared identifier. -// DeclarationError: (210-211): Undeclared identifier. -// DeclarationError: (221-222): Undeclared identifier. -// DeclarationError: (232-233): Undeclared identifier. -// DeclarationError: (243-244): Undeclared identifier. -// DeclarationError: (254-255): Undeclared identifier. -// DeclarationError: (265-266): Undeclared identifier. -// DeclarationError: (276-277): Undeclared identifier. -// DeclarationError: (287-288): Undeclared identifier. -// DeclarationError: (298-299): Undeclared identifier. -// DeclarationError: (309-310): Undeclared identifier. -// DeclarationError: (320-321): Undeclared identifier. -// DeclarationError: (331-332): Undeclared identifier. -// DeclarationError: (342-343): Undeclared identifier. -// DeclarationError: (353-354): Undeclared identifier. -// DeclarationError: (364-365): Undeclared identifier. -// DeclarationError: (375-376): Undeclared identifier. -// DeclarationError: (386-387): Undeclared identifier. -// DeclarationError: (397-398): Undeclared identifier. -// DeclarationError: (408-409): Undeclared identifier. -// DeclarationError: (419-420): Undeclared identifier. -// DeclarationError: (430-431): Undeclared identifier. -// DeclarationError: (441-442): Undeclared identifier. -// DeclarationError: (452-453): Undeclared identifier. -// DeclarationError: (463-464): Undeclared identifier. -// DeclarationError: (474-475): Undeclared identifier. -// DeclarationError: (485-486): Undeclared identifier. -// DeclarationError: (496-497): Undeclared identifier. -// DeclarationError: (507-508): Undeclared identifier. -// DeclarationError: (518-519): Undeclared identifier. -// DeclarationError: (529-530): Undeclared identifier. -// DeclarationError: (540-541): Undeclared identifier. -// DeclarationError: (551-552): Undeclared identifier. -// DeclarationError: (562-563): Undeclared identifier. -// DeclarationError: (573-574): Undeclared identifier. -// DeclarationError: (584-585): Undeclared identifier. -// DeclarationError: (595-596): Undeclared identifier. -// DeclarationError: (606-607): Undeclared identifier. -// DeclarationError: (617-618): Undeclared identifier. -// DeclarationError: (628-629): Undeclared identifier. -// DeclarationError: (639-640): Undeclared identifier. -// DeclarationError: (650-651): Undeclared identifier. -// DeclarationError: (661-662): Undeclared identifier. -// DeclarationError: (672-673): Undeclared identifier. -// DeclarationError: (683-684): Undeclared identifier. -// DeclarationError: (694-695): Undeclared identifier. -// DeclarationError: (705-706): Undeclared identifier. -// DeclarationError: (716-717): Undeclared identifier. -// DeclarationError: (727-728): Undeclared identifier. -// DeclarationError: (738-739): Undeclared identifier. -// DeclarationError: (749-750): Undeclared identifier. -// DeclarationError: (760-761): Undeclared identifier. -// DeclarationError: (771-772): Undeclared identifier. -// DeclarationError: (782-783): Undeclared identifier. -// DeclarationError: (793-794): Undeclared identifier. -// DeclarationError: (804-805): Undeclared identifier. -// DeclarationError: (815-816): Undeclared identifier. -// DeclarationError: (826-827): Undeclared identifier. -// DeclarationError: (837-838): Undeclared identifier. -// DeclarationError: (848-849): Undeclared identifier. -// DeclarationError: (859-860): Undeclared identifier. -// DeclarationError: (870-871): Undeclared identifier. -// DeclarationError: (881-882): Undeclared identifier. -// DeclarationError: (892-893): Undeclared identifier. -// DeclarationError: (903-904): Undeclared identifier. -// DeclarationError: (914-915): Undeclared identifier. -// DeclarationError: (925-926): Undeclared identifier. -// DeclarationError: (936-937): Undeclared identifier. -// DeclarationError: (947-948): Undeclared identifier. -// DeclarationError: (958-959): Undeclared identifier. -// DeclarationError: (969-970): Undeclared identifier. -// DeclarationError: (980-981): Undeclared identifier. -// DeclarationError: (991-992): Undeclared identifier. -// DeclarationError: (1002-1003): Undeclared identifier. -// DeclarationError: (1013-1014): Undeclared identifier. -// DeclarationError: (1024-1025): Undeclared identifier. -// DeclarationError: (1035-1036): Undeclared identifier. -// DeclarationError: (1046-1047): Undeclared identifier. -// DeclarationError: (1057-1058): Undeclared identifier. -// DeclarationError: (1068-1069): Undeclared identifier. -// DeclarationError: (1079-1080): Undeclared identifier. -// DeclarationError: (1090-1091): Undeclared identifier. -// DeclarationError: (1101-1102): Undeclared identifier. -// DeclarationError: (1112-1113): Undeclared identifier. -// DeclarationError: (1123-1124): Undeclared identifier. -// DeclarationError: (1134-1135): Undeclared identifier. -// DeclarationError: (1145-1146): Undeclared identifier. -// DeclarationError: (1156-1157): Undeclared identifier. -// DeclarationError: (1167-1168): Undeclared identifier. -// DeclarationError: (1178-1179): Undeclared identifier. -// DeclarationError: (1189-1190): Undeclared identifier. -// DeclarationError: (1200-1201): Undeclared identifier. -// DeclarationError: (1211-1212): Undeclared identifier. -// DeclarationError: (1222-1223): Undeclared identifier. -// DeclarationError: (1233-1234): Undeclared identifier. -// DeclarationError: (1244-1245): Undeclared identifier. -// DeclarationError: (1255-1256): Undeclared identifier. -// DeclarationError: (1266-1267): Undeclared identifier. -// DeclarationError: (1277-1278): Undeclared identifier. -// DeclarationError: (1288-1289): Undeclared identifier. -// DeclarationError: (1299-1300): Undeclared identifier. -// DeclarationError: (1310-1311): Undeclared identifier. -// DeclarationError: (1321-1322): Undeclared identifier. -// DeclarationError: (1332-1333): Undeclared identifier. -// DeclarationError: (1343-1344): Undeclared identifier. -// DeclarationError: (1354-1355): Undeclared identifier. -// DeclarationError: (1365-1366): Undeclared identifier. -// DeclarationError: (1376-1377): Undeclared identifier. -// DeclarationError: (1387-1388): Undeclared identifier. -// DeclarationError: (1398-1399): Undeclared identifier. -// DeclarationError: (1409-1410): Undeclared identifier. -// DeclarationError: (1420-1421): Undeclared identifier. -// DeclarationError: (1431-1432): Undeclared identifier. -// DeclarationError: (1442-1443): Undeclared identifier. -// DeclarationError: (1453-1454): Undeclared identifier. -// DeclarationError: (1464-1465): Undeclared identifier. -// DeclarationError: (1475-1476): Undeclared identifier. -// DeclarationError: (1486-1487): Undeclared identifier. -// DeclarationError: (1497-1498): Undeclared identifier. -// DeclarationError: (1508-1509): Undeclared identifier. -// DeclarationError: (1519-1520): Undeclared identifier. -// DeclarationError: (1530-1531): Undeclared identifier. -// DeclarationError: (1541-1542): Undeclared identifier. -// DeclarationError: (1552-1553): Undeclared identifier. -// DeclarationError: (1563-1564): Undeclared identifier. -// DeclarationError: (1574-1575): Undeclared identifier. -// DeclarationError: (1585-1586): Undeclared identifier. -// DeclarationError: (1596-1597): Undeclared identifier. -// DeclarationError: (1607-1608): Undeclared identifier. -// DeclarationError: (1618-1619): Undeclared identifier. -// DeclarationError: (1629-1630): Undeclared identifier. -// DeclarationError: (1640-1641): Undeclared identifier. -// DeclarationError: (1651-1652): Undeclared identifier. -// DeclarationError: (1662-1663): Undeclared identifier. -// DeclarationError: (1673-1674): Undeclared identifier. -// DeclarationError: (1684-1685): Undeclared identifier. -// DeclarationError: (1695-1696): Undeclared identifier. -// DeclarationError: (1706-1707): Undeclared identifier. -// DeclarationError: (1717-1718): Undeclared identifier. -// DeclarationError: (1728-1729): Undeclared identifier. -// DeclarationError: (1739-1740): Undeclared identifier. -// DeclarationError: (1750-1751): Undeclared identifier. -// DeclarationError: (1761-1762): Undeclared identifier. -// DeclarationError: (1772-1773): Undeclared identifier. -// DeclarationError: (1783-1784): Undeclared identifier. -// DeclarationError: (1794-1795): Undeclared identifier. -// DeclarationError: (1805-1806): Undeclared identifier. -// DeclarationError: (1816-1817): Undeclared identifier. -// DeclarationError: (1827-1828): Undeclared identifier. -// DeclarationError: (1838-1839): Undeclared identifier. -// DeclarationError: (1849-1850): Undeclared identifier. -// DeclarationError: (1860-1861): Undeclared identifier. -// DeclarationError: (1871-1872): Undeclared identifier. -// DeclarationError: (1882-1883): Undeclared identifier. -// DeclarationError: (1893-1894): Undeclared identifier. -// DeclarationError: (1904-1905): Undeclared identifier. -// DeclarationError: (1915-1916): Undeclared identifier. -// DeclarationError: (1926-1927): Undeclared identifier. -// DeclarationError: (1937-1938): Undeclared identifier. -// DeclarationError: (1948-1949): Undeclared identifier. -// DeclarationError: (1959-1960): Undeclared identifier. -// DeclarationError: (1970-1971): Undeclared identifier. -// DeclarationError: (1981-1982): Undeclared identifier. -// DeclarationError: (1992-1993): Undeclared identifier. -// DeclarationError: (2003-2004): Undeclared identifier. -// DeclarationError: (2014-2015): Undeclared identifier. -// DeclarationError: (2025-2026): Undeclared identifier. -// DeclarationError: (2036-2037): Undeclared identifier. -// DeclarationError: (2047-2048): Undeclared identifier. -// DeclarationError: (2058-2059): Undeclared identifier. -// DeclarationError: (2069-2070): Undeclared identifier. -// DeclarationError: (2080-2081): Undeclared identifier. -// DeclarationError: (2091-2092): Undeclared identifier. -// DeclarationError: (2102-2103): Undeclared identifier. -// DeclarationError: (2113-2114): Undeclared identifier. -// DeclarationError: (2124-2125): Undeclared identifier. -// DeclarationError: (2135-2136): Undeclared identifier. -// DeclarationError: (2146-2147): Undeclared identifier. -// DeclarationError: (2157-2158): Undeclared identifier. -// DeclarationError: (2168-2169): Undeclared identifier. -// DeclarationError: (2179-2180): Undeclared identifier. -// DeclarationError: (2190-2191): Undeclared identifier. -// DeclarationError: (2201-2202): Undeclared identifier. -// DeclarationError: (2212-2213): Undeclared identifier. -// DeclarationError: (2223-2224): Undeclared identifier. -// DeclarationError: (2234-2235): Undeclared identifier. -// DeclarationError: (2245-2246): Undeclared identifier. -// DeclarationError: (2256-2257): Undeclared identifier. -// DeclarationError: (2267-2268): Undeclared identifier. -// DeclarationError: (2278-2279): Undeclared identifier. -// DeclarationError: (2289-2290): Undeclared identifier. -// DeclarationError: (2300-2301): Undeclared identifier. -// DeclarationError: (2311-2312): Undeclared identifier. -// DeclarationError: (2322-2323): Undeclared identifier. -// DeclarationError: (2333-2334): Undeclared identifier. -// DeclarationError: (2344-2345): Undeclared identifier. -// DeclarationError: (2355-2356): Undeclared identifier. -// DeclarationError: (2366-2367): Undeclared identifier. -// DeclarationError: (2377-2378): Undeclared identifier. -// DeclarationError: (2388-2389): Undeclared identifier. -// DeclarationError: (2399-2400): Undeclared identifier. -// DeclarationError: (2410-2411): Undeclared identifier. -// DeclarationError: (2421-2422): Undeclared identifier. -// DeclarationError: (2432-2433): Undeclared identifier. -// DeclarationError: (2443-2444): Undeclared identifier. -// DeclarationError: (2454-2455): Undeclared identifier. -// DeclarationError: (2465-2466): Undeclared identifier. -// DeclarationError: (2476-2477): Undeclared identifier. -// DeclarationError: (2487-2488): Undeclared identifier. -// DeclarationError: (2498-2499): Undeclared identifier. -// DeclarationError: (2509-2510): Undeclared identifier. -// DeclarationError: (2520-2521): Undeclared identifier. -// DeclarationError: (2531-2532): Undeclared identifier. -// DeclarationError: (2542-2543): Undeclared identifier. -// DeclarationError: (2553-2554): Undeclared identifier. -// DeclarationError: (2564-2565): Undeclared identifier. -// DeclarationError: (2575-2576): Undeclared identifier. -// DeclarationError: (2586-2587): Undeclared identifier. -// DeclarationError: (2597-2598): Undeclared identifier. -// DeclarationError: (2608-2609): Undeclared identifier. -// DeclarationError: (2619-2620): Undeclared identifier. -// DeclarationError: (2630-2631): Undeclared identifier. -// DeclarationError: (2641-2642): Undeclared identifier. -// DeclarationError: (2652-2653): Undeclared identifier. -// DeclarationError: (2663-2664): Undeclared identifier. -// DeclarationError: (2674-2675): Undeclared identifier. -// DeclarationError: (2685-2686): Undeclared identifier. -// DeclarationError: (2696-2697): Undeclared identifier. -// DeclarationError: (2707-2708): Undeclared identifier. -// DeclarationError: (2718-2719): Undeclared identifier. -// DeclarationError: (2729-2730): Undeclared identifier. -// DeclarationError: (2740-2741): Undeclared identifier. -// DeclarationError: (2751-2752): Undeclared identifier. -// DeclarationError: (2762-2763): Undeclared identifier. -// DeclarationError: (2773-2774): Undeclared identifier. -// DeclarationError: (2784-2785): Undeclared identifier. -// DeclarationError: (2795-2796): Undeclared identifier. -// DeclarationError: (2806-2807): Undeclared identifier. -// DeclarationError: (2817-2818): Undeclared identifier. -// DeclarationError: (2828-2829): Undeclared identifier. -// DeclarationError: (2839-2840): Undeclared identifier. +// DeclarationError: (41-42): Undeclared identifier. +// DeclarationError: (52-53): Undeclared identifier. +// DeclarationError: (63-64): Undeclared identifier. +// DeclarationError: (74-75): Undeclared identifier. +// DeclarationError: (85-86): Undeclared identifier. +// DeclarationError: (96-97): Undeclared identifier. +// DeclarationError: (107-108): Undeclared identifier. +// DeclarationError: (118-119): Undeclared identifier. +// DeclarationError: (129-130): Undeclared identifier. +// DeclarationError: (140-141): Undeclared identifier. +// DeclarationError: (151-152): Undeclared identifier. +// DeclarationError: (162-163): Undeclared identifier. +// DeclarationError: (173-174): Undeclared identifier. +// DeclarationError: (184-185): Undeclared identifier. +// DeclarationError: (195-196): Undeclared identifier. +// DeclarationError: (206-207): Undeclared identifier. +// DeclarationError: (217-218): Undeclared identifier. +// DeclarationError: (228-229): Undeclared identifier. +// DeclarationError: (239-240): Undeclared identifier. +// DeclarationError: (250-251): Undeclared identifier. +// DeclarationError: (261-262): Undeclared identifier. +// DeclarationError: (272-273): Undeclared identifier. +// DeclarationError: (283-284): Undeclared identifier. +// DeclarationError: (294-295): Undeclared identifier. +// DeclarationError: (305-306): Undeclared identifier. +// DeclarationError: (316-317): Undeclared identifier. +// DeclarationError: (327-328): Undeclared identifier. +// DeclarationError: (338-339): Undeclared identifier. +// DeclarationError: (349-350): Undeclared identifier. +// DeclarationError: (360-361): Undeclared identifier. +// DeclarationError: (371-372): Undeclared identifier. +// DeclarationError: (382-383): Undeclared identifier. +// DeclarationError: (393-394): Undeclared identifier. +// DeclarationError: (404-405): Undeclared identifier. +// DeclarationError: (415-416): Undeclared identifier. +// DeclarationError: (426-427): Undeclared identifier. +// DeclarationError: (437-438): Undeclared identifier. +// DeclarationError: (448-449): Undeclared identifier. +// DeclarationError: (459-460): Undeclared identifier. +// DeclarationError: (470-471): Undeclared identifier. +// DeclarationError: (481-482): Undeclared identifier. +// DeclarationError: (492-493): Undeclared identifier. +// DeclarationError: (503-504): Undeclared identifier. +// DeclarationError: (514-515): Undeclared identifier. +// DeclarationError: (525-526): Undeclared identifier. +// DeclarationError: (536-537): Undeclared identifier. +// DeclarationError: (547-548): Undeclared identifier. +// DeclarationError: (558-559): Undeclared identifier. +// DeclarationError: (569-570): Undeclared identifier. +// DeclarationError: (580-581): Undeclared identifier. +// DeclarationError: (591-592): Undeclared identifier. +// DeclarationError: (602-603): Undeclared identifier. +// DeclarationError: (613-614): Undeclared identifier. +// DeclarationError: (624-625): Undeclared identifier. +// DeclarationError: (635-636): Undeclared identifier. +// DeclarationError: (646-647): Undeclared identifier. +// DeclarationError: (657-658): Undeclared identifier. +// DeclarationError: (668-669): Undeclared identifier. +// DeclarationError: (679-680): Undeclared identifier. +// DeclarationError: (690-691): Undeclared identifier. +// DeclarationError: (701-702): Undeclared identifier. +// DeclarationError: (712-713): Undeclared identifier. +// DeclarationError: (723-724): Undeclared identifier. +// DeclarationError: (734-735): Undeclared identifier. +// DeclarationError: (745-746): Undeclared identifier. +// DeclarationError: (756-757): Undeclared identifier. +// DeclarationError: (767-768): Undeclared identifier. +// DeclarationError: (778-779): Undeclared identifier. +// DeclarationError: (789-790): Undeclared identifier. +// DeclarationError: (800-801): Undeclared identifier. +// DeclarationError: (811-812): Undeclared identifier. +// DeclarationError: (822-823): Undeclared identifier. +// DeclarationError: (833-834): Undeclared identifier. +// DeclarationError: (844-845): Undeclared identifier. +// DeclarationError: (855-856): Undeclared identifier. +// DeclarationError: (866-867): Undeclared identifier. +// DeclarationError: (877-878): Undeclared identifier. +// DeclarationError: (888-889): Undeclared identifier. +// DeclarationError: (899-900): Undeclared identifier. +// DeclarationError: (910-911): Undeclared identifier. +// DeclarationError: (921-922): Undeclared identifier. +// DeclarationError: (932-933): Undeclared identifier. +// DeclarationError: (943-944): Undeclared identifier. +// DeclarationError: (954-955): Undeclared identifier. +// DeclarationError: (965-966): Undeclared identifier. +// DeclarationError: (976-977): Undeclared identifier. +// DeclarationError: (987-988): Undeclared identifier. +// DeclarationError: (998-999): Undeclared identifier. +// DeclarationError: (1009-1010): Undeclared identifier. +// DeclarationError: (1020-1021): Undeclared identifier. +// DeclarationError: (1031-1032): Undeclared identifier. +// DeclarationError: (1042-1043): Undeclared identifier. +// DeclarationError: (1053-1054): Undeclared identifier. +// DeclarationError: (1064-1065): Undeclared identifier. +// DeclarationError: (1075-1076): Undeclared identifier. +// DeclarationError: (1086-1087): Undeclared identifier. +// DeclarationError: (1097-1098): Undeclared identifier. +// DeclarationError: (1108-1109): Undeclared identifier. +// DeclarationError: (1119-1120): Undeclared identifier. +// DeclarationError: (1130-1131): Undeclared identifier. +// DeclarationError: (1141-1142): Undeclared identifier. +// DeclarationError: (1152-1153): Undeclared identifier. +// DeclarationError: (1163-1164): Undeclared identifier. +// DeclarationError: (1174-1175): Undeclared identifier. +// DeclarationError: (1185-1186): Undeclared identifier. +// DeclarationError: (1196-1197): Undeclared identifier. +// DeclarationError: (1207-1208): Undeclared identifier. +// DeclarationError: (1218-1219): Undeclared identifier. +// DeclarationError: (1229-1230): Undeclared identifier. +// DeclarationError: (1240-1241): Undeclared identifier. +// DeclarationError: (1251-1252): Undeclared identifier. +// DeclarationError: (1262-1263): Undeclared identifier. +// DeclarationError: (1273-1274): Undeclared identifier. +// DeclarationError: (1284-1285): Undeclared identifier. +// DeclarationError: (1295-1296): Undeclared identifier. +// DeclarationError: (1306-1307): Undeclared identifier. +// DeclarationError: (1317-1318): Undeclared identifier. +// DeclarationError: (1328-1329): Undeclared identifier. +// DeclarationError: (1339-1340): Undeclared identifier. +// DeclarationError: (1350-1351): Undeclared identifier. +// DeclarationError: (1361-1362): Undeclared identifier. +// DeclarationError: (1372-1373): Undeclared identifier. +// DeclarationError: (1383-1384): Undeclared identifier. +// DeclarationError: (1394-1395): Undeclared identifier. +// DeclarationError: (1405-1406): Undeclared identifier. +// DeclarationError: (1416-1417): Undeclared identifier. +// DeclarationError: (1427-1428): Undeclared identifier. +// DeclarationError: (1438-1439): Undeclared identifier. +// DeclarationError: (1449-1450): Undeclared identifier. +// DeclarationError: (1460-1461): Undeclared identifier. +// DeclarationError: (1471-1472): Undeclared identifier. +// DeclarationError: (1482-1483): Undeclared identifier. +// DeclarationError: (1493-1494): Undeclared identifier. +// DeclarationError: (1504-1505): Undeclared identifier. +// DeclarationError: (1515-1516): Undeclared identifier. +// DeclarationError: (1526-1527): Undeclared identifier. +// DeclarationError: (1537-1538): Undeclared identifier. +// DeclarationError: (1548-1549): Undeclared identifier. +// DeclarationError: (1559-1560): Undeclared identifier. +// DeclarationError: (1570-1571): Undeclared identifier. +// DeclarationError: (1581-1582): Undeclared identifier. +// DeclarationError: (1592-1593): Undeclared identifier. +// DeclarationError: (1603-1604): Undeclared identifier. +// DeclarationError: (1614-1615): Undeclared identifier. +// DeclarationError: (1625-1626): Undeclared identifier. +// DeclarationError: (1636-1637): Undeclared identifier. +// DeclarationError: (1647-1648): Undeclared identifier. +// DeclarationError: (1658-1659): Undeclared identifier. +// DeclarationError: (1669-1670): Undeclared identifier. +// DeclarationError: (1680-1681): Undeclared identifier. +// DeclarationError: (1691-1692): Undeclared identifier. +// DeclarationError: (1702-1703): Undeclared identifier. +// DeclarationError: (1713-1714): Undeclared identifier. +// DeclarationError: (1724-1725): Undeclared identifier. +// DeclarationError: (1735-1736): Undeclared identifier. +// DeclarationError: (1746-1747): Undeclared identifier. +// DeclarationError: (1757-1758): Undeclared identifier. +// DeclarationError: (1768-1769): Undeclared identifier. +// DeclarationError: (1779-1780): Undeclared identifier. +// DeclarationError: (1790-1791): Undeclared identifier. +// DeclarationError: (1801-1802): Undeclared identifier. +// DeclarationError: (1812-1813): Undeclared identifier. +// DeclarationError: (1823-1824): Undeclared identifier. +// DeclarationError: (1834-1835): Undeclared identifier. +// DeclarationError: (1845-1846): Undeclared identifier. +// DeclarationError: (1856-1857): Undeclared identifier. +// DeclarationError: (1867-1868): Undeclared identifier. +// DeclarationError: (1878-1879): Undeclared identifier. +// DeclarationError: (1889-1890): Undeclared identifier. +// DeclarationError: (1900-1901): Undeclared identifier. +// DeclarationError: (1911-1912): Undeclared identifier. +// DeclarationError: (1922-1923): Undeclared identifier. +// DeclarationError: (1933-1934): Undeclared identifier. +// DeclarationError: (1944-1945): Undeclared identifier. +// DeclarationError: (1955-1956): Undeclared identifier. +// DeclarationError: (1966-1967): Undeclared identifier. +// DeclarationError: (1977-1978): Undeclared identifier. +// DeclarationError: (1988-1989): Undeclared identifier. +// DeclarationError: (1999-2000): Undeclared identifier. +// DeclarationError: (2010-2011): Undeclared identifier. +// DeclarationError: (2021-2022): Undeclared identifier. +// DeclarationError: (2032-2033): Undeclared identifier. +// DeclarationError: (2043-2044): Undeclared identifier. +// DeclarationError: (2054-2055): Undeclared identifier. +// DeclarationError: (2065-2066): Undeclared identifier. +// DeclarationError: (2076-2077): Undeclared identifier. +// DeclarationError: (2087-2088): Undeclared identifier. +// DeclarationError: (2098-2099): Undeclared identifier. +// DeclarationError: (2109-2110): Undeclared identifier. +// DeclarationError: (2120-2121): Undeclared identifier. +// DeclarationError: (2131-2132): Undeclared identifier. +// DeclarationError: (2142-2143): Undeclared identifier. +// DeclarationError: (2153-2154): Undeclared identifier. +// DeclarationError: (2164-2165): Undeclared identifier. +// DeclarationError: (2175-2176): Undeclared identifier. +// DeclarationError: (2186-2187): Undeclared identifier. +// DeclarationError: (2197-2198): Undeclared identifier. +// DeclarationError: (2208-2209): Undeclared identifier. +// DeclarationError: (2219-2220): Undeclared identifier. +// DeclarationError: (2230-2231): Undeclared identifier. +// DeclarationError: (2241-2242): Undeclared identifier. +// DeclarationError: (2252-2253): Undeclared identifier. +// DeclarationError: (2263-2264): Undeclared identifier. +// DeclarationError: (2274-2275): Undeclared identifier. +// DeclarationError: (2285-2286): Undeclared identifier. +// DeclarationError: (2296-2297): Undeclared identifier. +// DeclarationError: (2307-2308): Undeclared identifier. +// DeclarationError: (2318-2319): Undeclared identifier. +// DeclarationError: (2329-2330): Undeclared identifier. +// DeclarationError: (2340-2341): Undeclared identifier. +// DeclarationError: (2351-2352): Undeclared identifier. +// DeclarationError: (2362-2363): Undeclared identifier. +// DeclarationError: (2373-2374): Undeclared identifier. +// DeclarationError: (2384-2385): Undeclared identifier. +// DeclarationError: (2395-2396): Undeclared identifier. +// DeclarationError: (2406-2407): Undeclared identifier. +// DeclarationError: (2417-2418): Undeclared identifier. +// DeclarationError: (2428-2429): Undeclared identifier. +// DeclarationError: (2439-2440): Undeclared identifier. +// DeclarationError: (2450-2451): Undeclared identifier. +// DeclarationError: (2461-2462): Undeclared identifier. +// DeclarationError: (2472-2473): Undeclared identifier. +// DeclarationError: (2483-2484): Undeclared identifier. +// DeclarationError: (2494-2495): Undeclared identifier. +// DeclarationError: (2505-2506): Undeclared identifier. +// DeclarationError: (2516-2517): Undeclared identifier. +// DeclarationError: (2527-2528): Undeclared identifier. +// DeclarationError: (2538-2539): Undeclared identifier. +// DeclarationError: (2549-2550): Undeclared identifier. +// DeclarationError: (2560-2561): Undeclared identifier. +// DeclarationError: (2571-2572): Undeclared identifier. +// DeclarationError: (2582-2583): Undeclared identifier. +// DeclarationError: (2593-2594): Undeclared identifier. +// DeclarationError: (2604-2605): Undeclared identifier. +// DeclarationError: (2615-2616): Undeclared identifier. +// DeclarationError: (2626-2627): Undeclared identifier. +// DeclarationError: (2637-2638): Undeclared identifier. +// DeclarationError: (2648-2649): Undeclared identifier. +// DeclarationError: (2659-2660): Undeclared identifier. +// DeclarationError: (2670-2671): Undeclared identifier. +// DeclarationError: (2681-2682): Undeclared identifier. +// DeclarationError: (2692-2693): Undeclared identifier. +// DeclarationError: (2703-2704): Undeclared identifier. +// DeclarationError: (2714-2715): Undeclared identifier. +// DeclarationError: (2725-2726): Undeclared identifier. +// DeclarationError: (2736-2737): Undeclared identifier. +// DeclarationError: (2747-2748): Undeclared identifier. +// DeclarationError: (2758-2759): Undeclared identifier. +// DeclarationError: (2769-2770): Undeclared identifier. +// DeclarationError: (2780-2781): Undeclared identifier. +// DeclarationError: (2791-2792): Undeclared identifier. +// DeclarationError: (2802-2803): Undeclared identifier. +// DeclarationError: (2813-2814): Undeclared identifier. +// DeclarationError: (2824-2825): Undeclared identifier. +// DeclarationError: (2835-2836): Undeclared identifier. +// DeclarationError: (2846-2847): Undeclared identifier. // Warning: There are more than 256 errors. Aborting. diff --git a/test/libsolidity/syntaxTests/more_than_256_syntaxerrors.sol b/test/libsolidity/syntaxTests/more_than_256_syntaxerrors.sol index 2c9b8a42..fe877396 100644 --- a/test/libsolidity/syntaxTests/more_than_256_syntaxerrors.sol +++ b/test/libsolidity/syntaxTests/more_than_256_syntaxerrors.sol @@ -1,5 +1,5 @@ contract C { - function f() { + function f() public { continue; continue; continue; @@ -265,260 +265,260 @@ contract C { } } // ---- -// SyntaxError: (34-42): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (48-56): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (62-70): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (76-84): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (90-98): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (104-112): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (118-126): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (132-140): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (146-154): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (160-168): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (174-182): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (188-196): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (202-210): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (216-224): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (230-238): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (244-252): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (258-266): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (272-280): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (286-294): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (300-308): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (314-322): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (328-336): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (342-350): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (356-364): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (370-378): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (384-392): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (398-406): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (412-420): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (426-434): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (440-448): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (454-462): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (468-476): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (482-490): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (496-504): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (510-518): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (524-532): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (538-546): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (552-560): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (566-574): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (580-588): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (594-602): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (608-616): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (622-630): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (636-644): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (650-658): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (664-672): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (678-686): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (692-700): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (706-714): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (720-728): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (734-742): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (748-756): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (762-770): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (776-784): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (790-798): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (804-812): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (818-826): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (832-840): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (846-854): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (860-868): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (874-882): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (888-896): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (902-910): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (916-924): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (930-938): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (944-952): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (958-966): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (972-980): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (986-994): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (1000-1008): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (1014-1022): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (1028-1036): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (1042-1050): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (1056-1064): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (1070-1078): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (1084-1092): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (1098-1106): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (1112-1120): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (1126-1134): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (1140-1148): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (1154-1162): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (1168-1176): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (1182-1190): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (1196-1204): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (1210-1218): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (1224-1232): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (1238-1246): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (1252-1260): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (1266-1274): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (1280-1288): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (1294-1302): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (1308-1316): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (1322-1330): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (1336-1344): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (1350-1358): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (1364-1372): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (1378-1386): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (1392-1400): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (1406-1414): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (1420-1428): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (1434-1442): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (1448-1456): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (1462-1470): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (1476-1484): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (1490-1498): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (1504-1512): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (1518-1526): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (1532-1540): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (1546-1554): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (1560-1568): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (1574-1582): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (1588-1596): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (1602-1610): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (1616-1624): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (1630-1638): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (1644-1652): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (1658-1666): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (1672-1680): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (1686-1694): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (1700-1708): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (1714-1722): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (1728-1736): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (1742-1750): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (1756-1764): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (1770-1778): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (1784-1792): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (1798-1806): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (1812-1820): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (1826-1834): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (1840-1848): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (1854-1862): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (1868-1876): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (1882-1890): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (1896-1904): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (1910-1918): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (1924-1932): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (1938-1946): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (1952-1960): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (1966-1974): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (1980-1988): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (1994-2002): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (2008-2016): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (2022-2030): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (2036-2044): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (2050-2058): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (2064-2072): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (2078-2086): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (2092-2100): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (2106-2114): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (2120-2128): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (2134-2142): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (2148-2156): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (2162-2170): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (2176-2184): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (2190-2198): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (2204-2212): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (2218-2226): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (2232-2240): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (2246-2254): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (2260-2268): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (2274-2282): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (2288-2296): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (2302-2310): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (2316-2324): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (2330-2338): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (2344-2352): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (2358-2366): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (2372-2380): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (2386-2394): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (2400-2408): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (2414-2422): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (2428-2436): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (2442-2450): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (2456-2464): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (2470-2478): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (2484-2492): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (2498-2506): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (2512-2520): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (2526-2534): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (2540-2548): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (2554-2562): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (2568-2576): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (2582-2590): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (2596-2604): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (2610-2618): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (2624-2632): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (2638-2646): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (2652-2660): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (2666-2674): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (2680-2688): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (2694-2702): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (2708-2716): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (2722-2730): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (2736-2744): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (2750-2758): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (2764-2772): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (2778-2786): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (2792-2800): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (2806-2814): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (2820-2828): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (2834-2842): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (2848-2856): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (2862-2870): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (2876-2884): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (2890-2898): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (2904-2912): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (2918-2926): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (2932-2940): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (2946-2954): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (2960-2968): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (2974-2982): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (2988-2996): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (3002-3010): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (3016-3024): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (3030-3038): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (3044-3052): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (3058-3066): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (3072-3080): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (3086-3094): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (3100-3108): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (3114-3122): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (3128-3136): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (3142-3150): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (3156-3164): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (3170-3178): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (3184-3192): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (3198-3206): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (3212-3220): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (3226-3234): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (3240-3248): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (3254-3262): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (3268-3276): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (3282-3290): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (3296-3304): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (3310-3318): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (3324-3332): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (3338-3346): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (3352-3360): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (3366-3374): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (3380-3388): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (3394-3402): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (3408-3416): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (3422-3430): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (3436-3444): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (3450-3458): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (3464-3472): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (3478-3486): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (3492-3500): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (3506-3514): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (3520-3528): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (3534-3542): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (3548-3556): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (3562-3570): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (3576-3584): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (3590-3598): "continue" has to be in a "for" or "while" loop. -// SyntaxError: (3604-3612): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (41-49): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (55-63): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (69-77): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (83-91): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (97-105): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (111-119): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (125-133): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (139-147): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (153-161): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (167-175): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (181-189): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (195-203): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (209-217): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (223-231): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (237-245): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (251-259): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (265-273): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (279-287): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (293-301): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (307-315): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (321-329): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (335-343): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (349-357): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (363-371): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (377-385): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (391-399): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (405-413): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (419-427): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (433-441): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (447-455): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (461-469): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (475-483): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (489-497): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (503-511): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (517-525): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (531-539): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (545-553): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (559-567): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (573-581): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (587-595): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (601-609): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (615-623): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (629-637): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (643-651): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (657-665): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (671-679): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (685-693): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (699-707): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (713-721): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (727-735): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (741-749): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (755-763): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (769-777): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (783-791): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (797-805): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (811-819): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (825-833): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (839-847): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (853-861): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (867-875): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (881-889): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (895-903): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (909-917): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (923-931): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (937-945): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (951-959): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (965-973): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (979-987): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (993-1001): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (1007-1015): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (1021-1029): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (1035-1043): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (1049-1057): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (1063-1071): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (1077-1085): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (1091-1099): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (1105-1113): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (1119-1127): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (1133-1141): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (1147-1155): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (1161-1169): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (1175-1183): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (1189-1197): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (1203-1211): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (1217-1225): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (1231-1239): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (1245-1253): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (1259-1267): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (1273-1281): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (1287-1295): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (1301-1309): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (1315-1323): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (1329-1337): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (1343-1351): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (1357-1365): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (1371-1379): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (1385-1393): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (1399-1407): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (1413-1421): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (1427-1435): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (1441-1449): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (1455-1463): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (1469-1477): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (1483-1491): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (1497-1505): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (1511-1519): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (1525-1533): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (1539-1547): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (1553-1561): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (1567-1575): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (1581-1589): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (1595-1603): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (1609-1617): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (1623-1631): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (1637-1645): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (1651-1659): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (1665-1673): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (1679-1687): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (1693-1701): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (1707-1715): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (1721-1729): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (1735-1743): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (1749-1757): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (1763-1771): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (1777-1785): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (1791-1799): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (1805-1813): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (1819-1827): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (1833-1841): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (1847-1855): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (1861-1869): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (1875-1883): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (1889-1897): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (1903-1911): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (1917-1925): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (1931-1939): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (1945-1953): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (1959-1967): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (1973-1981): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (1987-1995): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (2001-2009): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (2015-2023): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (2029-2037): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (2043-2051): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (2057-2065): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (2071-2079): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (2085-2093): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (2099-2107): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (2113-2121): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (2127-2135): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (2141-2149): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (2155-2163): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (2169-2177): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (2183-2191): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (2197-2205): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (2211-2219): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (2225-2233): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (2239-2247): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (2253-2261): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (2267-2275): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (2281-2289): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (2295-2303): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (2309-2317): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (2323-2331): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (2337-2345): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (2351-2359): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (2365-2373): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (2379-2387): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (2393-2401): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (2407-2415): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (2421-2429): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (2435-2443): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (2449-2457): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (2463-2471): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (2477-2485): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (2491-2499): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (2505-2513): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (2519-2527): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (2533-2541): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (2547-2555): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (2561-2569): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (2575-2583): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (2589-2597): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (2603-2611): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (2617-2625): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (2631-2639): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (2645-2653): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (2659-2667): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (2673-2681): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (2687-2695): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (2701-2709): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (2715-2723): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (2729-2737): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (2743-2751): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (2757-2765): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (2771-2779): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (2785-2793): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (2799-2807): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (2813-2821): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (2827-2835): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (2841-2849): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (2855-2863): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (2869-2877): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (2883-2891): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (2897-2905): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (2911-2919): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (2925-2933): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (2939-2947): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (2953-2961): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (2967-2975): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (2981-2989): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (2995-3003): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (3009-3017): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (3023-3031): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (3037-3045): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (3051-3059): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (3065-3073): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (3079-3087): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (3093-3101): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (3107-3115): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (3121-3129): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (3135-3143): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (3149-3157): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (3163-3171): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (3177-3185): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (3191-3199): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (3205-3213): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (3219-3227): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (3233-3241): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (3247-3255): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (3261-3269): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (3275-3283): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (3289-3297): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (3303-3311): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (3317-3325): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (3331-3339): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (3345-3353): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (3359-3367): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (3373-3381): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (3387-3395): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (3401-3409): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (3415-3423): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (3429-3437): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (3443-3451): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (3457-3465): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (3471-3479): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (3485-3493): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (3499-3507): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (3513-3521): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (3527-3535): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (3541-3549): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (3555-3563): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (3569-3577): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (3583-3591): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (3597-3605): "continue" has to be in a "for" or "while" loop. +// SyntaxError: (3611-3619): "continue" has to be in a "for" or "while" loop. // Warning: There are more than 256 errors. Aborting. diff --git a/test/libsolidity/syntaxTests/multiVariableDeclaration/differentNumberOfComponents.sol b/test/libsolidity/syntaxTests/multiVariableDeclaration/differentNumberOfComponents.sol new file mode 100644 index 00000000..3b05a54c --- /dev/null +++ b/test/libsolidity/syntaxTests/multiVariableDeclaration/differentNumberOfComponents.sol @@ -0,0 +1,25 @@ +contract C { + function f() public { + uint a = (1,2); + uint b = (1,2,3); + uint c = (1,2,3,4); + } + function g() public { + (uint a1, uint b1, uint c1, uint d1) = 1; + (uint a2, uint b2, uint c2) = 1; + (uint a3, uint b3) = 1; + } + function h() public { + (uint a1, uint b1, uint c1, uint d1) = (1,2,3); + (uint a2, uint b2, uint c2) = (1,2,3,4); + } +} +// ---- +// TypeError: (47-61): Different number of components on the left hand side (1) than on the right hand side (2). +// TypeError: (71-87): Different number of components on the left hand side (1) than on the right hand side (3). +// TypeError: (97-115): Different number of components on the left hand side (1) than on the right hand side (4). +// TypeError: (157-197): Different number of components on the left hand side (4) than on the right hand side (1). +// TypeError: (207-238): Different number of components on the left hand side (3) than on the right hand side (1). +// TypeError: (248-270): Different number of components on the left hand side (2) than on the right hand side (1). +// TypeError: (312-358): Different number of components on the left hand side (4) than on the right hand side (3). +// TypeError: (368-407): Different number of components on the left hand side (3) than on the right hand side (4). diff --git a/test/libsolidity/syntaxTests/multiVariableDeclaration/differentNumberOfComponentsFromReturn.sol b/test/libsolidity/syntaxTests/multiVariableDeclaration/differentNumberOfComponentsFromReturn.sol new file mode 100644 index 00000000..7b556350 --- /dev/null +++ b/test/libsolidity/syntaxTests/multiVariableDeclaration/differentNumberOfComponentsFromReturn.sol @@ -0,0 +1,29 @@ +contract C { + function f() public { + uint a = two(); + uint b = three(); + uint c = four(); + } + function g() public { + (uint a1, uint b1, uint c1, uint d1) = one(); + (uint a2, uint b2, uint c2) = one(); + (uint a3, uint b3) = one(); + } + function h() public { + (uint a1, uint b1, uint c1, uint d1) = three(); + (uint a2, uint b2, uint c2) = four(); + } + function one() public pure returns (uint); + function two() public pure returns (uint, uint); + function three() public pure returns (uint, uint, uint); + function four() public pure returns (uint, uint, uint, uint); +} +// ---- +// TypeError: (47-61): Different number of components on the left hand side (1) than on the right hand side (2). +// TypeError: (71-87): Different number of components on the left hand side (1) than on the right hand side (3). +// TypeError: (97-112): Different number of components on the left hand side (1) than on the right hand side (4). +// TypeError: (154-198): Different number of components on the left hand side (4) than on the right hand side (1). +// TypeError: (208-243): Different number of components on the left hand side (3) than on the right hand side (1). +// TypeError: (253-279): Different number of components on the left hand side (2) than on the right hand side (1). +// TypeError: (321-367): Different number of components on the left hand side (4) than on the right hand side (3). +// TypeError: (377-413): Different number of components on the left hand side (3) than on the right hand side (4). diff --git a/test/libsolidity/syntaxTests/multiVariableDeclaration/disallowWildcards.sol b/test/libsolidity/syntaxTests/multiVariableDeclaration/disallowWildcards.sol new file mode 100644 index 00000000..b500823d --- /dev/null +++ b/test/libsolidity/syntaxTests/multiVariableDeclaration/disallowWildcards.sol @@ -0,0 +1,24 @@ +contract C { + function fn() public pure { + (uint a,) = (1,2,3); + (,uint b) = (1,2,3); + (,uint c,) = (1,2,3,4,5); + (uint d, uint e,) = (1,2,3,4); + (,uint f, uint g) = (1,2,3,4); + (,uint h, uint i,) = (1,2,3); + (uint j,) = 1; + (,uint k) = 1; + (,uint l,) = 1; + a;b;c;d;e;f;g;h;i;j;k;l; + } +} +// ---- +// TypeError: (53-72): Different number of components on the left hand side (2) than on the right hand side (3). +// TypeError: (82-101): Different number of components on the left hand side (2) than on the right hand side (3). +// TypeError: (111-135): Different number of components on the left hand side (3) than on the right hand side (5). +// TypeError: (145-174): Different number of components on the left hand side (3) than on the right hand side (4). +// TypeError: (184-213): Different number of components on the left hand side (3) than on the right hand side (4). +// TypeError: (223-251): Different number of components on the left hand side (4) than on the right hand side (3). +// TypeError: (261-274): Different number of components on the left hand side (2) than on the right hand side (1). +// TypeError: (284-297): Different number of components on the left hand side (2) than on the right hand side (1). +// TypeError: (307-321): Different number of components on the left hand side (3) than on the right hand side (1). diff --git a/test/libsolidity/syntaxTests/multiVariableDeclaration/disallowWildcardsFromReturn.sol b/test/libsolidity/syntaxTests/multiVariableDeclaration/disallowWildcardsFromReturn.sol new file mode 100644 index 00000000..3224a182 --- /dev/null +++ b/test/libsolidity/syntaxTests/multiVariableDeclaration/disallowWildcardsFromReturn.sol @@ -0,0 +1,31 @@ +contract C { + function fn() public pure { + (uint a,) = three(); + (,uint b) = three(); + (,uint c,) = five(); + (uint d, uint e,) = four(); + (,uint f, uint g) = four(); + (,uint h, uint i,) = three(); + (uint j,) = one(); + (,uint k) = one(); + (,uint l,) = one(); + (,uint m, uint n,) = five(); + a;b;c;d;e;f;g;h;i;j;k;l;m;n; + } + function one() public pure returns (uint); + function two() public pure returns (uint, uint); + function three() public pure returns (uint, uint, uint); + function four() public pure returns (uint, uint, uint, uint); + function five() public pure returns (uint, uint, uint, uint, uint); +} +// ---- +// TypeError: (53-72): Different number of components on the left hand side (2) than on the right hand side (3). +// TypeError: (82-101): Different number of components on the left hand side (2) than on the right hand side (3). +// TypeError: (111-130): Different number of components on the left hand side (3) than on the right hand side (5). +// TypeError: (140-166): Different number of components on the left hand side (3) than on the right hand side (4). +// TypeError: (176-202): Different number of components on the left hand side (3) than on the right hand side (4). +// TypeError: (212-240): Different number of components on the left hand side (4) than on the right hand side (3). +// TypeError: (250-267): Different number of components on the left hand side (2) than on the right hand side (1). +// TypeError: (277-294): Different number of components on the left hand side (2) than on the right hand side (1). +// TypeError: (304-322): Different number of components on the left hand side (3) than on the right hand side (1). +// TypeError: (332-359): Different number of components on the left hand side (4) than on the right hand side (5). diff --git a/test/libsolidity/syntaxTests/multiVariableDeclaration/multiSingleVariableDeclaration.sol b/test/libsolidity/syntaxTests/multiVariableDeclaration/multiSingleVariableDeclaration.sol index 182ba072..7db98577 100644 --- a/test/libsolidity/syntaxTests/multiVariableDeclaration/multiSingleVariableDeclaration.sol +++ b/test/libsolidity/syntaxTests/multiVariableDeclaration/multiSingleVariableDeclaration.sol @@ -3,4 +3,4 @@ contract C { (uint a) = f(); a; } -} +} diff --git a/test/libsolidity/syntaxTests/multiVariableDeclaration/multiVariableDeclarationComplex.sol b/test/libsolidity/syntaxTests/multiVariableDeclaration/multiVariableDeclarationComplex.sol index a3ce6a74..ba6e9916 100644 --- a/test/libsolidity/syntaxTests/multiVariableDeclaration/multiVariableDeclarationComplex.sol +++ b/test/libsolidity/syntaxTests/multiVariableDeclaration/multiVariableDeclarationComplex.sol @@ -2,10 +2,10 @@ contract D { struct S { uint a; uint b; } } contract C { - function f() internal returns (uint, uint, uint, D.S[20] storage, uint) { - (,,,D.S[10*2] storage x,) = f(); + function f() internal pure { + (,,,D.S[10*2] storage x,) = g(); x; } -} + function g() internal pure returns (uint, uint, uint, D.S[20] storage x, uint) { x = x; } +} // ---- -// Warning: (110-117): This variable is of storage pointer type and might be returned without assignment. This can cause storage corruption. Assign the variable (potentially from itself) to remove this warning. diff --git a/test/libsolidity/syntaxTests/multiVariableDeclaration/multiVariableDeclarationEmpty.sol b/test/libsolidity/syntaxTests/multiVariableDeclaration/multiVariableDeclarationEmpty.sol new file mode 100644 index 00000000..9618958e --- /dev/null +++ b/test/libsolidity/syntaxTests/multiVariableDeclaration/multiVariableDeclarationEmpty.sol @@ -0,0 +1,11 @@ +contract C { + function f() public pure { + (uint a, uint b) = f(); + (uint c) = f(); + uint d = f(); + } +} +// ---- +// TypeError: (52-74): Different number of components on the left hand side (2) than on the right hand side (0). +// TypeError: (84-98): Different number of components on the left hand side (1) than on the right hand side (0). +// TypeError: (108-120): Different number of components on the left hand side (1) than on the right hand side (0). diff --git a/test/libsolidity/syntaxTests/multiVariableDeclaration/multiVariableDeclarationInvalid.sol b/test/libsolidity/syntaxTests/multiVariableDeclaration/multiVariableDeclarationInvalid.sol deleted file mode 100644 index c8686ae8..00000000 --- a/test/libsolidity/syntaxTests/multiVariableDeclaration/multiVariableDeclarationInvalid.sol +++ /dev/null @@ -1,8 +0,0 @@ -contract C { - function f() internal returns (uint, uint, uint, uint) { - var (uint a, uint b,,) = f(); - a; b; - } -} -// ---- -// ParserError: (81-85): Expected identifier but got 'uint' diff --git a/test/libsolidity/syntaxTests/multiVariableDeclaration/multiVariableDeclarationInvalidType.sol b/test/libsolidity/syntaxTests/multiVariableDeclaration/multiVariableDeclarationInvalidType.sol index 2b765837..85094d00 100644 --- a/test/libsolidity/syntaxTests/multiVariableDeclaration/multiVariableDeclarationInvalidType.sol +++ b/test/libsolidity/syntaxTests/multiVariableDeclaration/multiVariableDeclarationInvalidType.sol @@ -3,7 +3,7 @@ contract C { (uint a, string memory b,,) = f(); a; b; } -} +} // ---- // TypeError: (85-118): Type string memory is not implicitly convertible to expected type uint256. // TypeError: (85-118): Type uint256 is not implicitly convertible to expected type string memory. diff --git a/test/libsolidity/syntaxTests/multiVariableDeclaration/multiVariableDeclarationScoping.sol b/test/libsolidity/syntaxTests/multiVariableDeclaration/multiVariableDeclarationScoping.sol index 3ba85f69..1f9e52d1 100644 --- a/test/libsolidity/syntaxTests/multiVariableDeclaration/multiVariableDeclarationScoping.sol +++ b/test/libsolidity/syntaxTests/multiVariableDeclaration/multiVariableDeclarationScoping.sol @@ -1,5 +1,3 @@ -pragma experimental "v0.5.0"; - contract C { function f() internal { { @@ -7,6 +5,6 @@ contract C { } a; } -} +} // ---- -// DeclarationError: (130-131): Undeclared identifier. +// DeclarationError: (99-100): Undeclared identifier. diff --git a/test/libsolidity/syntaxTests/multiVariableDeclaration/multiVariableDeclarationScoping2.sol b/test/libsolidity/syntaxTests/multiVariableDeclaration/multiVariableDeclarationScoping2.sol index e21181de..45b8858b 100644 --- a/test/libsolidity/syntaxTests/multiVariableDeclaration/multiVariableDeclarationScoping2.sol +++ b/test/libsolidity/syntaxTests/multiVariableDeclaration/multiVariableDeclarationScoping2.sol @@ -1,13 +1,11 @@ -pragma experimental "v0.5.0"; - contract C { function f() internal { { (uint a, uint b, uint c) = (a, b, c); } } -} +} // ---- -// DeclarationError: (110-111): Undeclared identifier. Did you mean "a"? -// DeclarationError: (113-114): Undeclared identifier. Did you mean "b"? -// DeclarationError: (116-117): Undeclared identifier. Did you mean "c"? +// DeclarationError: (79-80): Undeclared identifier. "a" is not (or not yet) visible at this point. +// DeclarationError: (82-83): Undeclared identifier. "b" is not (or not yet) visible at this point. +// DeclarationError: (85-86): Undeclared identifier. "c" is not (or not yet) visible at this point. diff --git a/test/libsolidity/syntaxTests/multiVariableDeclaration/multiVariableDeclarationSimple.sol b/test/libsolidity/syntaxTests/multiVariableDeclaration/multiVariableDeclarationSimple.sol index 8e06322c..a2fcce18 100644 --- a/test/libsolidity/syntaxTests/multiVariableDeclaration/multiVariableDeclarationSimple.sol +++ b/test/libsolidity/syntaxTests/multiVariableDeclaration/multiVariableDeclarationSimple.sol @@ -1,12 +1,12 @@ contract C { - function f() internal returns (uint, uint, uint, uint) { + function f() internal pure returns (uint, uint, uint, uint) { (uint a, uint b,,) = f(); a; b; } - function g() internal returns (bytes memory, string storage) { - (bytes memory a, string storage b) = g(); + function g() internal pure { + (bytes memory a, string storage b) = h(); a; b; } -} + function h() internal pure returns (bytes memory, string storage s) { s = s; } +} // ---- -// Warning: (163-169): This variable is of storage pointer type and might be returned without assignment. This can cause storage corruption. Assign the variable (potentially from itself) to remove this warning. diff --git a/test/libsolidity/syntaxTests/multiVariableDeclaration/multiVariableDeclarationThatIsExpression.sol b/test/libsolidity/syntaxTests/multiVariableDeclaration/multiVariableDeclarationThatIsExpression.sol index 8ae0eaac..00458908 100644 --- a/test/libsolidity/syntaxTests/multiVariableDeclaration/multiVariableDeclarationThatIsExpression.sol +++ b/test/libsolidity/syntaxTests/multiVariableDeclaration/multiVariableDeclarationThatIsExpression.sol @@ -4,6 +4,6 @@ contract C { function f() internal pure returns (uint, uint, uint, S storage, uint, uint) { (,,,s.x[2](),,) = f(); } -} +} // ---- // TypeError: (160-168): Expression has to be an lvalue. diff --git a/test/libsolidity/syntaxTests/multiVariableDeclaration/oneElementTuple.sol b/test/libsolidity/syntaxTests/multiVariableDeclaration/oneElementTuple.sol new file mode 100644 index 00000000..562c7c0b --- /dev/null +++ b/test/libsolidity/syntaxTests/multiVariableDeclaration/oneElementTuple.sol @@ -0,0 +1,8 @@ +contract C { + function f() public { + (uint a,) = (1,); + a; + } +} +// ---- +// TypeError: (59-63): Tuple component cannot be empty. diff --git a/test/libsolidity/syntaxTests/multiVariableDeclaration/sameNumberOfComponents.sol b/test/libsolidity/syntaxTests/multiVariableDeclaration/sameNumberOfComponents.sol new file mode 100644 index 00000000..59eb34af --- /dev/null +++ b/test/libsolidity/syntaxTests/multiVariableDeclaration/sameNumberOfComponents.sol @@ -0,0 +1,9 @@ +contract C { + function f() public pure { + (uint a1, uint b1, uint c1, uint d1) = (1,2,3,4); + (uint a2, uint b2, uint c2) = (1,2,3); + (uint a3, uint b3) = (1,2); + a1; b1; c1; d1; a2; b2; c2; a3; b3; + } +} +// ---- diff --git a/test/libsolidity/syntaxTests/multiline_comments.sol b/test/libsolidity/syntaxTests/multiline_comments.sol new file mode 100644 index 00000000..480fde6c --- /dev/null +++ b/test/libsolidity/syntaxTests/multiline_comments.sol @@ -0,0 +1,13 @@ +/* + * This is a multi-line comment + * it should create no problems + * +*/ + +contract test { + /* + * this is another multi-line comment + * + */ +} +// ---- diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/001_name_references.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/001_name_references.sol new file mode 100644 index 00000000..dc304a1d --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/001_name_references.sol @@ -0,0 +1,4 @@ +contract test { + uint256 variable; + function f(uint256) public returns (uint out) { f(variable); test; out; } +} diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/002_undeclared_name.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/002_undeclared_name.sol new file mode 100644 index 00000000..afe9483f --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/002_undeclared_name.sol @@ -0,0 +1,8 @@ +contract test { + uint256 variable; + function f(uint256 arg) public { + f(notfound); + } +} +// ---- +// DeclarationError: (85-93): Undeclared identifier. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/003_undeclared_name_is_not_fatal.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/003_undeclared_name_is_not_fatal.sol new file mode 100644 index 00000000..0f2a1526 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/003_undeclared_name_is_not_fatal.sol @@ -0,0 +1,10 @@ +contract test { + uint256 variable; + function f(uint256 arg) public { + f(notfound); + f(notfound); + } +} +// ---- +// DeclarationError: (85-93): Undeclared identifier. +// DeclarationError: (106-114): Undeclared identifier. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/004_reference_to_later_declaration.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/004_reference_to_later_declaration.sol new file mode 100644 index 00000000..e112e16c --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/004_reference_to_later_declaration.sol @@ -0,0 +1,6 @@ +contract test { + function g() public { f(); } + function f() public {} +} +// ---- +// Warning: (53-75): Function state mutability can be restricted to pure diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/006_type_checking_return.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/006_type_checking_return.sol new file mode 100644 index 00000000..d0e87139 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/006_type_checking_return.sol @@ -0,0 +1,5 @@ +contract test { + function f() public returns (bool r) { return 1 >= 2; } +} +// ---- +// Warning: (20-75): Function state mutability can be restricted to pure diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/007_type_checking_return_wrong_number.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/007_type_checking_return_wrong_number.sol new file mode 100644 index 00000000..13c70ad9 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/007_type_checking_return_wrong_number.sol @@ -0,0 +1,5 @@ +contract test { + function f() public returns (bool r1, bool r2) { return 1 >= 2; } +} +// ---- +// TypeError: (69-82): Different number of arguments in return statement than in returns declaration. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/008_type_checking_return_wrong_type.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/008_type_checking_return_wrong_type.sol new file mode 100644 index 00000000..a7459ae8 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/008_type_checking_return_wrong_type.sol @@ -0,0 +1,5 @@ +contract test { + function f() public returns (uint256 r) { return 1 >= 2; } +} +// ---- +// TypeError: (69-75): Return argument type bool is not implicitly convertible to expected type (type of first return variable) uint256. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/009_type_checking_function_call.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/009_type_checking_function_call.sol new file mode 100644 index 00000000..abe2beac --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/009_type_checking_function_call.sol @@ -0,0 +1,6 @@ +contract test { + function f() public returns (bool) { return g(12, true) == 3; } + function g(uint256, bool) public returns (uint256) { } +} +// ---- +// Warning: (88-142): Function state mutability can be restricted to pure diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/010_type_conversion_for_comparison.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/010_type_conversion_for_comparison.sol new file mode 100644 index 00000000..c0cd87d4 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/010_type_conversion_for_comparison.sol @@ -0,0 +1,6 @@ +contract test { + function f() public { uint32(2) == int64(2); } +} +// ---- +// Warning: (42-63): Statement has no effect. +// Warning: (20-66): Function state mutability can be restricted to pure diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/011_type_conversion_for_comparison_invalid.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/011_type_conversion_for_comparison_invalid.sol new file mode 100644 index 00000000..9cbce0d0 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/011_type_conversion_for_comparison_invalid.sol @@ -0,0 +1,5 @@ +contract test { + function f() public { int32(2) == uint64(2); } +} +// ---- +// TypeError: (42-63): Operator == not compatible with types int32 and uint64 diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/013_large_string_literal.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/013_large_string_literal.sol new file mode 100644 index 00000000..7f858a4d --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/013_large_string_literal.sol @@ -0,0 +1,6 @@ +contract test { + function f() public { string memory x = "123456789012345678901234567890123"; } +} +// ---- +// Warning: (42-57): Unused local variable. +// Warning: (20-98): Function state mutability can be restricted to pure diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/014_balance.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/014_balance.sol new file mode 100644 index 00000000..e2c9a8bf --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/014_balance.sol @@ -0,0 +1,8 @@ +contract test { + function fun() public { + uint256 x = address(0).balance; + } +} +// ---- +// Warning: (52-61): Unused local variable. +// Warning: (20-89): Function state mutability can be restricted to view diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/015_balance_invalid.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/015_balance_invalid.sol new file mode 100644 index 00000000..18658fbe --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/015_balance_invalid.sol @@ -0,0 +1,7 @@ +contract test { + function fun() public { + address(0).balance = 7; + } +} +// ---- +// TypeError: (52-70): Expression has to be an lvalue. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/017_assignment_to_struct.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/017_assignment_to_struct.sol new file mode 100644 index 00000000..6fbd09ae --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/017_assignment_to_struct.sol @@ -0,0 +1,11 @@ +contract test { + struct str { + mapping(uint=>uint) map; + } + str data; + function fun() public { + str storage a = data; + data = a; + } +} +// ---- diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/018_forward_function_reference.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/018_forward_function_reference.sol new file mode 100644 index 00000000..fd9ab7ed --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/018_forward_function_reference.sol @@ -0,0 +1,10 @@ +contract First { + function fun() public returns (bool) { + return Second(1).fun(1, true, 3) > 0; + } +} +contract Second { + function fun(uint, bool, uint) public returns (uint) { + if (First(2).fun() == true) return 1; + } +} diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/019_comparison_bitop_precedence.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/019_comparison_bitop_precedence.sol new file mode 100644 index 00000000..eab272df --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/019_comparison_bitop_precedence.sol @@ -0,0 +1,7 @@ +contract First { + function fun() public returns (bool ret) { + return 1 & 2 == 8 & 9 && 1 ^ 2 < 4 | 6; + } +} +// ---- +// Warning: (21-117): Function state mutability can be restricted to pure diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/020_comparison_of_function_types_lt_1.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/020_comparison_of_function_types_lt_1.sol new file mode 100644 index 00000000..1f288ff7 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/020_comparison_of_function_types_lt_1.sol @@ -0,0 +1,7 @@ +contract C { + function f() public returns (bool ret) { + return this.f < this.f; + } +} +// ---- +// TypeError: (73-88): Operator < not compatible with types function () external returns (bool) and function () external returns (bool) diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/021_comparison_of_function_types_lt_2.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/021_comparison_of_function_types_lt_2.sol new file mode 100644 index 00000000..a6422d38 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/021_comparison_of_function_types_lt_2.sol @@ -0,0 +1,7 @@ +contract C { + function f() public returns (bool ret) { + return f < f; + } +} +// ---- +// TypeError: (73-78): Operator < not compatible with types function () returns (bool) and function () returns (bool) diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/022_comparison_of_function_types_gt_1.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/022_comparison_of_function_types_gt_1.sol new file mode 100644 index 00000000..ee865912 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/022_comparison_of_function_types_gt_1.sol @@ -0,0 +1,7 @@ +contract C { + function f() public returns (bool ret) { + return this.f > this.f; + } +} +// ---- +// TypeError: (73-88): Operator > not compatible with types function () external returns (bool) and function () external returns (bool) diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/023_comparison_of_function_types_gt_2.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/023_comparison_of_function_types_gt_2.sol new file mode 100644 index 00000000..590cc98b --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/023_comparison_of_function_types_gt_2.sol @@ -0,0 +1,7 @@ +contract C { + function f() public returns (bool ret) { + return f > f; + } +} +// ---- +// TypeError: (73-78): Operator > not compatible with types function () returns (bool) and function () returns (bool) diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/024_comparison_of_function_types_eq.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/024_comparison_of_function_types_eq.sol new file mode 100644 index 00000000..71dbec6b --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/024_comparison_of_function_types_eq.sol @@ -0,0 +1,11 @@ +contract C { + function f() public returns (bool ret) { + return f == f; + } + function g() public returns (bool ret) { + return f != f; + } +} +// ---- +// Warning: (17-86): Function state mutability can be restricted to pure +// Warning: (91-160): Function state mutability can be restricted to pure diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/025_comparison_of_mapping_types.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/025_comparison_of_mapping_types.sol new file mode 100644 index 00000000..b15666c0 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/025_comparison_of_mapping_types.sol @@ -0,0 +1,9 @@ +contract C { + mapping(uint => uint) x; + function f() public returns (bool ret) { + mapping(uint => uint) storage y = x; + return x == y; + } +} +// ---- +// TypeError: (147-153): Operator == not compatible with types mapping(uint256 => uint256) and mapping(uint256 => uint256) diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/029_create_abstract_contract.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/029_create_abstract_contract.sol new file mode 100644 index 00000000..455f4189 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/029_create_abstract_contract.sol @@ -0,0 +1,7 @@ +contract base { function foo() public; } +contract derived { + base b; + function foo() public { b = new base(); } +} +// ---- +// TypeError: (104-112): Trying to create an instance of an abstract contract. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/030_redeclare_implemented_abstract_function_as_abstract.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/030_redeclare_implemented_abstract_function_as_abstract.sol new file mode 100644 index 00000000..55bdea89 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/030_redeclare_implemented_abstract_function_as_abstract.sol @@ -0,0 +1,5 @@ +contract base { function foo() public; } +contract derived is base { function foo() public {} } +contract wrong is derived { function foo() public; } +// ---- +// TypeError: (123-145): Redeclaring an already implemented function as abstract diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/039_functions_with_identical_structs_in_interface.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/039_functions_with_identical_structs_in_interface.sol new file mode 100644 index 00000000..3389ffe4 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/039_functions_with_identical_structs_in_interface.sol @@ -0,0 +1,11 @@ +pragma experimental ABIEncoderV2; + +contract C { + struct S1 { int i; } + struct S2 { int i; } + function f(S1 memory) public pure {} + function f(S2 memory) public pure {} +} +// ---- +// Warning: (0-33): Experimental features are turned on. Do not use experimental features on live deployments. +// TypeError: (143-179): Function overload clash during conversion to external types for arguments. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/040_functions_with_different_structs_in_interface.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/040_functions_with_different_structs_in_interface.sol new file mode 100644 index 00000000..6ff8fd6e --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/040_functions_with_different_structs_in_interface.sol @@ -0,0 +1,10 @@ +pragma experimental ABIEncoderV2; + +contract C { + struct S1 { function() external a; } + struct S2 { bytes24 a; } + function f(S1 memory) public pure {} + function f(S2 memory) public pure {} +} +// ---- +// Warning: (0-33): Experimental features are turned on. Do not use experimental features on live deployments.
\ No newline at end of file diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/041_functions_with_stucts_of_non_external_types_in_interface.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/041_functions_with_stucts_of_non_external_types_in_interface.sol new file mode 100644 index 00000000..73b608ae --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/041_functions_with_stucts_of_non_external_types_in_interface.sol @@ -0,0 +1,9 @@ +pragma experimental ABIEncoderV2; + +contract C { + struct S { function() internal a; } + function f(S memory) public {} +} +// ---- +// Warning: (0-33): Experimental features are turned on. Do not use experimental features on live deployments. +// TypeError: (103-111): Internal or recursive type is not allowed for public or external functions. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/042_functions_with_stucts_of_non_external_types_in_interface_2.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/042_functions_with_stucts_of_non_external_types_in_interface_2.sol new file mode 100644 index 00000000..607a4a68 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/042_functions_with_stucts_of_non_external_types_in_interface_2.sol @@ -0,0 +1,9 @@ +pragma experimental ABIEncoderV2; + +contract C { + struct S { mapping(uint => uint) a; } + function f(S memory) public {} +} +// ---- +// Warning: (0-33): Experimental features are turned on. Do not use experimental features on live deployments. +// TypeError: (105-113): Internal or recursive type is not allowed for public or external functions. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/043_functions_with_stucts_of_non_external_types_in_interface_nested.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/043_functions_with_stucts_of_non_external_types_in_interface_nested.sol new file mode 100644 index 00000000..da73d8dd --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/043_functions_with_stucts_of_non_external_types_in_interface_nested.sol @@ -0,0 +1,10 @@ +pragma experimental ABIEncoderV2; + +contract C { + struct T { mapping(uint => uint) a; } + struct S { T[][2] b; } + function f(S memory) public {} +} +// ---- +// Warning: (0-33): Experimental features are turned on. Do not use experimental features on live deployments. +// TypeError: (132-140): Internal or recursive type is not allowed for public or external functions. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/044_returning_multi_dimensional_arrays_new_abi.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/044_returning_multi_dimensional_arrays_new_abi.sol new file mode 100644 index 00000000..ae9416e5 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/044_returning_multi_dimensional_arrays_new_abi.sol @@ -0,0 +1,7 @@ +pragma experimental ABIEncoderV2; + +contract C { + function f() public pure returns (string[][] memory) {} +} +// ---- +// Warning: (0-33): Experimental features are turned on. Do not use experimental features on live deployments. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/045_returning_multi_dimensional_arrays.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/045_returning_multi_dimensional_arrays.sol new file mode 100644 index 00000000..b9a64c2a --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/045_returning_multi_dimensional_arrays.sol @@ -0,0 +1,5 @@ +contract C { + function f() public pure returns (string[][] memory) {} +} +// ---- +// TypeError: (51-68): This type is only supported in the new experimental ABI encoder. Use "pragma experimental ABIEncoderV2;" to enable the feature. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/046_returning_multi_dimensional_static_arrays.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/046_returning_multi_dimensional_static_arrays.sol new file mode 100644 index 00000000..ccee6093 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/046_returning_multi_dimensional_static_arrays.sol @@ -0,0 +1,5 @@ +contract C { + function f() public pure returns (uint[][2] memory) {} +} +// ---- +// TypeError: (51-67): This type is only supported in the new experimental ABI encoder. Use "pragma experimental ABIEncoderV2;" to enable the feature. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/047_returning_arrays_in_structs_new_abi.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/047_returning_arrays_in_structs_new_abi.sol new file mode 100644 index 00000000..8ca3a53d --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/047_returning_arrays_in_structs_new_abi.sol @@ -0,0 +1,8 @@ +pragma experimental ABIEncoderV2; + +contract C { + struct S { string[] s; } + function f() public pure returns (S memory) {} +} +// ---- +// Warning: (0-33): Experimental features are turned on. Do not use experimental features on live deployments. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/048_returning_arrays_in_structs_arrays.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/048_returning_arrays_in_structs_arrays.sol new file mode 100644 index 00000000..48e80fcf --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/048_returning_arrays_in_structs_arrays.sol @@ -0,0 +1,6 @@ +contract C { + struct S { string[] s; } + function f() public pure returns (S memory x) {} +} +// ---- +// TypeError: (80-90): This type is only supported in the new experimental ABI encoder. Use "pragma experimental ABIEncoderV2;" to enable the feature. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/049_function_external_call_allowed_conversion.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/049_function_external_call_allowed_conversion.sol new file mode 100644 index 00000000..ec72adeb --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/049_function_external_call_allowed_conversion.sol @@ -0,0 +1,11 @@ +contract C {} +contract Test { + function externalCall() public { + C arg; + this.g(arg); + } + function g (C c) external {} +} +// ---- +// Warning: (125-128): Unused function parameter. Remove or comment out the variable name to silence this warning. +// Warning: (113-141): Function state mutability can be restricted to pure diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/050_function_external_call_not_allowed_conversion.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/050_function_external_call_not_allowed_conversion.sol new file mode 100644 index 00000000..18d75948 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/050_function_external_call_not_allowed_conversion.sol @@ -0,0 +1,10 @@ +contract C {} +contract Test { + function externalCall() public { + address arg; + this.g(arg); + } + function g (C c) external {} +} +// ---- +// TypeError: (103-106): Invalid type for argument in function call. Invalid implicit conversion from address to contract C requested. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/051_function_internal_allowed_conversion.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/051_function_internal_allowed_conversion.sol new file mode 100644 index 00000000..aedc7b0b --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/051_function_internal_allowed_conversion.sol @@ -0,0 +1,13 @@ +contract C { + uint a; +} +contract Test { + C a; + function g (C c) public {} + function internalCall() public { + g(a); + } +} +// ---- +// Warning: (68-71): Unused function parameter. Remove or comment out the variable name to silence this warning. +// Warning: (56-82): Function state mutability can be restricted to pure diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/052_function_internal_not_allowed_conversion.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/052_function_internal_not_allowed_conversion.sol new file mode 100644 index 00000000..c16d35eb --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/052_function_internal_not_allowed_conversion.sol @@ -0,0 +1,12 @@ +contract C { + uint a; +} +contract Test { + address a; + function g (C c) public {} + function internalCall() public { + g(a); + } +} +// ---- +// TypeError: (136-137): Invalid type for argument in function call. Invalid implicit conversion from address to contract C requested. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/053_hash_collision_in_interface.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/053_hash_collision_in_interface.sol new file mode 100644 index 00000000..fe690e16 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/053_hash_collision_in_interface.sol @@ -0,0 +1,6 @@ +contract test { + function gsf() public { } + function tgeo() public { } +} +// ---- +// TypeError: (0-78): Function signature hash collision for tgeo() diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/054_inheritance_basic.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/054_inheritance_basic.sol new file mode 100644 index 00000000..6229a1dc --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/054_inheritance_basic.sol @@ -0,0 +1,5 @@ +contract base { uint baseMember; struct BaseType { uint element; } } +contract derived is base { + BaseType data; + function f() public { baseMember = 7; } +} diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/055_inheritance_diamond_basic.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/055_inheritance_diamond_basic.sol new file mode 100644 index 00000000..c07e59e2 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/055_inheritance_diamond_basic.sol @@ -0,0 +1,9 @@ +contract root { function rootFunction() public {} } +contract inter1 is root { function f() public {} } +contract inter2 is root { function f() public {} } +contract derived is root, inter2, inter1 { + function g() public { f(); rootFunction(); } +} +// ---- +// Warning: (16-49): Function state mutability can be restricted to pure +// Warning: (129-151): Function state mutability can be restricted to pure diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/056_cyclic_inheritance.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/056_cyclic_inheritance.sol new file mode 100644 index 00000000..0e1ec4cb --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/056_cyclic_inheritance.sol @@ -0,0 +1,4 @@ +contract A is B { } +contract B is A { } +// ---- +// TypeError: (14-15): Definition of base has to precede definition of derived contract diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/057_legal_override_direct.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/057_legal_override_direct.sol new file mode 100644 index 00000000..062507ee --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/057_legal_override_direct.sol @@ -0,0 +1,6 @@ +contract B { function f() public {} } +contract C is B { function f(uint i) public {} } +// ---- +// Warning: (67-73): Unused function parameter. Remove or comment out the variable name to silence this warning. +// Warning: (13-35): Function state mutability can be restricted to pure +// Warning: (56-84): Function state mutability can be restricted to pure diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/058_legal_override_indirect.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/058_legal_override_indirect.sol new file mode 100644 index 00000000..f59da472 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/058_legal_override_indirect.sol @@ -0,0 +1,7 @@ +contract A { function f(uint a) public {} } +contract B { function f() public {} } +contract C is A, B { } +// ---- +// Warning: (24-30): Unused function parameter. Remove or comment out the variable name to silence this warning. +// Warning: (13-41): Function state mutability can be restricted to pure +// Warning: (57-79): Function state mutability can be restricted to pure diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/059_illegal_override_visibility.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/059_illegal_override_visibility.sol new file mode 100644 index 00000000..8c13a478 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/059_illegal_override_visibility.sol @@ -0,0 +1,4 @@ +contract B { function f() internal {} } +contract C is B { function f() public {} } +// ---- +// TypeError: (58-80): Overriding function visibility differs. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/060_complex_inheritance.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/060_complex_inheritance.sol new file mode 100644 index 00000000..c7e42238 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/060_complex_inheritance.sol @@ -0,0 +1,6 @@ +contract A { function f() public { uint8 x = C(0).g(); } } +contract B { function f() public {} function g() public returns (uint8) {} } +contract C is A, B { } +// ---- +// Warning: (35-42): Unused local variable. +// Warning: (95-133): Function state mutability can be restricted to pure diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/061_missing_base_constructor_arguments.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/061_missing_base_constructor_arguments.sol new file mode 100644 index 00000000..8ebb46aa --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/061_missing_base_constructor_arguments.sol @@ -0,0 +1,4 @@ +contract A { constructor(uint a) public { } } +contract B is A { } +// ---- +// Warning: (25-31): Unused function parameter. Remove or comment out the variable name to silence this warning. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/062_base_constructor_arguments_override.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/062_base_constructor_arguments_override.sol new file mode 100644 index 00000000..8ebb46aa --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/062_base_constructor_arguments_override.sol @@ -0,0 +1,4 @@ +contract A { constructor(uint a) public { } } +contract B is A { } +// ---- +// Warning: (25-31): Unused function parameter. Remove or comment out the variable name to silence this warning. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/063_implicit_derived_to_base_conversion.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/063_implicit_derived_to_base_conversion.sol new file mode 100644 index 00000000..f4667996 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/063_implicit_derived_to_base_conversion.sol @@ -0,0 +1,7 @@ +contract A { } +contract B is A { + function f() public { A a = B(1); } +} +// ---- +// Warning: (59-62): Unused local variable. +// Warning: (37-72): Function state mutability can be restricted to pure diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/064_implicit_base_to_derived_conversion.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/064_implicit_base_to_derived_conversion.sol new file mode 100644 index 00000000..0d23ea87 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/064_implicit_base_to_derived_conversion.sol @@ -0,0 +1,6 @@ +contract A { } +contract B is A { + function f() public { B b = A(1); } +} +// ---- +// TypeError: (59-69): Type contract A is not implicitly convertible to expected type contract B. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/065_super_excludes_current_contract.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/065_super_excludes_current_contract.sol new file mode 100644 index 00000000..544df1a5 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/065_super_excludes_current_contract.sol @@ -0,0 +1,11 @@ +contract A { + function b() public {} +} + +contract B is A { + function f() public { + super.f(); + } +} +// ---- +// TypeError: (95-102): Member "f" not found or not visible after argument-dependent lookup in contract super B. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/067_function_clash_with_state_variable_accessor.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/067_function_clash_with_state_variable_accessor.sol new file mode 100644 index 00000000..a99682c0 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/067_function_clash_with_state_variable_accessor.sol @@ -0,0 +1,9 @@ +contract test { + function fun() public { + uint64(2); + } + uint256 foo; + function foo() public {} +} +// ---- +// DeclarationError: (90-114): Identifier already declared. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/069_base_class_state_variable_accessor.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/069_base_class_state_variable_accessor.sol new file mode 100644 index 00000000..8f2c6438 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/069_base_class_state_variable_accessor.sol @@ -0,0 +1,9 @@ +// test for issue #1126 https://github.com/ethereum/cpp-ethereum/issues/1126 +contract Parent { + uint256 public m_aMember; +} +contract Child is Parent { + function foo() public returns (uint256) { return Parent.m_aMember; } +} +// ---- +// Warning: (158-226): Function state mutability can be restricted to pure diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/070_struct_accessor_one_array_only.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/070_struct_accessor_one_array_only.sol new file mode 100644 index 00000000..6741a7fa --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/070_struct_accessor_one_array_only.sol @@ -0,0 +1,6 @@ +contract test { + struct Data { uint[15] m_array; } + Data public data; +} +// ---- +// TypeError: (58-74): Internal or recursive type is not allowed for public state variables. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/071_base_class_state_variable_internal_member.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/071_base_class_state_variable_internal_member.sol new file mode 100644 index 00000000..774ea38e --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/071_base_class_state_variable_internal_member.sol @@ -0,0 +1,8 @@ +contract Parent { + uint256 internal m_aMember; +} +contract Child is Parent { + function foo() public returns (uint256) { return Parent.m_aMember; } +} +// ---- +// Warning: (83-151): Function state mutability can be restricted to pure diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/072_state_variable_member_of_wrong_class1.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/072_state_variable_member_of_wrong_class1.sol new file mode 100644 index 00000000..dd73ac47 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/072_state_variable_member_of_wrong_class1.sol @@ -0,0 +1,11 @@ +contract Parent1 { + uint256 internal m_aMember1; +} +contract Parent2 is Parent1 { + uint256 internal m_aMember2; +} +contract Child is Parent2 { + function foo() public returns (uint256) { return Parent2.m_aMember1; } +} +// ---- +// TypeError: (200-218): Member "m_aMember1" not found or not visible after argument-dependent lookup in type(contract Parent2). diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/073_state_variable_member_of_wrong_class2.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/073_state_variable_member_of_wrong_class2.sol new file mode 100644 index 00000000..f2de6e72 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/073_state_variable_member_of_wrong_class2.sol @@ -0,0 +1,12 @@ +contract Parent1 { + uint256 internal m_aMember1; +} +contract Parent2 is Parent1 { + uint256 internal m_aMember2; +} +contract Child is Parent2 { + function foo() public returns (uint256) { return Child.m_aMember2; } + uint256 public m_aMember3; +} +// ---- +// TypeError: (200-216): Member "m_aMember2" not found or not visible after argument-dependent lookup in type(contract Child). diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/074_fallback_function.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/074_fallback_function.sol new file mode 100644 index 00000000..466e80cb --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/074_fallback_function.sol @@ -0,0 +1,4 @@ +contract C { + uint x; + function() external { x = 2; } +} diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/075_fallback_function_with_arguments.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/075_fallback_function_with_arguments.sol new file mode 100644 index 00000000..68d40952 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/075_fallback_function_with_arguments.sol @@ -0,0 +1,6 @@ +contract C { + uint x; + function(uint a) external { x = 2; } +} +// ---- +// TypeError: (37-45): Fallback function cannot take parameters. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/076_fallback_function_in_library.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/076_fallback_function_in_library.sol new file mode 100644 index 00000000..25878a61 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/076_fallback_function_in_library.sol @@ -0,0 +1,5 @@ +library C { + function() external {} +} +// ---- +// TypeError: (16-38): Libraries cannot have fallback functions. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/077_fallback_function_with_return_parameters.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/077_fallback_function_with_return_parameters.sol new file mode 100644 index 00000000..3ff7a1c4 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/077_fallback_function_with_return_parameters.sol @@ -0,0 +1,5 @@ +contract C { + function() external returns (uint) { } +} +// ---- +// TypeError: (45-51): Fallback function cannot return values. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/078_fallback_function_twice.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/078_fallback_function_twice.sol new file mode 100644 index 00000000..e5746c63 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/078_fallback_function_twice.sol @@ -0,0 +1,7 @@ +contract C { + uint x; + function() external { x = 2; } + function() external { x = 3; } +} +// ---- +// DeclarationError: (64-94): Only one fallback function is allowed. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/079_fallback_function_inheritance.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/079_fallback_function_inheritance.sol new file mode 100644 index 00000000..c8c06c6e --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/079_fallback_function_inheritance.sol @@ -0,0 +1,7 @@ +contract A { + uint x; + function() external { x = 1; } +} +contract C is A { + function() external { x = 2; } +} diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/080_event.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/080_event.sol new file mode 100644 index 00000000..c5f9e4d0 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/080_event.sol @@ -0,0 +1,5 @@ +contract c { + event e(uint indexed a, bytes3 indexed s, bool indexed b); + function f() public { emit e(2, "abc", true); } +} +// ---- diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/081_event_too_many_indexed.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/081_event_too_many_indexed.sol new file mode 100644 index 00000000..ee0af605 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/081_event_too_many_indexed.sol @@ -0,0 +1,5 @@ +contract c { + event e(uint indexed a, bytes3 indexed b, bool indexed c, uint indexed d); +} +// ---- +// TypeError: (17-91): More than 3 indexed arguments for event. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/082_anonymous_event_four_indexed.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/082_anonymous_event_four_indexed.sol new file mode 100644 index 00000000..e8b36906 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/082_anonymous_event_four_indexed.sol @@ -0,0 +1,3 @@ +contract c { + event e(uint indexed a, bytes3 indexed b, bool indexed c, uint indexed d) anonymous; +} diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/083_anonymous_event_too_many_indexed.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/083_anonymous_event_too_many_indexed.sol new file mode 100644 index 00000000..d439c5b9 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/083_anonymous_event_too_many_indexed.sol @@ -0,0 +1,5 @@ +contract c { + event e(uint indexed a, bytes3 indexed b, bool indexed c, uint indexed d, uint indexed e) anonymous; +} +// ---- +// TypeError: (17-117): More than 4 indexed arguments for anonymous event. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/084_events_with_same_name.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/084_events_with_same_name.sol new file mode 100644 index 00000000..24f633b3 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/084_events_with_same_name.sol @@ -0,0 +1,4 @@ +contract TestIt { + event A(); + event A(uint i); +} diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/085_events_with_same_name_unnamed_arguments.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/085_events_with_same_name_unnamed_arguments.sol new file mode 100644 index 00000000..cccd9d57 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/085_events_with_same_name_unnamed_arguments.sol @@ -0,0 +1,4 @@ +contract test { + event A(uint); + event A(uint, uint); +} diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/086_events_with_same_name_different_types.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/086_events_with_same_name_different_types.sol new file mode 100644 index 00000000..fbeab711 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/086_events_with_same_name_different_types.sol @@ -0,0 +1,4 @@ +contract test { + event A(uint); + event A(bytes); +} diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/087_double_event_declaration.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/087_double_event_declaration.sol new file mode 100644 index 00000000..af0280c5 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/087_double_event_declaration.sol @@ -0,0 +1,6 @@ +contract test { + event A(uint i); + event A(uint i); +} +// ---- +// DeclarationError: (20-36): Event with same name and arguments defined twice. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/088_double_event_declaration_ignores_anonymous.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/088_double_event_declaration_ignores_anonymous.sol new file mode 100644 index 00000000..7d4b0ac9 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/088_double_event_declaration_ignores_anonymous.sol @@ -0,0 +1,6 @@ +contract test { + event A(uint i); + event A(uint i) anonymous; +} +// ---- +// DeclarationError: (20-36): Event with same name and arguments defined twice. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/089_double_event_declaration_ignores_indexed.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/089_double_event_declaration_ignores_indexed.sol new file mode 100644 index 00000000..e6aa3e5f --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/089_double_event_declaration_ignores_indexed.sol @@ -0,0 +1,6 @@ +contract test { + event A(uint i); + event A(uint indexed i); +} +// ---- +// DeclarationError: (20-36): Event with same name and arguments defined twice. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/090_event_call.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/090_event_call.sol new file mode 100644 index 00000000..8cf50597 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/090_event_call.sol @@ -0,0 +1,5 @@ +contract c { + event e(uint a, bytes3 indexed s, bool indexed b); + function f() public { emit e(2, "abc", true); } +} +// ---- diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/091_event_function_inheritance_clash.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/091_event_function_inheritance_clash.sol new file mode 100644 index 00000000..5e0f58ea --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/091_event_function_inheritance_clash.sol @@ -0,0 +1,12 @@ +contract A { + function dup() public returns (uint) { + return 1; + } +} +contract B { + event dup(); +} +contract C is A, B { +} +// ---- +// DeclarationError: (99-111): Identifier already declared. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/092_function_event_inheritance_clash.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/092_function_event_inheritance_clash.sol new file mode 100644 index 00000000..c567f992 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/092_function_event_inheritance_clash.sol @@ -0,0 +1,12 @@ +contract B { + event dup(); +} +contract A { + function dup() public returns (uint) { + return 1; + } +} +contract C is B, A { +} +// ---- +// DeclarationError: (49-111): Identifier already declared. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/093_function_event_in_contract_clash.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/093_function_event_in_contract_clash.sol new file mode 100644 index 00000000..7b4fcde9 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/093_function_event_in_contract_clash.sol @@ -0,0 +1,8 @@ +contract A { + event dup(); + function dup() public returns (uint) { + return 1; + } +} +// ---- +// DeclarationError: (34-96): Identifier already declared. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/094_event_inheritance.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/094_event_inheritance.sol new file mode 100644 index 00000000..b13d5755 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/094_event_inheritance.sol @@ -0,0 +1,7 @@ +contract base { + event e(uint a, bytes3 indexed s, bool indexed b); +} +contract c is base { + function f() public { emit e(2, "abc", true); } +} +// ---- diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/095_multiple_events_argument_clash.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/095_multiple_events_argument_clash.sol new file mode 100644 index 00000000..79127119 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/095_multiple_events_argument_clash.sol @@ -0,0 +1,4 @@ +contract c { + event e1(uint a, uint e1, uint e2); + event e2(uint a, uint e1, uint e2); +} diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/096_access_to_default_function_visibility.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/096_access_to_default_function_visibility.sol new file mode 100644 index 00000000..9251df73 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/096_access_to_default_function_visibility.sol @@ -0,0 +1,8 @@ +contract c { + function f() public {} +} +contract d { + function g() public { c(0).f(); } +} +// ---- +// Warning: (17-39): Function state mutability can be restricted to pure diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/097_access_to_internal_function.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/097_access_to_internal_function.sol new file mode 100644 index 00000000..60d7b758 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/097_access_to_internal_function.sol @@ -0,0 +1,8 @@ +contract c { + function f() internal {} +} +contract d { + function g() public { c(0).f(); } +} +// ---- +// TypeError: (83-89): Member "f" not found or not visible after argument-dependent lookup in contract c. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/098_access_to_default_state_variable_visibility.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/098_access_to_default_state_variable_visibility.sol new file mode 100644 index 00000000..8c9d0c0f --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/098_access_to_default_state_variable_visibility.sol @@ -0,0 +1,8 @@ +contract c { + uint a; +} +contract d { + function g() public { c(0).a(); } +} +// ---- +// TypeError: (66-72): Member "a" not found or not visible after argument-dependent lookup in contract c. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/099_access_to_internal_state_variable.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/099_access_to_internal_state_variable.sol new file mode 100644 index 00000000..60aba574 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/099_access_to_internal_state_variable.sol @@ -0,0 +1,8 @@ +contract c { + uint public a; +} +contract d { + function g() public { c(0).a(); } +} +// ---- +// Warning: (51-84): Function state mutability can be restricted to view diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/100_error_count_in_named_args.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/100_error_count_in_named_args.sol new file mode 100644 index 00000000..a679c25a --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/100_error_count_in_named_args.sol @@ -0,0 +1,11 @@ +contract test { + function a(uint a, uint b) public returns (uint r) { + r = a + b; + } + function b() public returns (uint r) { + r = a({a: 1}); + } +} +// ---- +// Warning: (31-37): This declaration shadows an existing declaration. +// TypeError: (153-162): Wrong argument count for function call: 1 arguments given but expected 2. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/101_empty_in_named_args.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/101_empty_in_named_args.sol new file mode 100644 index 00000000..9da11d6f --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/101_empty_in_named_args.sol @@ -0,0 +1,11 @@ +contract test { + function a(uint a, uint b) public returns (uint r) { + r = a + b; + } + function b() public returns (uint r) { + r = a({}); + } +} +// ---- +// Warning: (31-37): This declaration shadows an existing declaration. +// TypeError: (153-158): Wrong argument count for function call: 0 arguments given but expected 2. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/102_duplicate_parameter_names_in_named_args.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/102_duplicate_parameter_names_in_named_args.sol new file mode 100644 index 00000000..88402fa3 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/102_duplicate_parameter_names_in_named_args.sol @@ -0,0 +1,11 @@ +contract test { + function a(uint a, uint b) public returns (uint r) { + r = a + b; + } + function b() public returns (uint r) { + r = a({a: 1, a: 2}); + } +} +// ---- +// Warning: (31-37): This declaration shadows an existing declaration. +// TypeError: (159-160): Duplicate named argument "a". diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/103_invalid_parameter_names_in_named_args.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/103_invalid_parameter_names_in_named_args.sol new file mode 100644 index 00000000..bed15186 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/103_invalid_parameter_names_in_named_args.sol @@ -0,0 +1,11 @@ +contract test { + function a(uint a, uint b) public returns (uint r) { + r = a + b; + } + function b() public returns (uint r) { + r = a({a: 1, c: 2}); + } +} +// ---- +// Warning: (31-37): This declaration shadows an existing declaration. +// TypeError: (153-168): Named argument "c" does not match function declaration. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/104_empty_name_input_parameter.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/104_empty_name_input_parameter.sol new file mode 100644 index 00000000..824543ef --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/104_empty_name_input_parameter.sol @@ -0,0 +1,5 @@ +contract test { + function f(uint) public { } +} +// ---- +// Warning: (20-47): Function state mutability can be restricted to pure diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/105_constant_input_parameter.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/105_constant_input_parameter.sol new file mode 100644 index 00000000..ba05fcb3 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/105_constant_input_parameter.sol @@ -0,0 +1,7 @@ +contract test { + function f(uint[] memory constant a) public { } +} +// ---- +// DeclarationError: (31-55): The "constant" keyword can only be used for state variables. +// TypeError: (31-55): Constants of non-value type not yet implemented. +// TypeError: (31-55): Uninitialized "constant" variable. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/106_empty_name_return_parameter.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/106_empty_name_return_parameter.sol new file mode 100644 index 00000000..a2ffc6e1 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/106_empty_name_return_parameter.sol @@ -0,0 +1,5 @@ +contract test { + function f() public returns (bool) { } +} +// ---- +// Warning: (20-58): Function state mutability can be restricted to pure diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/107_empty_name_input_parameter_with_named_one.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/107_empty_name_input_parameter_with_named_one.sol new file mode 100644 index 00000000..e0efa0a0 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/107_empty_name_input_parameter_with_named_one.sol @@ -0,0 +1,7 @@ +contract test { + function f(uint, uint k) public returns (uint ret_k) { + return k; + } +} +// ---- +// Warning: (20-98): Function state mutability can be restricted to pure diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/108_empty_name_return_parameter_with_named_one.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/108_empty_name_return_parameter_with_named_one.sol new file mode 100644 index 00000000..39ae7877 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/108_empty_name_return_parameter_with_named_one.sol @@ -0,0 +1,7 @@ +contract test { + function f() public returns (uint ret_k, uint) { + return 5; + } +} +// ---- +// TypeError: (77-85): Different number of arguments in return statement than in returns declaration. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/110_no_overflow_with_large_literal.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/110_no_overflow_with_large_literal.sol new file mode 100644 index 00000000..9b36fa70 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/110_no_overflow_with_large_literal.sol @@ -0,0 +1,7 @@ +contract c { + constructor() public { + a = 115792089237316195423570985008687907853269984665640564039458; + } + uint256 a; +} +// ---- diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/111_overflow_caused_by_ether_units.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/111_overflow_caused_by_ether_units.sol new file mode 100644 index 00000000..dc4cab8a --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/111_overflow_caused_by_ether_units.sol @@ -0,0 +1,8 @@ +contract c { + constructor() public { + a = 115792089237316195423570985008687907853269984665640564039458 ether; + } + uint256 a; +} +// ---- +// TypeError: (52-118): Type int_const 1157...(70 digits omitted)...0000 is not implicitly convertible to expected type uint256. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/112_exp_operator_exponent_too_big.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/112_exp_operator_exponent_too_big.sol new file mode 100644 index 00000000..2a9e6204 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/112_exp_operator_exponent_too_big.sol @@ -0,0 +1,5 @@ +contract test { + function f() public returns (uint d) { return 2 ** 10000000000; } +} +// ---- +// TypeError: (66-82): Operator ** not compatible with types int_const 2 and int_const 10000000000 diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/113_exp_warn_literal_base_1.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/113_exp_warn_literal_base_1.sol new file mode 100644 index 00000000..0d91fcab --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/113_exp_warn_literal_base_1.sol @@ -0,0 +1,8 @@ +contract test { + function f() pure public returns(uint) { + uint8 x = 100; + return 10**x; + } +} +// ---- +// Warning: (99-104): Result of exponentiation has type uint8 and thus might overflow. Silence this warning by converting the literal to the expected type. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/114_exp_warn_literal_base_2.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/114_exp_warn_literal_base_2.sol new file mode 100644 index 00000000..eb430b9a --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/114_exp_warn_literal_base_2.sol @@ -0,0 +1,6 @@ +contract test { + function f() pure public returns(uint) { + uint8 x = 100; + return uint8(10)**x; + } +} diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/115_exp_warn_literal_base_3.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/115_exp_warn_literal_base_3.sol new file mode 100644 index 00000000..01c0fc06 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/115_exp_warn_literal_base_3.sol @@ -0,0 +1,5 @@ +contract test { + function f() pure public returns(uint) { + return 2**80; + } +} diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/116_shift_warn_literal_base_1.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/116_shift_warn_literal_base_1.sol new file mode 100644 index 00000000..c6a4052e --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/116_shift_warn_literal_base_1.sol @@ -0,0 +1,8 @@ +contract test { + function f() pure public returns(uint) { + uint8 x = 100; + return 10 << x; + } +} +// ---- +// Warning: (99-106): Result of shift has type uint8 and thus might overflow. Silence this warning by converting the literal to the expected type. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/117_shift_warn_literal_base_2.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/117_shift_warn_literal_base_2.sol new file mode 100644 index 00000000..954d1943 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/117_shift_warn_literal_base_2.sol @@ -0,0 +1,6 @@ +contract test { + function f() pure public returns(uint) { + uint8 x = 100; + return uint8(10) << x; + } +} diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/118_shift_warn_literal_base_3.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/118_shift_warn_literal_base_3.sol new file mode 100644 index 00000000..5fbaa806 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/118_shift_warn_literal_base_3.sol @@ -0,0 +1,5 @@ +contract test { + function f() pure public returns(uint) { + return 2 << 80; + } +} diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/119_shift_warn_literal_base_4.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/119_shift_warn_literal_base_4.sol new file mode 100644 index 00000000..19869157 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/119_shift_warn_literal_base_4.sol @@ -0,0 +1,6 @@ +contract test { + function f() pure public returns(uint) { + uint8 x = 100; + return 10 >> x; + } +} diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/124_enum_member_access.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/124_enum_member_access.sol new file mode 100644 index 00000000..98bc8e66 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/124_enum_member_access.sol @@ -0,0 +1,8 @@ +contract test { + enum ActionChoices { GoLeft, GoRight, GoStraight, Sit } + constructor() public + { + choices = ActionChoices.GoStraight; + } + ActionChoices choices; +} diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/125_enum_member_access_accross_contracts.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/125_enum_member_access_accross_contracts.sol new file mode 100644 index 00000000..3bed62d6 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/125_enum_member_access_accross_contracts.sol @@ -0,0 +1,10 @@ +contract Interface { + enum MyEnum { One, Two } +} +contract Impl { + function test() public returns (Interface.MyEnum) { + return Interface.MyEnum.One; + } +} +// ---- +// Warning: (72-166): Function state mutability can be restricted to pure diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/126_enum_invalid_member_access.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/126_enum_invalid_member_access.sol new file mode 100644 index 00000000..e58ed160 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/126_enum_invalid_member_access.sol @@ -0,0 +1,9 @@ +contract test { + enum ActionChoices { GoLeft, GoRight, GoStraight, Sit } + constructor() public { + choices = ActionChoices.RunAroundWavingYourHands; + } + ActionChoices choices; +} +// ---- +// TypeError: (121-159): Member "RunAroundWavingYourHands" not found or not visible after argument-dependent lookup in type(enum test.ActionChoices). diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/127_enum_invalid_direct_member_access.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/127_enum_invalid_direct_member_access.sol new file mode 100644 index 00000000..68510a0a --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/127_enum_invalid_direct_member_access.sol @@ -0,0 +1,9 @@ +contract test { + enum ActionChoices { GoLeft, GoRight, GoStraight, Sit } + constructor() public { + choices = Sit; + } + ActionChoices choices; +} +// ---- +// DeclarationError: (121-124): Undeclared identifier. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/128_enum_explicit_conversion_is_okay.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/128_enum_explicit_conversion_is_okay.sol new file mode 100644 index 00000000..0948d550 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/128_enum_explicit_conversion_is_okay.sol @@ -0,0 +1,10 @@ +contract test { + enum ActionChoices { GoLeft, GoRight, GoStraight, Sit } + constructor() public { + a = uint256(ActionChoices.GoStraight); + b = uint64(ActionChoices.Sit); + } + uint256 a; + uint64 b; +} +// ---- diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/129_int_to_enum_explicit_conversion_is_okay.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/129_int_to_enum_explicit_conversion_is_okay.sol new file mode 100644 index 00000000..2639decf --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/129_int_to_enum_explicit_conversion_is_okay.sol @@ -0,0 +1,10 @@ +contract test { + enum ActionChoices { GoLeft, GoRight, GoStraight, Sit } + constructor() public { + a = 2; + b = ActionChoices(a); + } + uint256 a; + ActionChoices b; +} +// ---- diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/130_enum_implicit_conversion_is_not_okay_256.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/130_enum_implicit_conversion_is_not_okay_256.sol new file mode 100644 index 00000000..01c5e93f --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/130_enum_implicit_conversion_is_not_okay_256.sol @@ -0,0 +1,9 @@ +contract test { + enum ActionChoices { GoLeft, GoRight, GoStraight, Sit } + constructor() public { + a = ActionChoices.GoStraight; + } + uint256 a; +} +// ---- +// TypeError: (115-139): Type enum test.ActionChoices is not implicitly convertible to expected type uint256. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/131_enum_implicit_conversion_is_not_okay_64.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/131_enum_implicit_conversion_is_not_okay_64.sol new file mode 100644 index 00000000..4e21b9aa --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/131_enum_implicit_conversion_is_not_okay_64.sol @@ -0,0 +1,9 @@ +contract test { + enum ActionChoices { GoLeft, GoRight, GoStraight, Sit } + constructor() public { + b = ActionChoices.Sit; + } + uint64 b; +} +// ---- +// TypeError: (115-132): Type enum test.ActionChoices is not implicitly convertible to expected type uint64. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/132_enum_to_enum_conversion_is_not_okay.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/132_enum_to_enum_conversion_is_not_okay.sol new file mode 100644 index 00000000..5b9ba813 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/132_enum_to_enum_conversion_is_not_okay.sol @@ -0,0 +1,9 @@ +contract test { + enum Paper { Up, Down, Left, Right } + enum Ground { North, South, West, East } + constructor() public { + Ground(Paper.Up); + } +} +// ---- +// TypeError: (137-153): Explicit type conversion not allowed from "enum test.Paper" to "enum test.Ground". diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/133_enum_duplicate_values.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/133_enum_duplicate_values.sol new file mode 100644 index 00000000..996a9b78 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/133_enum_duplicate_values.sol @@ -0,0 +1,5 @@ + contract test { + enum ActionChoices { GoLeft, GoRight, GoLeft, Sit } + } +// ---- +// DeclarationError: (66-72): Identifier already declared. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/134_enum_name_resolution_under_current_contract_name.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/134_enum_name_resolution_under_current_contract_name.sol new file mode 100644 index 00000000..4a16eee1 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/134_enum_name_resolution_under_current_contract_name.sol @@ -0,0 +1,12 @@ +contract A { + enum Foo { + First, + Second + } + + function a() public { + A.Foo; + } +} +// ---- +// Warning: (69-111): Function state mutability can be restricted to pure diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/135_private_visibility.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/135_private_visibility.sol new file mode 100644 index 00000000..faafc631 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/135_private_visibility.sol @@ -0,0 +1,8 @@ +contract base { + function f() private {} +} +contract derived is base { + function g() public { f(); } +} +// ---- +// DeclarationError: (99-100): Undeclared identifier. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/136_private_visibility_via_explicit_base_access.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/136_private_visibility_via_explicit_base_access.sol new file mode 100644 index 00000000..2f94ef92 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/136_private_visibility_via_explicit_base_access.sol @@ -0,0 +1,8 @@ +contract base { + function f() private {} +} +contract derived is base { + function g() public { base.f(); } +} +// ---- +// TypeError: (99-105): Member "f" not found or not visible after argument-dependent lookup in type(contract base). diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/137_external_visibility.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/137_external_visibility.sol new file mode 100644 index 00000000..214ad60a --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/137_external_visibility.sol @@ -0,0 +1,6 @@ +contract c { + function f() external {} + function g() public { f(); } +} +// ---- +// DeclarationError: (68-69): Undeclared identifier. "f" is not (or not yet) visible at this point. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/138_similar_name_suggestions_expected.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/138_similar_name_suggestions_expected.sol new file mode 100644 index 00000000..ef6e933a --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/138_similar_name_suggestions_expected.sol @@ -0,0 +1,6 @@ +contract c { + function func() public {} + function g() public { fun(); } +} +// ---- +// DeclarationError: (69-72): Undeclared identifier. Did you mean "func"? diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/139_no_name_suggestion.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/139_no_name_suggestion.sol new file mode 100644 index 00000000..40827dca --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/139_no_name_suggestion.sol @@ -0,0 +1,5 @@ +contract c { + function g() public { fun(); } +} +// ---- +// DeclarationError: (39-42): Undeclared identifier. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/140_multiple_similar_suggestions.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/140_multiple_similar_suggestions.sol new file mode 100644 index 00000000..34b4604d --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/140_multiple_similar_suggestions.sol @@ -0,0 +1,11 @@ +contract c { + function g() public { + uint var1 = 1; + uint var2 = 1; + uint var3 = 1; + uint var4 = 1; + uint var5 = varx; + } +} +// ---- +// DeclarationError: (151-155): Undeclared identifier. Did you mean "var1", "var2", "var3", "var4" or "var5"? diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/141_multiple_scopes_suggestions.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/141_multiple_scopes_suggestions.sol new file mode 100644 index 00000000..f9471146 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/141_multiple_scopes_suggestions.sol @@ -0,0 +1,9 @@ +contract c { + uint log9 = 2; + function g() public { + uint log8 = 3; + uint var1 = lgox; + } +} +// ---- +// DeclarationError: (101-105): Undeclared identifier. Did you mean "log8", "log9", "log0", "log1", "log2", "log3" or "log4"? diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/142_inheritence_suggestions.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/142_inheritence_suggestions.sol new file mode 100644 index 00000000..4231e1bd --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/142_inheritence_suggestions.sol @@ -0,0 +1,8 @@ +contract a { function func() public {} } +contract c is a { + function g() public { + uint var1 = fun(); + } +} +// ---- +// DeclarationError: (105-108): Undeclared identifier. Did you mean "func"? diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/143_no_spurious_identifier_suggestions_with_submatch.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/143_no_spurious_identifier_suggestions_with_submatch.sol new file mode 100644 index 00000000..db9f07c6 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/143_no_spurious_identifier_suggestions_with_submatch.sol @@ -0,0 +1,8 @@ +contract c { + function g() public { + uint va = 1; + uint vb = vaxyz; + } +} +// ---- +// DeclarationError: (78-83): Undeclared identifier. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/144_no_spurious_identifier_suggestions.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/144_no_spurious_identifier_suggestions.sol new file mode 100644 index 00000000..2316cb3d --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/144_no_spurious_identifier_suggestions.sol @@ -0,0 +1,8 @@ +contract c { + function g() public { + uint va = 1; + uint vb = x; + } +} +// ---- +// DeclarationError: (78-79): Undeclared identifier. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/145_external_base_visibility.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/145_external_base_visibility.sol new file mode 100644 index 00000000..cf680462 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/145_external_base_visibility.sol @@ -0,0 +1,8 @@ +contract base { + function f() external {} +} +contract derived is base { + function g() public { base.f(); } +} +// ---- +// TypeError: (100-106): Member "f" not found or not visible after argument-dependent lookup in type(contract base). diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/146_external_argument_assign.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/146_external_argument_assign.sol new file mode 100644 index 00000000..d2c0245c --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/146_external_argument_assign.sol @@ -0,0 +1,5 @@ +contract c { + function f(uint a) external { a = 1; } +} +// ---- +// TypeError: (47-48): Expression has to be an lvalue. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/147_external_argument_increment.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/147_external_argument_increment.sol new file mode 100644 index 00000000..2bfba42b --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/147_external_argument_increment.sol @@ -0,0 +1,5 @@ +contract c { + function f(uint a) external { a++; } +} +// ---- +// TypeError: (47-48): Expression has to be an lvalue. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/148_external_argument_delete.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/148_external_argument_delete.sol new file mode 100644 index 00000000..30eb204e --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/148_external_argument_delete.sol @@ -0,0 +1,5 @@ +contract c { + function f(uint a) external { delete a; } +} +// ---- +// TypeError: (54-55): Expression has to be an lvalue. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/149_test_for_bug_override_function_with_bytearray_type.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/149_test_for_bug_override_function_with_bytearray_type.sol new file mode 100644 index 00000000..bc1c4267 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/149_test_for_bug_override_function_with_bytearray_type.sol @@ -0,0 +1,8 @@ +contract Vehicle { + function f(bytes calldata) external returns (uint256 r) {r = 1;} +} +contract Bike is Vehicle { + function f(bytes calldata) external returns (uint256 r) {r = 42;} +} +// ---- +// Warning: (23-87): Function state mutability can be restricted to pure diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/150_array_with_nonconstant_length.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/150_array_with_nonconstant_length.sol new file mode 100644 index 00000000..49a1851c --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/150_array_with_nonconstant_length.sol @@ -0,0 +1,5 @@ +contract c { + function f(uint a) public { uint8[a] x; } +} +// ---- +// TypeError: (51-52): Invalid array length, expected integer literal or constant expression. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/151_array_with_negative_length.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/151_array_with_negative_length.sol new file mode 100644 index 00000000..b87160b0 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/151_array_with_negative_length.sol @@ -0,0 +1,5 @@ +contract c { + function f(uint a) public { uint8[-1] x; } +} +// ---- +// TypeError: (51-53): Array with negative length specified. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/152_array_copy_with_different_types1.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/152_array_copy_with_different_types1.sol new file mode 100644 index 00000000..a0e71847 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/152_array_copy_with_different_types1.sol @@ -0,0 +1,7 @@ +contract c { + bytes a; + uint[] b; + function f() public { b = a; } +} +// ---- +// TypeError: (70-71): Type bytes storage ref is not implicitly convertible to expected type uint256[] storage ref. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/153_array_copy_with_different_types2.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/153_array_copy_with_different_types2.sol new file mode 100644 index 00000000..8d1cb1ef --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/153_array_copy_with_different_types2.sol @@ -0,0 +1,7 @@ +contract c { + uint32[] a; + uint8[] b; + function f() public { b = a; } +} +// ---- +// TypeError: (74-75): Type uint32[] storage ref is not implicitly convertible to expected type uint8[] storage ref. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/154_array_copy_with_different_types_conversion_possible.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/154_array_copy_with_different_types_conversion_possible.sol new file mode 100644 index 00000000..b15a9350 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/154_array_copy_with_different_types_conversion_possible.sol @@ -0,0 +1,5 @@ +contract c { + uint32[] a; + uint8[] b; + function f() public { a = b; } +} diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/155_array_copy_with_different_types_static_dynamic.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/155_array_copy_with_different_types_static_dynamic.sol new file mode 100644 index 00000000..025593a5 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/155_array_copy_with_different_types_static_dynamic.sol @@ -0,0 +1,5 @@ +contract c { + uint32[] a; + uint8[80] b; + function f() public { a = b; } +} diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/156_array_copy_with_different_types_dynamic_static.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/156_array_copy_with_different_types_dynamic_static.sol new file mode 100644 index 00000000..90aa53a0 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/156_array_copy_with_different_types_dynamic_static.sol @@ -0,0 +1,7 @@ +contract c { + uint[] a; + uint[80] b; + function f() public { b = a; } +} +// ---- +// TypeError: (73-74): Type uint256[] storage ref is not implicitly convertible to expected type uint256[80] storage ref. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/157_array_of_undeclared_type.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/157_array_of_undeclared_type.sol new file mode 100644 index 00000000..1409db5e --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/157_array_of_undeclared_type.sol @@ -0,0 +1,5 @@ +contract c { + a[] public foo; +} +// ---- +// DeclarationError: (17-18): Identifier not found or not unique. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/158_storage_variable_initialization_with_incorrect_type_int.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/158_storage_variable_initialization_with_incorrect_type_int.sol new file mode 100644 index 00000000..b1ef153e --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/158_storage_variable_initialization_with_incorrect_type_int.sol @@ -0,0 +1,5 @@ +contract c { + uint8 a = 1000; +} +// ---- +// TypeError: (27-31): Type int_const 1000 is not implicitly convertible to expected type uint8. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/159_storage_variable_initialization_with_incorrect_type_string.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/159_storage_variable_initialization_with_incorrect_type_string.sol new file mode 100644 index 00000000..75736d98 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/159_storage_variable_initialization_with_incorrect_type_string.sol @@ -0,0 +1,5 @@ +contract c { + uint a = "abc"; +} +// ---- +// TypeError: (26-31): Type literal_string "abc" is not implicitly convertible to expected type uint256. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/160_test_byte_is_alias_of_byte1.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/160_test_byte_is_alias_of_byte1.sol new file mode 100644 index 00000000..9977c839 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/160_test_byte_is_alias_of_byte1.sol @@ -0,0 +1,7 @@ +contract c { + bytes arr; + function f() public { byte a = arr[0];} +} +// ---- +// Warning: (54-60): Unused local variable. +// Warning: (32-71): Function state mutability can be restricted to view diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/164_assigning_value_to_const_variable.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/164_assigning_value_to_const_variable.sol new file mode 100644 index 00000000..4e543e70 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/164_assigning_value_to_const_variable.sol @@ -0,0 +1,6 @@ +contract Foo { + function changeIt() public { x = 9; } + uint constant x = 56; +} +// ---- +// TypeError: (48-49): Cannot assign to a constant variable. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/165_assigning_state_to_const_variable.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/165_assigning_state_to_const_variable.sol new file mode 100644 index 00000000..0de15dfb --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/165_assigning_state_to_const_variable.sol @@ -0,0 +1,5 @@ +contract C { + address constant x = msg.sender; +} +// ---- +// TypeError: (38-48): Initial value for constant variable has to be compile-time constant. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/167_constant_string_literal_disallows_assignment.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/167_constant_string_literal_disallows_assignment.sol new file mode 100644 index 00000000..3f19ea3b --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/167_constant_string_literal_disallows_assignment.sol @@ -0,0 +1,10 @@ +contract Test { + string constant x = "abefghijklmnopqabcdefghijklmnopqabcdefghijklmnopqabca"; + function f() public { + // Even if this is made possible in the future, we should not allow assignment + // to elements of constant arrays. + x[0] = "f"; + } +} +// ---- +// TypeError: (261-265): Index access for string is not possible. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/168_assignment_to_const_var_involving_conversion.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/168_assignment_to_const_var_involving_conversion.sol new file mode 100644 index 00000000..fb31e199 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/168_assignment_to_const_var_involving_conversion.sol @@ -0,0 +1,3 @@ +contract C { + C constant x = C(0x123); +} diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/169_assignment_to_const_var_involving_expression.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/169_assignment_to_const_var_involving_expression.sol new file mode 100644 index 00000000..692aad9f --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/169_assignment_to_const_var_involving_expression.sol @@ -0,0 +1,3 @@ +contract C { + uint constant x = 0x123 + 0x456; +} diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/170_assignment_to_const_var_involving_keccak.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/170_assignment_to_const_var_involving_keccak.sol new file mode 100644 index 00000000..54f022bb --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/170_assignment_to_const_var_involving_keccak.sol @@ -0,0 +1,3 @@ +contract C { + bytes32 constant x = keccak256("abc"); +} diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/171_assignment_to_const_array_vars.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/171_assignment_to_const_array_vars.sol new file mode 100644 index 00000000..b9e9aa7a --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/171_assignment_to_const_array_vars.sol @@ -0,0 +1,5 @@ +contract C { + uint[3] constant x = [uint(1), 2, 3]; +} +// ---- +// TypeError: (17-53): Constants of non-value type not yet implemented. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/172_assignment_to_const_string_bytes.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/172_assignment_to_const_string_bytes.sol new file mode 100644 index 00000000..f0e1528c --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/172_assignment_to_const_string_bytes.sol @@ -0,0 +1,5 @@ +contract C { + bytes constant a = "\x00\x01\x02"; + bytes constant b = hex"000102"; + string constant c = "hello"; +} diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/173_constant_struct.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/173_constant_struct.sol new file mode 100644 index 00000000..07bf0439 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/173_constant_struct.sol @@ -0,0 +1,6 @@ +contract C { + struct S { uint x; uint[] y; } + S constant x = S(5, new uint[](4)); +} +// ---- +// TypeError: (52-86): Constants of non-value type not yet implemented. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/174_address_is_constant.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/174_address_is_constant.sol new file mode 100644 index 00000000..10850e16 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/174_address_is_constant.sol @@ -0,0 +1,3 @@ +contract C { + address constant x = 0x1212121212121212121212121212121212121212; +} diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/175_uninitialized_const_variable.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/175_uninitialized_const_variable.sol new file mode 100644 index 00000000..13496d8b --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/175_uninitialized_const_variable.sol @@ -0,0 +1,5 @@ +contract Foo { + uint constant y; +} +// ---- +// TypeError: (19-34): Uninitialized "constant" variable. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/176_overloaded_function_cannot_resolve.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/176_overloaded_function_cannot_resolve.sol new file mode 100644 index 00000000..bcf25948 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/176_overloaded_function_cannot_resolve.sol @@ -0,0 +1,7 @@ +contract test { + function f() public returns (uint) { return 1; } + function f(uint a) public returns (uint) { return a; } + function g() public returns (uint) { return f(3, 5); } +} +// ---- +// TypeError: (176-177): No matching declaration found after argument-dependent lookup. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/177_ambiguous_overloaded_function.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/177_ambiguous_overloaded_function.sol new file mode 100644 index 00000000..759e02f2 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/177_ambiguous_overloaded_function.sol @@ -0,0 +1,8 @@ +contract test { + function f(uint8 a) public returns (uint) { return a; } + function f(uint a) public returns (uint) { return 2 * a; } + // literal 1 can be both converted to uint and uint8, so the call is ambiguous. + function g() public returns (uint) { return f(1); } +} +// ---- +// TypeError: (271-272): No unique declaration found after argument-dependent lookup. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/178_assignment_of_nonoverloaded_function.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/178_assignment_of_nonoverloaded_function.sol new file mode 100644 index 00000000..07fc1c43 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/178_assignment_of_nonoverloaded_function.sol @@ -0,0 +1,6 @@ +contract test { + function f(uint a) public returns (uint) { return 2 * a; } + function g() public returns (uint) { function (uint) returns (uint) x = f; return x(7); } +} +// ---- +// Warning: (20-78): Function state mutability can be restricted to pure diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/179_assignment_of_overloaded_function.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/179_assignment_of_overloaded_function.sol new file mode 100644 index 00000000..9ed864f1 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/179_assignment_of_overloaded_function.sol @@ -0,0 +1,7 @@ +contract test { + function f() public returns (uint) { return 1; } + function f(uint a) public returns (uint) { return 2 * a; } + function g() public returns (uint) { function (uint) returns (uint) x = f; return x(7); } +} +// ---- +// TypeError: (208-209): No matching declaration found after variable lookup. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/180_external_types_clash.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/180_external_types_clash.sol new file mode 100644 index 00000000..91ddcd9b --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/180_external_types_clash.sol @@ -0,0 +1,9 @@ +contract base { + enum a { X } + function f(a) public { } +} +contract test is base { + function f(uint8 a) public { } +} +// ---- +// TypeError: (37-61): Function overload clash during conversion to external types for arguments. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/181_override_changes_return_types.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/181_override_changes_return_types.sol new file mode 100644 index 00000000..c887f259 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/181_override_changes_return_types.sol @@ -0,0 +1,8 @@ +contract base { + function f(uint a) public returns (uint) { } +} +contract test is base { + function f(uint a) public returns (uint8) { } +} +// ---- +// TypeError: (95-140): Overriding function return types differ. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/182_equal_overload.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/182_equal_overload.sol new file mode 100644 index 00000000..cb9eb3fa --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/182_equal_overload.sol @@ -0,0 +1,7 @@ +contract C { + function test(uint a) public returns (uint b) { } + function test(uint a) external {} +} +// ---- +// DeclarationError: (17-66): Function with same name and arguments defined twice. +// TypeError: (17-66): Overriding function visibility differs. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/185_invalid_utf8_implicit.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/185_invalid_utf8_implicit.sol new file mode 100644 index 00000000..5440b425 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/185_invalid_utf8_implicit.sol @@ -0,0 +1,5 @@ +contract C { + string s = "\xa0\x00"; +} +// ---- +// TypeError: (28-38): Type literal_string (contains invalid UTF-8 sequence at position 0) is not implicitly convertible to expected type string storage ref. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/186_invalid_utf8_explicit.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/186_invalid_utf8_explicit.sol new file mode 100644 index 00000000..0f67460f --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/186_invalid_utf8_explicit.sol @@ -0,0 +1,5 @@ +contract C { + string s = string("\xa0\x00"); +} +// ---- +// TypeError: (28-46): Explicit type conversion not allowed from "literal_string (contains invalid UTF-8 sequence at position 0)" to "string memory". diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/187_large_utf8_codepoint.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/187_large_utf8_codepoint.sol new file mode 100644 index 00000000..5e406d0c --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/187_large_utf8_codepoint.sol @@ -0,0 +1,3 @@ +contract C { + string s = "\xf0\x9f\xa6\x84"; +} diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/188_string_index.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/188_string_index.sol new file mode 100644 index 00000000..9d51e06b --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/188_string_index.sol @@ -0,0 +1,6 @@ +contract C { + string s; + function f() public { bytes1 a = s[2]; } +} +// ---- +// TypeError: (64-68): Index access for string is not possible. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/189_string_length.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/189_string_length.sol new file mode 100644 index 00000000..845b9156 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/189_string_length.sol @@ -0,0 +1,6 @@ +contract C { + string s; + function f() public { uint a = s.length; } +} +// ---- +// TypeError: (62-70): Member "length" not found or not visible after argument-dependent lookup in string storage ref. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/190_negative_integers_to_signed_out_of_bound.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/190_negative_integers_to_signed_out_of_bound.sol new file mode 100644 index 00000000..2e8503af --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/190_negative_integers_to_signed_out_of_bound.sol @@ -0,0 +1,5 @@ +contract test { + int8 public i = -129; +} +// ---- +// TypeError: (36-40): Type int_const -129 is not implicitly convertible to expected type int8. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/191_negative_integers_to_signed_min.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/191_negative_integers_to_signed_min.sol new file mode 100644 index 00000000..211cfee2 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/191_negative_integers_to_signed_min.sol @@ -0,0 +1,3 @@ +contract test { + int8 public i = -128; +} diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/192_positive_integers_to_signed_out_of_bound.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/192_positive_integers_to_signed_out_of_bound.sol new file mode 100644 index 00000000..d56045c2 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/192_positive_integers_to_signed_out_of_bound.sol @@ -0,0 +1,5 @@ +contract test { + int8 public j = 128; +} +// ---- +// TypeError: (36-39): Type int_const 128 is not implicitly convertible to expected type int8. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/193_positive_integers_to_signed_out_of_bound_max.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/193_positive_integers_to_signed_out_of_bound_max.sol new file mode 100644 index 00000000..66701339 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/193_positive_integers_to_signed_out_of_bound_max.sol @@ -0,0 +1,3 @@ +contract test { + int8 public j = 127; +} diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/194_negative_integers_to_unsigned.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/194_negative_integers_to_unsigned.sol new file mode 100644 index 00000000..3702f09b --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/194_negative_integers_to_unsigned.sol @@ -0,0 +1,5 @@ +contract test { + uint8 public x = -1; +} +// ---- +// TypeError: (37-39): Type int_const -1 is not implicitly convertible to expected type uint8. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/195_positive_integers_to_unsigned_out_of_bound.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/195_positive_integers_to_unsigned_out_of_bound.sol new file mode 100644 index 00000000..81216229 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/195_positive_integers_to_unsigned_out_of_bound.sol @@ -0,0 +1,5 @@ +contract test { + uint8 public x = 700; +} +// ---- +// TypeError: (37-40): Type int_const 700 is not implicitly convertible to expected type uint8. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/196_integer_boolean_or.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/196_integer_boolean_or.sol new file mode 100644 index 00000000..db42786d --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/196_integer_boolean_or.sol @@ -0,0 +1,3 @@ +contract test { function() external { uint x = 1; uint y = 2; x || y; } } +// ---- +// TypeError: (62-68): Operator || not compatible with types uint256 and uint256 diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/197_integer_boolean_and.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/197_integer_boolean_and.sol new file mode 100644 index 00000000..94d1c691 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/197_integer_boolean_and.sol @@ -0,0 +1,3 @@ +contract test { function() external { uint x = 1; uint y = 2; x && y; } } +// ---- +// TypeError: (62-68): Operator && not compatible with types uint256 and uint256 diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/198_integer_boolean_not.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/198_integer_boolean_not.sol new file mode 100644 index 00000000..68fe6e94 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/198_integer_boolean_not.sol @@ -0,0 +1,3 @@ +contract test { function() external { uint x = 1; !x; } } +// ---- +// TypeError: (50-52): Unary operator ! cannot be applied to type uint256 diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/199_integer_unsigned_exp_signed.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/199_integer_unsigned_exp_signed.sol new file mode 100644 index 00000000..fbeadfb6 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/199_integer_unsigned_exp_signed.sol @@ -0,0 +1,3 @@ +contract test { function() external { uint x = 3; int y = -4; x ** y; } } +// ---- +// TypeError: (62-68): Operator ** not compatible with types uint256 and int256 diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/200_integer_signed_exp_unsigned.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/200_integer_signed_exp_unsigned.sol new file mode 100644 index 00000000..75e92085 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/200_integer_signed_exp_unsigned.sol @@ -0,0 +1,3 @@ +contract test { function() external { uint x = 3; int y = -4; y ** x; } } +// ---- +// TypeError: (62-68): Operator ** not compatible with types int256 and uint256 diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/201_integer_signed_exp_signed.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/201_integer_signed_exp_signed.sol new file mode 100644 index 00000000..93e5f065 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/201_integer_signed_exp_signed.sol @@ -0,0 +1,3 @@ +contract test { function() external { int x = -3; int y = -4; x ** y; } } +// ---- +// TypeError: (62-68): Operator ** not compatible with types int256 and int256 diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/202_bytes_reference_compare_operators.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/202_bytes_reference_compare_operators.sol new file mode 100644 index 00000000..711b794c --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/202_bytes_reference_compare_operators.sol @@ -0,0 +1,3 @@ +contract test { bytes a; bytes b; function() external { a == b; } } +// ---- +// TypeError: (56-62): Operator == not compatible with types bytes storage ref and bytes storage ref diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/203_struct_reference_compare_operators.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/203_struct_reference_compare_operators.sol new file mode 100644 index 00000000..a74850b3 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/203_struct_reference_compare_operators.sol @@ -0,0 +1,10 @@ +contract test { + struct s {uint a;} + s x; + s y; + function() external { + x == y; + } +} +// ---- +// TypeError: (79-85): Operator == not compatible with types struct test.s storage ref and struct test.s storage ref diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/204_overwrite_memory_location_external.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/204_overwrite_memory_location_external.sol new file mode 100644 index 00000000..22d515ea --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/204_overwrite_memory_location_external.sol @@ -0,0 +1,5 @@ +contract C { + function f(uint[] memory a) external {} +} +// ---- +// TypeError: (28-43): Data location must be "calldata" for parameter in external function, but "memory" was given. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/205_overwrite_storage_location_external.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/205_overwrite_storage_location_external.sol new file mode 100644 index 00000000..3825809c --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/205_overwrite_storage_location_external.sol @@ -0,0 +1,5 @@ +contract C { + function f(uint[] storage a) external {} +} +// ---- +// TypeError: (28-44): Data location must be "calldata" for parameter in external function, but "storage" was given. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/206_storage_location_local_variables.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/206_storage_location_local_variables.sol new file mode 100644 index 00000000..868d7bc8 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/206_storage_location_local_variables.sol @@ -0,0 +1,9 @@ +contract C { + uint[] m_x; + function f() public view { + uint[] storage x = m_x; + uint[] memory y; + x;y; + } +} +// ---- diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/207_no_mappings_in_memory_array.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/207_no_mappings_in_memory_array.sol new file mode 100644 index 00000000..5220ee22 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/207_no_mappings_in_memory_array.sol @@ -0,0 +1,7 @@ +contract C { + function f() public { + mapping(uint=>uint)[] memory x; + } +} +// ---- +// TypeError: (47-77): Type mapping(uint256 => uint256)[] memory is only valid in storage. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/208_assignment_mem_to_local_storage_variable.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/208_assignment_mem_to_local_storage_variable.sol new file mode 100644 index 00000000..cf303772 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/208_assignment_mem_to_local_storage_variable.sol @@ -0,0 +1,9 @@ +contract C { + uint[] data; + function f(uint[] memory x) public { + uint[] storage dataRef = data; + dataRef = x; + } +} +// ---- +// TypeError: (128-129): Type uint256[] memory is not implicitly convertible to expected type uint256[] storage pointer. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/209_storage_assign_to_different_local_variable.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/209_storage_assign_to_different_local_variable.sol new file mode 100644 index 00000000..aabdcd88 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/209_storage_assign_to_different_local_variable.sol @@ -0,0 +1,12 @@ +contract C { + uint[] data; + uint8[] otherData; + function f() public { + uint8[] storage x = otherData; + uint[] storage y = data; + y = x; + // note that data = otherData works + } +} +// ---- +// TypeError: (163-164): Type uint8[] storage pointer is not implicitly convertible to expected type uint256[] storage pointer. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/210_uninitialized_mapping_variable.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/210_uninitialized_mapping_variable.sol new file mode 100644 index 00000000..0547ace1 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/210_uninitialized_mapping_variable.sol @@ -0,0 +1,8 @@ +contract C { + function f() public { + mapping(uint => uint) storage x; + x; + } +} +// ---- +// TypeError: (47-78): Uninitialized mapping. Mappings cannot be created dynamically, you have to assign them from a state variable. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/211_uninitialized_mapping_array_variable.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/211_uninitialized_mapping_array_variable.sol new file mode 100644 index 00000000..edae7549 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/211_uninitialized_mapping_array_variable.sol @@ -0,0 +1,8 @@ +contract C { + function f() pure public { + mapping(uint => uint)[] storage x; + x; + } +} +// ---- +// DeclarationError: (52-85): Uninitialized storage pointer. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/213_no_delete_on_storage_pointers.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/213_no_delete_on_storage_pointers.sol new file mode 100644 index 00000000..7a6fb1c7 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/213_no_delete_on_storage_pointers.sol @@ -0,0 +1,9 @@ +contract C { + uint[] data; + function f() public { + uint[] storage x = data; + delete x; + } +} +// ---- +// TypeError: (97-105): Unary operator delete cannot be applied to type uint256[] storage pointer diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/214_assignment_mem_storage_variable_directly.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/214_assignment_mem_storage_variable_directly.sol new file mode 100644 index 00000000..801eb275 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/214_assignment_mem_storage_variable_directly.sol @@ -0,0 +1,6 @@ +contract C { + uint[] data; + function f(uint[] memory x) public { + data = x; + } +} diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/215_function_argument_mem_to_storage.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/215_function_argument_mem_to_storage.sol new file mode 100644 index 00000000..984b81b1 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/215_function_argument_mem_to_storage.sol @@ -0,0 +1,9 @@ +contract C { + function f(uint[] storage x) private { + } + function g(uint[] memory x) public { + f(x); + } +} +// ---- +// TypeError: (113-114): Invalid type for argument in function call. Invalid implicit conversion from uint256[] memory to uint256[] storage pointer requested. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/216_function_argument_storage_to_mem.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/216_function_argument_storage_to_mem.sol new file mode 100644 index 00000000..c5175a41 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/216_function_argument_storage_to_mem.sol @@ -0,0 +1,10 @@ +contract C { + function f(uint[] storage x) private { + g(x); + } + function g(uint[] memory x) public { + } +} +// ---- +// Warning: (91-106): Unused function parameter. Remove or comment out the variable name to silence this warning. +// Warning: (80-122): Function state mutability can be restricted to pure diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/217_mem_array_assignment_changes_base_type.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/217_mem_array_assignment_changes_base_type.sol new file mode 100644 index 00000000..3755b935 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/217_mem_array_assignment_changes_base_type.sol @@ -0,0 +1,10 @@ +contract C { + function f(uint8[] memory x) private { + // Such an assignment is possible in storage, but not in memory + // (because it would incur an otherwise unnecessary copy). + // This requirement might be lifted, though. + uint[] memory y = x; + } +} +// ---- +// TypeError: (256-275): Type uint8[] memory is not implicitly convertible to expected type uint256[] memory. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/219_memory_arrays_not_resizeable.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/219_memory_arrays_not_resizeable.sol new file mode 100644 index 00000000..93d8cd42 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/219_memory_arrays_not_resizeable.sol @@ -0,0 +1,8 @@ +contract C { + function f() public { + uint[] memory x; + x.length = 2; + } +} +// ---- +// TypeError: (72-80): Expression has to be an lvalue. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/220_struct_constructor.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/220_struct_constructor.sol new file mode 100644 index 00000000..50585c11 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/220_struct_constructor.sol @@ -0,0 +1,9 @@ +contract C { + struct S { uint a; bool x; } + function f() public { + S memory s = S(1, true); + } +} +// ---- +// Warning: (80-90): Unused local variable. +// Warning: (50-110): Function state mutability can be restricted to pure diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/221_struct_constructor_nested.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/221_struct_constructor_nested.sol new file mode 100644 index 00000000..00222682 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/221_struct_constructor_nested.sol @@ -0,0 +1,11 @@ +contract C { + struct X { uint x1; uint x2; } + struct S { uint s1; uint[3] s2; X s3; } + function f() public { + uint[3] memory s2; + S memory s = S(1, s2, X(4, 5)); + } +} +// ---- +// Warning: (153-163): Unused local variable. +// Warning: (96-190): Function state mutability can be restricted to pure diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/222_struct_named_constructor.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/222_struct_named_constructor.sol new file mode 100644 index 00000000..8ab8ee46 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/222_struct_named_constructor.sol @@ -0,0 +1,9 @@ +contract C { + struct S { uint a; bool x; } + function f() public { + S memory s = S({a: 1, x: true}); + } +} +// ---- +// Warning: (80-90): Unused local variable. +// Warning: (50-118): Function state mutability can be restricted to pure diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/223_literal_strings.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/223_literal_strings.sol new file mode 100644 index 00000000..1dadcc4d --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/223_literal_strings.sol @@ -0,0 +1,9 @@ +contract Foo { + function f() public { + string memory long = "01234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890"; + string memory short = "123"; + long; short; + } +} +// ---- +// Warning: (19-238): Function state mutability can be restricted to pure diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/224_string_bytes_conversion.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/224_string_bytes_conversion.sol new file mode 100644 index 00000000..ed6a9b37 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/224_string_bytes_conversion.sol @@ -0,0 +1,11 @@ +contract Test { + string s; + bytes b; + function h(string calldata _s) pure external { bytes(_s).length; } + function i(string memory _s) pure internal { bytes(_s).length; } + function j() view internal { bytes(s).length; } + function k(bytes calldata _b) pure external { string(_b); } + function l(bytes memory _b) pure internal { string(_b); } + function m() view internal { string(b); } +} +// ---- diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/225_inheriting_from_library.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/225_inheriting_from_library.sol new file mode 100644 index 00000000..eff7bf86 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/225_inheriting_from_library.sol @@ -0,0 +1,4 @@ +library Lib {} +contract Test is Lib {} +// ---- +// TypeError: (32-35): Libraries cannot be inherited from. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/226_inheriting_library.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/226_inheriting_library.sol new file mode 100644 index 00000000..2d601c1c --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/226_inheriting_library.sol @@ -0,0 +1,4 @@ +contract Test {} +library Lib is Test {} +// ---- +// TypeError: (17-39): Library is not allowed to inherit. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/227_library_having_variables.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/227_library_having_variables.sol new file mode 100644 index 00000000..804ef3d3 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/227_library_having_variables.sol @@ -0,0 +1,3 @@ +library Lib { uint x; } +// ---- +// TypeError: (14-20): Library cannot have non-constant state variables diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/228_valid_library.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/228_valid_library.sol new file mode 100644 index 00000000..de6b0b3e --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/228_valid_library.sol @@ -0,0 +1 @@ +library Lib { uint constant x = 9; } diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/229_call_to_library_function.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/229_call_to_library_function.sol new file mode 100644 index 00000000..c007d9a1 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/229_call_to_library_function.sol @@ -0,0 +1,10 @@ +library Lib { + function min(uint, uint) public returns (uint); +} +contract Test { + function f() public { + uint t = Lib.min(12, 7); + } +} +// ---- +// Warning: (118-124): Unused local variable. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/230_creating_contract_within_the_contract.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/230_creating_contract_within_the_contract.sol new file mode 100644 index 00000000..8624b0b0 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/230_creating_contract_within_the_contract.sol @@ -0,0 +1,5 @@ +contract Test { + function f() public { Test x = new Test(); } +} +// ---- +// TypeError: (51-59): Circular reference for contract creation (cannot create instance of derived or same contract). diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/231_array_out_of_bound_access.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/231_array_out_of_bound_access.sol new file mode 100644 index 00000000..7230a0a6 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/231_array_out_of_bound_access.sol @@ -0,0 +1,9 @@ +contract c { + uint[2] dataArray; + function set5th() public returns (bool) { + dataArray[5] = 2; + return true; + } +} +// ---- +// TypeError: (90-102): Out of bounds array access. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/232_literal_string_to_storage_pointer.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/232_literal_string_to_storage_pointer.sol new file mode 100644 index 00000000..be57144e --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/232_literal_string_to_storage_pointer.sol @@ -0,0 +1,5 @@ +contract C { + function f() public { string storage x = "abc"; } +} +// ---- +// TypeError: (39-63): Type literal_string "abc" is not implicitly convertible to expected type string storage pointer. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/233_non_initialized_references.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/233_non_initialized_references.sol new file mode 100644 index 00000000..a0b6f71e --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/233_non_initialized_references.sol @@ -0,0 +1,11 @@ +contract C { + struct s { + uint a; + } + function f() public { + s storage x; + x.a = 2; + } +} +// ---- +// DeclarationError: (84-95): Uninitialized storage pointer. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/235_abi_encode_with_large_integer_constant.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/235_abi_encode_with_large_integer_constant.sol new file mode 100644 index 00000000..fd9717f1 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/235_abi_encode_with_large_integer_constant.sol @@ -0,0 +1,5 @@ +contract C { + function f() pure public { abi.encode(2**500); } +} +// ---- +// TypeError: (55-61): Invalid rational number (too large or division by zero). diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/236_cyclic_binary_dependency.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/236_cyclic_binary_dependency.sol new file mode 100644 index 00000000..c287507d --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/236_cyclic_binary_dependency.sol @@ -0,0 +1,5 @@ +contract A { function f() public { new B(); } } +contract B { function f() public { new C(); } } +contract C { function f() public { new A(); } } +// ---- +// TypeError: (131-136): Circular reference for contract creation (cannot create instance of derived or same contract). diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/237_cyclic_binary_dependency_via_inheritance.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/237_cyclic_binary_dependency_via_inheritance.sol new file mode 100644 index 00000000..00ee536e --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/237_cyclic_binary_dependency_via_inheritance.sol @@ -0,0 +1,5 @@ +contract A is B { } +contract B { function f() public { new C(); } } +contract C { function f() public { new A(); } } +// ---- +// TypeError: (14-15): Definition of base has to precede definition of derived contract diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/244_tuples.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/244_tuples.sol new file mode 100644 index 00000000..d18c115d --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/244_tuples.sol @@ -0,0 +1,10 @@ +contract C { + function f() public pure { + uint a = (1); + (uint b,) = (uint8(1),2); + (uint c, uint d) = (uint32(1), 2 + a); + (uint e, ,) = (uint64(1), 2, b); + a;b;c;d;e; + } +} +// ---- diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/245_tuples_empty_components.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/245_tuples_empty_components.sol new file mode 100644 index 00000000..7815edea --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/245_tuples_empty_components.sol @@ -0,0 +1,7 @@ +contract C { + function f() public { + (1,,2); + } +} +// ---- +// TypeError: (47-53): Tuple component cannot be empty. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/249_tuple_compound_assignment.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/249_tuple_compound_assignment.sol new file mode 100644 index 00000000..bcdbde02 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/249_tuple_compound_assignment.sol @@ -0,0 +1,7 @@ +contract C { + function f() public returns (uint a, uint b) { + (a, b) += (1, 1); + } +} +// ---- +// TypeError: (72-88): Compound assignment is not allowed for tuple types. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/250_member_access_parser_ambiguity.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/250_member_access_parser_ambiguity.sol new file mode 100644 index 00000000..0ab3c198 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/250_member_access_parser_ambiguity.sol @@ -0,0 +1,16 @@ +contract C { + struct R { uint[10][10] y; } + struct S { uint a; uint b; uint[20][20][20] c; R d; } + S data; + function f() public { + C.S storage x = data; + C.S memory y; + C.S[10] memory z; + C.S[10]; + y.a = 2; + x.c[1][2][3] = 9; + x.d.y[2][2] = 3; + z; + } +} +// ---- diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/251_using_for_library.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/251_using_for_library.sol new file mode 100644 index 00000000..c7dcdbcd --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/251_using_for_library.sol @@ -0,0 +1,4 @@ +library D { } +contract C { + using D for uint; +} diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/252_using_for_not_library.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/252_using_for_not_library.sol new file mode 100644 index 00000000..4693b27f --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/252_using_for_not_library.sol @@ -0,0 +1,6 @@ +contract D { } +contract C { + using D for uint; +} +// ---- +// TypeError: (38-39): Library name expected. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/253_using_for_function_exists.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/253_using_for_function_exists.sol new file mode 100644 index 00000000..9e570805 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/253_using_for_function_exists.sol @@ -0,0 +1,10 @@ +library D { function double(uint self) public returns (uint) { return 2*self; } } +contract C { + using D for uint; + function f(uint a) public { + a.double; + } +} +// ---- +// Warning: (12-79): Function state mutability can be restricted to pure +// Warning: (121-172): Function state mutability can be restricted to pure diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/254_using_for_function_on_int.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/254_using_for_function_on_int.sol new file mode 100644 index 00000000..a8e23d0f --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/254_using_for_function_on_int.sol @@ -0,0 +1,9 @@ +library D { function double(uint self) public returns (uint) { return 2*self; } } +contract C { + using D for uint; + function f(uint a) public returns (uint) { + return a.double(); + } +} +// ---- +// Warning: (12-79): Function state mutability can be restricted to pure diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/255_using_for_function_on_struct.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/255_using_for_function_on_struct.sol new file mode 100644 index 00000000..2c3deb4c --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/255_using_for_function_on_struct.sol @@ -0,0 +1,8 @@ +library D { struct s { uint a; } function mul(s storage self, uint x) public returns (uint) { return self.a *= x; } } +contract C { + using D for D.s; + D.s x; + function f(uint a) public returns (uint) { + return x.mul(a); + } +} diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/256_using_for_overload.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/256_using_for_overload.sol new file mode 100644 index 00000000..155281f5 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/256_using_for_overload.sol @@ -0,0 +1,14 @@ +library D { + struct s { uint a; } + function mul(s storage self, uint x) public returns (uint) { return self.a *= x; } + function mul(s storage, bytes32) public returns (bytes32) { } +} +contract C { + using D for D.s; + D.s x; + function f(uint a) public returns (uint) { + return x.mul(a); + } +} +// ---- +// Warning: (128-189): Function state mutability can be restricted to pure diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/257_using_for_by_name.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/257_using_for_by_name.sol new file mode 100644 index 00000000..b3eab987 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/257_using_for_by_name.sol @@ -0,0 +1,8 @@ +library D { struct s { uint a; } function mul(s storage self, uint x) public returns (uint) { return self.a *= x; } } +contract C { + using D for D.s; + D.s x; + function f(uint a) public returns (uint) { + return x.mul({x: a}); + } +} diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/258_using_for_mismatch.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/258_using_for_mismatch.sol new file mode 100644 index 00000000..c60ee651 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/258_using_for_mismatch.sol @@ -0,0 +1,9 @@ +library D { function double(bytes32 self) public returns (uint) { return 2; } } +contract C { + using D for uint; + function f(uint a) public returns (uint) { + return a.double(); + } +} +// ---- +// TypeError: (177-185): Member "double" not found or not visible after argument-dependent lookup in uint256. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/259_using_for_not_used.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/259_using_for_not_used.sol new file mode 100644 index 00000000..b11cefba --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/259_using_for_not_used.sol @@ -0,0 +1,11 @@ +library D { function double(uint self) public returns (uint) { return 2; } } +contract C { + using D for uint; + function f(uint16 a) public returns (uint) { + // This is an error because the function is only bound to uint. + // Had it been bound to *, it would have worked. + return a.double(); + } +} +// ---- +// TypeError: (305-313): Member "double" not found or not visible after argument-dependent lookup in uint16. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/260_library_memory_struct.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/260_library_memory_struct.sol new file mode 100644 index 00000000..20d8afa5 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/260_library_memory_struct.sol @@ -0,0 +1,8 @@ +pragma experimental ABIEncoderV2; +library c { + struct S { uint x; } + function f() public returns (S memory) {} +} +// ---- +// Warning: (0-33): Experimental features are turned on. Do not use experimental features on live deployments. +// Warning: (75-116): Function state mutability can be restricted to pure diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/261_using_for_arbitrary_mismatch.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/261_using_for_arbitrary_mismatch.sol new file mode 100644 index 00000000..b2b55350 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/261_using_for_arbitrary_mismatch.sol @@ -0,0 +1,10 @@ +library D { function double(bytes32 self) public returns (uint) { return 2; } } +contract C { + using D for *; + function f(uint a) public returns (uint) { + // Bound to a, but self type does not match. + return a.double(); + } +} +// ---- +// TypeError: (227-235): Member "double" not found or not visible after argument-dependent lookup in uint256. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/262_bound_function_in_var.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/262_bound_function_in_var.sol new file mode 100644 index 00000000..c3cc5232 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/262_bound_function_in_var.sol @@ -0,0 +1,13 @@ +library D { struct s { uint a; } function mul(s storage self, uint x) public returns (uint) { return self.a *= x; } } +contract C { + using D for D.s; + D.s x; + function f(uint a) public returns (uint) { + function (D.s storage, uint) returns (uint) g = x.mul; + g(x, a); + g(a); + } +} +// ---- +// TypeError: (218-271): Type function (struct D.s storage pointer,uint256) returns (uint256) is not implicitly convertible to expected type function (struct D.s storage pointer,uint256) returns (uint256). +// TypeError: (298-302): Wrong argument count for function call: 1 arguments given but expected 2. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/263_create_memory_arrays.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/263_create_memory_arrays.sol new file mode 100644 index 00000000..71f43992 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/263_create_memory_arrays.sol @@ -0,0 +1,14 @@ +library L { + struct R { uint[10][10] y; } + struct S { uint a; uint b; uint[20][20][20] c; R d; } +} +contract C { + function f(uint size) public { + L.S[][] memory x = new L.S[][](10); + uint[] memory y = new uint[](20); + bytes memory z = new bytes(size); + x;y;z; + } +} +// ---- +// Warning: (122-301): Function state mutability can be restricted to pure diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/264_mapping_in_memory_array.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/264_mapping_in_memory_array.sol new file mode 100644 index 00000000..e45e09de --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/264_mapping_in_memory_array.sol @@ -0,0 +1,7 @@ +contract C { + function f(uint size) public { + mapping(uint => uint) storage x = new mapping(uint => uint)[](4); + } +} +// ---- +// TypeError: (94-117): Type cannot live outside storage. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/265_new_for_non_array.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/265_new_for_non_array.sol new file mode 100644 index 00000000..c4b2c692 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/265_new_for_non_array.sol @@ -0,0 +1,7 @@ +contract C { + function f(uint size) public { + uint x = new uint(7); + } +} +// ---- +// TypeError: (65-73): Contract or array type expected. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/266_invalid_args_creating_memory_array.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/266_invalid_args_creating_memory_array.sol new file mode 100644 index 00000000..078255e3 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/266_invalid_args_creating_memory_array.sol @@ -0,0 +1,7 @@ +contract C { + function f(uint size) public { + uint[] memory x = new uint[](); + } +} +// ---- +// TypeError: (74-86): Wrong argument count for function call: 0 arguments given but expected 1. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/267_invalid_args_creating_struct.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/267_invalid_args_creating_struct.sol new file mode 100644 index 00000000..35671e6f --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/267_invalid_args_creating_struct.sol @@ -0,0 +1,9 @@ +contract C { + struct S { uint a; uint b; } + + function f() public { + S memory s = S({a: 1}); + } +} +// ---- +// TypeError: (94-103): Wrong argument count for struct constructor: 1 arguments given but expected 2. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/268_function_overload_array_type.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/268_function_overload_array_type.sol new file mode 100644 index 00000000..4fc9d46e --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/268_function_overload_array_type.sol @@ -0,0 +1,4 @@ +contract M { + function f(uint[] memory) public; + function f(int[] memory) public; +} diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/269_inline_array_declaration_and_passing_implicit_conversion.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/269_inline_array_declaration_and_passing_implicit_conversion.sol new file mode 100644 index 00000000..023404f7 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/269_inline_array_declaration_and_passing_implicit_conversion.sol @@ -0,0 +1,11 @@ + contract C { + function f() public returns (uint) { + uint8 x = 7; + uint16 y = 8; + uint32 z = 9; + uint32[3] memory ending = [x, y, z]; + return (ending[1]); + } + } +// ---- +// Warning: (25-229): Function state mutability can be restricted to pure diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/270_inline_array_declaration_and_passing_implicit_conversion_strings.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/270_inline_array_declaration_and_passing_implicit_conversion_strings.sol new file mode 100644 index 00000000..025244d3 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/270_inline_array_declaration_and_passing_implicit_conversion_strings.sol @@ -0,0 +1,10 @@ +contract C { + function f() public returns (string memory) { + string memory x = "Hello"; + string memory y = "World"; + string[2] memory z = [x, y]; + return (z[0]); + } +} +// ---- +// Warning: (17-198): Function state mutability can be restricted to pure diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/271_inline_array_declaration_const_int_conversion.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/271_inline_array_declaration_const_int_conversion.sol new file mode 100644 index 00000000..e7036bdf --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/271_inline_array_declaration_const_int_conversion.sol @@ -0,0 +1,8 @@ +contract C { + function f() public returns (uint) { + uint8[4] memory z = [1,2,3,5]; + return (z[0]); + } +} +// ---- +// Warning: (17-121): Function state mutability can be restricted to pure diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/272_inline_array_declaration_const_string_conversion.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/272_inline_array_declaration_const_string_conversion.sol new file mode 100644 index 00000000..4e92f6e1 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/272_inline_array_declaration_const_string_conversion.sol @@ -0,0 +1,8 @@ +contract C { + function f() public returns (string memory) { + string[2] memory z = ["Hello", "World"]; + return (z[0]); + } +} +// ---- +// Warning: (17-140): Function state mutability can be restricted to pure diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/273_inline_array_declaration_no_type.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/273_inline_array_declaration_no_type.sol new file mode 100644 index 00000000..4d3e6aed --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/273_inline_array_declaration_no_type.sol @@ -0,0 +1,7 @@ +contract C { + function f() public returns (uint) { + return ([4,5,6][1]); + } +} +// ---- +// Warning: (17-88): Function state mutability can be restricted to pure diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/274_inline_array_declaration_no_type_strings.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/274_inline_array_declaration_no_type_strings.sol new file mode 100644 index 00000000..6d36942d --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/274_inline_array_declaration_no_type_strings.sol @@ -0,0 +1,7 @@ +contract C { + function f() public returns (string memory) { + return (["foo", "man", "choo"][1]); + } +} +// ---- +// Warning: (17-112): Function state mutability can be restricted to pure diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/275_inline_struct_declaration_arrays.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/275_inline_struct_declaration_arrays.sol new file mode 100644 index 00000000..bdf033a3 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/275_inline_struct_declaration_arrays.sol @@ -0,0 +1,12 @@ +contract C { + struct S { + uint a; + string b; + } + function f() public { + S[2] memory x = [S({a: 1, b: "fish"}), S({a: 2, b: "fish"})]; + } +} +// ---- +// Warning: (102-115): Unused local variable. +// Warning: (72-169): Function state mutability can be restricted to pure diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/276_invalid_types_in_inline_array.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/276_invalid_types_in_inline_array.sol new file mode 100644 index 00000000..03d7266a --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/276_invalid_types_in_inline_array.sol @@ -0,0 +1,7 @@ +contract C { + function f() public { + uint[3] memory x = [45, 'foo', true]; + } +} +// ---- +// TypeError: (66-83): Unable to deduce common type for array elements. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/277_dynamic_inline_array.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/277_dynamic_inline_array.sol new file mode 100644 index 00000000..e613758b --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/277_dynamic_inline_array.sol @@ -0,0 +1,8 @@ +contract C { + function f() public { + uint8[4][4] memory dyn = [[1, 2, 3, 4], [2, 3, 4, 5], [3, 4, 5, 6], [4, 5, 6, 7]]; + } +} +// ---- +// Warning: (47-69): Unused local variable. +// Warning: (17-135): Function state mutability can be restricted to pure diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/278_lvalues_as_inline_array.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/278_lvalues_as_inline_array.sol new file mode 100644 index 00000000..5a39f550 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/278_lvalues_as_inline_array.sol @@ -0,0 +1,8 @@ +contract C { + function f() public { + [1, 2, 3]++; + [1, 2, 3] = [4, 5, 6]; + } +} +// ---- +// TypeError: (47-56): Inline array type cannot be declared as LValue. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/279_break_not_in_loop.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/279_break_not_in_loop.sol new file mode 100644 index 00000000..6b88da44 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/279_break_not_in_loop.sol @@ -0,0 +1,8 @@ +contract C { + function f() public { + if (true) + break; + } +} +// ---- +// SyntaxError: (69-74): "break" has to be in a "for" or "while" loop. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/280_continue_not_in_loop.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/280_continue_not_in_loop.sol new file mode 100644 index 00000000..b0e8cda9 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/280_continue_not_in_loop.sol @@ -0,0 +1,8 @@ +contract C { + function f() public { + if (true) + continue; + } +} +// ---- +// SyntaxError: (69-77): "continue" has to be in a "for" or "while" loop. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/281_continue_not_in_loop_2.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/281_continue_not_in_loop_2.sol new file mode 100644 index 00000000..845faf86 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/281_continue_not_in_loop_2.sol @@ -0,0 +1,10 @@ +contract C { + function f() public { + while (true) + { + } + continue; + } +} +// ---- +// SyntaxError: (88-96): "continue" has to be in a "for" or "while" loop. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/282_invalid_different_types_for_conditional_expression.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/282_invalid_different_types_for_conditional_expression.sol new file mode 100644 index 00000000..e4e75e3f --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/282_invalid_different_types_for_conditional_expression.sol @@ -0,0 +1,7 @@ +contract C { + function f() public { + true ? true : 2; + } +} +// ---- +// TypeError: (47-62): True expression's type bool doesn't match false expression's type uint8. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/283_left_value_in_conditional_expression_not_supported_yet.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/283_left_value_in_conditional_expression_not_supported_yet.sol new file mode 100644 index 00000000..ef8f9930 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/283_left_value_in_conditional_expression_not_supported_yet.sol @@ -0,0 +1,10 @@ +contract C { + function f() public { + uint x; + uint y; + (true ? x : y) = 1; + } +} +// ---- +// TypeError: (80-92): Conditional expression as left value is not supported yet. +// TypeError: (80-92): Expression has to be an lvalue. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/284_conditional_expression_with_different_struct.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/284_conditional_expression_with_different_struct.sol new file mode 100644 index 00000000..049d9e21 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/284_conditional_expression_with_different_struct.sol @@ -0,0 +1,15 @@ +contract C { + struct s1 { + uint x; + } + struct s2 { + uint x; + } + function f() public { + s1 memory x; + s2 memory y; + true ? x : y; + } +} +// ---- +// TypeError: (165-177): True expression's type struct C.s1 memory doesn't match false expression's type struct C.s2 memory. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/285_conditional_expression_with_different_function_type.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/285_conditional_expression_with_different_function_type.sol new file mode 100644 index 00000000..963fb7da --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/285_conditional_expression_with_different_function_type.sol @@ -0,0 +1,10 @@ +contract C { + function x(bool) public {} + function y() public {} + + function f() public { + true ? x : y; + } +} +// ---- +// TypeError: (106-118): True expression's type function (bool) doesn't match false expression's type function (). diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/286_conditional_expression_with_different_enum.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/286_conditional_expression_with_different_enum.sol new file mode 100644 index 00000000..8c312624 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/286_conditional_expression_with_different_enum.sol @@ -0,0 +1,13 @@ +contract C { + enum small { A, B, C, D } + enum big { A, B, C, D } + + function f() public { + small x; + big y; + + true ? x : y; + } +} +// ---- +// TypeError: (139-151): True expression's type enum C.small doesn't match false expression's type enum C.big. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/287_conditional_expression_with_different_mapping.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/287_conditional_expression_with_different_mapping.sol new file mode 100644 index 00000000..8139f3ed --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/287_conditional_expression_with_different_mapping.sol @@ -0,0 +1,10 @@ +contract C { + mapping(uint8 => uint8) table1; + mapping(uint32 => uint8) table2; + + function f() public { + true ? table1 : table2; + } +} +// ---- +// TypeError: (121-143): True expression's type mapping(uint8 => uint8) doesn't match false expression's type mapping(uint32 => uint8). diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/288_conditional_with_all_types.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/288_conditional_with_all_types.sol new file mode 100644 index 00000000..e9ab08ba --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/288_conditional_with_all_types.sol @@ -0,0 +1,91 @@ +contract C { + struct s1 { + uint x; + } + s1 struct_x; + s1 struct_y; + + function fun_x() public {} + function fun_y() public {} + + enum small { A, B, C, D } + + mapping(uint8 => uint8) table1; + mapping(uint8 => uint8) table2; + + function f() public { + // integers + uint x; + uint y; + uint g = true ? x : y; + g += 1; // Avoid unused var warning + + // integer constants + uint h = true ? 1 : 3; + h += 1; // Avoid unused var warning + + // string literal + string memory i = true ? "hello" : "world"; + i = "used"; //Avoid unused var warning + } + function f2() public { + // bool + bool j = true ? true : false; + j = j && true; // Avoid unused var warning + + // real is not there yet. + + // array + byte[2] memory a; + byte[2] memory b; + byte[2] memory k = true ? a : b; + k[0] = byte(0); //Avoid unused var warning + + bytes memory e; + bytes memory f; + bytes memory l = true ? e : f; + l[0] = byte(0); // Avoid unused var warning + + // fixed bytes + bytes2 c; + bytes2 d; + bytes2 m = true ? c : d; + m &= m; + + } + function f3() public { + // contract doesn't fit in here + + // struct + struct_x = true ? struct_x : struct_y; + + // function + function () r = true ? fun_x : fun_y; + r(); // Avoid unused var warning + // enum + small enum_x; + small enum_y; + enum_x = true ? enum_x : enum_y; + + // tuple + (uint n, uint o) = true ? (1, 2) : (3, 4); + (n, o) = (o, n); // Avoid unused var warning + // mapping + mapping(uint8 => uint8) storage p = true ? table1 : table2; + p[0] = 0; // Avoid unused var warning + // typetype + uint32 q = true ? uint32(1) : uint32(2); + q += 1; // Avoid unused var warning + // modifier doesn't fit in here + + // magic doesn't fit in here + + // module doesn't fit in here + } +} +// ---- +// Warning: (1005-1019): This declaration shadows an existing declaration. +// Warning: (90-116): Function state mutability can be restricted to pure +// Warning: (121-147): Function state mutability can be restricted to pure +// Warning: (257-642): Function state mutability can be restricted to pure +// Warning: (647-1227): Function state mutability can be restricted to pure diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/289_uint7_and_uintM_as_identifier.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/289_uint7_and_uintM_as_identifier.sol new file mode 100644 index 00000000..58e84090 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/289_uint7_and_uintM_as_identifier.sol @@ -0,0 +1,12 @@ +contract test { +string uintM = "Hello 4 you"; + function f() public { + uint8 uint7 = 3; + uint7 = 5; + string memory intM; + uint bytesM = 21; + intM; bytesM; + } +} +// ---- +// Warning: (50-197): Function state mutability can be restricted to pure diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/290_varM_disqualified_as_keyword_1.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/290_varM_disqualified_as_keyword_1.sol new file mode 100644 index 00000000..0d0a0797 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/290_varM_disqualified_as_keyword_1.sol @@ -0,0 +1,7 @@ +contract test { + function f() public { + uintM something = 3; + } +} +// ---- +// DeclarationError: (50-55): Identifier not found or not unique. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/290_varM_disqualified_as_keyword_2.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/290_varM_disqualified_as_keyword_2.sol new file mode 100644 index 00000000..b9590a8c --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/290_varM_disqualified_as_keyword_2.sol @@ -0,0 +1,7 @@ +contract test { + function f() public { + intM should = 4; + } +} +// ---- +// DeclarationError: (50-54): Identifier not found or not unique. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/290_varM_disqualified_as_keyword_3.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/290_varM_disqualified_as_keyword_3.sol new file mode 100644 index 00000000..85d4c25b --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/290_varM_disqualified_as_keyword_3.sol @@ -0,0 +1,7 @@ +contract test { + function f() public { + bytesM fail = "now"; + } +} +// ---- +// DeclarationError: (50-56): Identifier not found or not unique. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/291_modifier_is_not_a_valid_typename.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/291_modifier_is_not_a_valid_typename.sol new file mode 100644 index 00000000..2f3143d5 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/291_modifier_is_not_a_valid_typename.sol @@ -0,0 +1,9 @@ +contract test { + modifier mod() { _; } + + function f() public { + mod g; + } +} +// ---- +// TypeError: (77-80): Name has to refer to a struct, enum or contract. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/292_modifier_is_not_a_valid_typename_is_not_fatal.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/292_modifier_is_not_a_valid_typename_is_not_fatal.sol new file mode 100644 index 00000000..9187c19d --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/292_modifier_is_not_a_valid_typename_is_not_fatal.sol @@ -0,0 +1,10 @@ +contract test { + modifier mod() { _; } + + function f() public { + mod g; + g = f; + } +} +// ---- +// TypeError: (77-80): Name has to refer to a struct, enum or contract. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/293_function_is_not_a_valid_typename.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/293_function_is_not_a_valid_typename.sol new file mode 100644 index 00000000..390eccd9 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/293_function_is_not_a_valid_typename.sol @@ -0,0 +1,10 @@ +contract test { + function foo() public { + } + + function f() public { + foo g; + } +} +// ---- +// TypeError: (85-88): Name has to refer to a struct, enum or contract. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/294_long_uint_variable_fails.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/294_long_uint_variable_fails.sol new file mode 100644 index 00000000..1e608320 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/294_long_uint_variable_fails.sol @@ -0,0 +1,7 @@ +contract test { + function f() public { + uint99999999999999999999999999 something = 3; + } +} +// ---- +// DeclarationError: (50-80): Identifier not found or not unique. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/295_bytes10abc_is_identifier.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/295_bytes10abc_is_identifier.sol new file mode 100644 index 00000000..8b65fc65 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/295_bytes10abc_is_identifier.sol @@ -0,0 +1,8 @@ +contract test { + function f() public { + bytes32 bytes10abc = "abc"; + } +} +// ---- +// Warning: (50-68): Unused local variable. +// Warning: (20-83): Function state mutability can be restricted to pure diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/296_int10abc_is_identifier.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/296_int10abc_is_identifier.sol new file mode 100644 index 00000000..2678cfb9 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/296_int10abc_is_identifier.sol @@ -0,0 +1,9 @@ +contract test { + function f() public { + uint uint10abc = 3; + int int10abc = 4; + uint10abc; int10abc; + } +} +// ---- +// Warning: (20-130): Function state mutability can be restricted to pure diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/297_library_functions_do_not_have_value.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/297_library_functions_do_not_have_value.sol new file mode 100644 index 00000000..918544cc --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/297_library_functions_do_not_have_value.sol @@ -0,0 +1,8 @@ +library L { function l() public {} } +contract test { + function f() public { + L.l.value; + } +} +// ---- +// TypeError: (87-96): Member "value" not found or not visible after argument-dependent lookup in function () - did you forget the "payable" modifier? diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/298_invalid_fixed_types_0x7_mxn.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/298_invalid_fixed_types_0x7_mxn.sol new file mode 100644 index 00000000..ea9e5d0f --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/298_invalid_fixed_types_0x7_mxn.sol @@ -0,0 +1,5 @@ +contract test { + fixed0x7 a = .3; +} +// ---- +// DeclarationError: (20-28): Identifier not found or not unique. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/299_invalid_fixed_types_long_invalid_identifier.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/299_invalid_fixed_types_long_invalid_identifier.sol new file mode 100644 index 00000000..9ce2b106 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/299_invalid_fixed_types_long_invalid_identifier.sol @@ -0,0 +1,5 @@ +contract test { + fixed99999999999999999999999999999999999999x7 b = 9.5; +} +// ---- +// DeclarationError: (20-65): Identifier not found or not unique. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/300_invalid_fixed_types_7x8_mxn.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/300_invalid_fixed_types_7x8_mxn.sol new file mode 100644 index 00000000..7c511d2f --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/300_invalid_fixed_types_7x8_mxn.sol @@ -0,0 +1,5 @@ +contract test { + fixed7x8 c = 3.12345678; +} +// ---- +// DeclarationError: (20-28): Identifier not found or not unique. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/301_library_instances_cannot_be_used.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/301_library_instances_cannot_be_used.sol new file mode 100644 index 00000000..dcf11a6e --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/301_library_instances_cannot_be_used.sol @@ -0,0 +1,9 @@ +library L { function l() public {} } +contract test { + function f() public { + L x; + x.l(); + } +} +// ---- +// TypeError: (100-103): Member "l" not found or not visible after argument-dependent lookup in library L. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/302_invalid_fixed_type_long.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/302_invalid_fixed_type_long.sol new file mode 100644 index 00000000..12679631 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/302_invalid_fixed_type_long.sol @@ -0,0 +1,7 @@ +contract test { + function f() public { + fixed8x888888888888888888888888888888888888888888888888888 b; + } +} +// ---- +// DeclarationError: (50-108): Identifier not found or not unique. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/303_fixed_type_int_conversion.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/303_fixed_type_int_conversion.sol new file mode 100644 index 00000000..ddf1e5fb --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/303_fixed_type_int_conversion.sol @@ -0,0 +1,11 @@ +contract test { + function f() public { + uint64 a = 3; + int64 b = 4; + fixed c = b; + ufixed d = a; + c; d; + } +} +// ---- +// Warning: (20-147): Function state mutability can be restricted to pure diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/304_fixed_type_rational_int_conversion.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/304_fixed_type_rational_int_conversion.sol new file mode 100644 index 00000000..16d2cbad --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/304_fixed_type_rational_int_conversion.sol @@ -0,0 +1,9 @@ +contract test { + function f() public { + fixed c = 3; + ufixed d = 4; + c; d; + } +} +// ---- +// Warning: (20-104): Function state mutability can be restricted to pure diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/305_fixed_type_rational_fraction_conversion.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/305_fixed_type_rational_fraction_conversion.sol new file mode 100644 index 00000000..27029860 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/305_fixed_type_rational_fraction_conversion.sol @@ -0,0 +1,9 @@ +contract test { + function f() public { + fixed a = 4.5; + ufixed d = 2.5; + a; d; + } +} +// ---- +// Warning: (20-108): Function state mutability can be restricted to pure diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/306_invalid_int_implicit_conversion_from_fixed.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/306_invalid_int_implicit_conversion_from_fixed.sol new file mode 100644 index 00000000..c0a56314 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/306_invalid_int_implicit_conversion_from_fixed.sol @@ -0,0 +1,9 @@ +contract test { + function f() public { + fixed a = 4.5; + int b = a; + a; b; + } +} +// ---- +// TypeError: (73-82): Type fixed128x18 is not implicitly convertible to expected type int256. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/307_rational_unary_minus_operation.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/307_rational_unary_minus_operation.sol new file mode 100644 index 00000000..7827e57b --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/307_rational_unary_minus_operation.sol @@ -0,0 +1,7 @@ +contract test { + function f() pure public { + ufixed16x2 a = 3.25; + fixed16x2 b = -3.25; + a; b; + } +} diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/308_rational_unary_plus_operation.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/308_rational_unary_plus_operation.sol new file mode 100644 index 00000000..f635a214 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/308_rational_unary_plus_operation.sol @@ -0,0 +1,9 @@ +contract test { + function f() pure public { + ufixed16x2 a = +3.25; + fixed16x2 b = -3.25; + a; b; + } +} +// ---- +// SyntaxError: (70-75): Use of unary + is disallowed. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/312_leading_zero_rationals_convert.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/312_leading_zero_rationals_convert.sol new file mode 100644 index 00000000..9fe7c6f7 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/312_leading_zero_rationals_convert.sol @@ -0,0 +1,9 @@ +contract A { + function f() pure public { + ufixed16x2 a = 0.5; + ufixed256x52 b = 0.0000000000000006661338147750939242541790008544921875; + fixed16x2 c = -0.5; + fixed256x52 d = -0.0000000000000006661338147750939242541790008544921875; + a; b; c; d; + } +} diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/313_fixed_type_size_capabilities.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/313_fixed_type_size_capabilities.sol new file mode 100644 index 00000000..441cf81e --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/313_fixed_type_size_capabilities.sol @@ -0,0 +1,13 @@ +contract test { + function f() public { + ufixed256x1 a = 123456781234567979695948382928485849359686494864095409282048094275023098123.5; + ufixed256x77 b = 0.920890746623327805482905058466021565416131529487595827354393978494366605267637; + ufixed224x78 c = 0.000000000001519884736399797998492268541131529487595827354393978494366605267646; + fixed256x1 d = -123456781234567979695948382928485849359686494864095409282048094275023098123.5; + fixed256x76 e = -0.93322335481643744342575580035176794825198893968114429702091846411734101080123; + fixed256x79 g = -0.0001178860664374434257558003517679482519889396811442970209184641173410108012309; + a; b; c; d; e; g; + } +} +// ---- +// Warning: (20-707): Function state mutability can be restricted to pure diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/314_fixed_type_zero_handling.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/314_fixed_type_zero_handling.sol new file mode 100644 index 00000000..5f0d2909 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/314_fixed_type_zero_handling.sol @@ -0,0 +1,8 @@ +contract test { + function f() public { + fixed16x2 a = 0; a; + ufixed32x1 b = 0; b; + } +} +// ---- +// Warning: (20-104): Function state mutability can be restricted to pure diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/315_fixed_type_invalid_implicit_conversion_size.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/315_fixed_type_invalid_implicit_conversion_size.sol new file mode 100644 index 00000000..79698228 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/315_fixed_type_invalid_implicit_conversion_size.sol @@ -0,0 +1,8 @@ +contract test { + function f() public { + ufixed a = 11/4; + ufixed248x8 b = a; b; + } +} +// ---- +// TypeError: (75-92): Type ufixed128x18 is not implicitly convertible to expected type ufixed248x8. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/316_fixed_type_invalid_implicit_conversion_lost_data.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/316_fixed_type_invalid_implicit_conversion_lost_data.sol new file mode 100644 index 00000000..76c0284e --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/316_fixed_type_invalid_implicit_conversion_lost_data.sol @@ -0,0 +1,7 @@ +contract test { + function f() public { + ufixed256x1 a = 1/3; a; + } +} +// ---- +// TypeError: (50-69): Type rational_const 1 / 3 is not implicitly convertible to expected type ufixed256x1. Try converting to type ufixed256x77 or use an explicit conversion. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/317_fixed_type_valid_explicit_conversions.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/317_fixed_type_valid_explicit_conversions.sol new file mode 100644 index 00000000..38801457 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/317_fixed_type_valid_explicit_conversions.sol @@ -0,0 +1,9 @@ +contract test { + function f() public { + ufixed256x80 a = ufixed256x80(1/3); a; + ufixed248x80 b = ufixed248x80(1/3); b; + ufixed8x1 c = ufixed8x1(1/3); c; + } +} +// ---- +// Warning: (20-182): Function state mutability can be restricted to pure diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/318_invalid_array_declaration_with_rational.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/318_invalid_array_declaration_with_rational.sol new file mode 100644 index 00000000..3dd779ec --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/318_invalid_array_declaration_with_rational.sol @@ -0,0 +1,7 @@ +contract test { + function f() public { + uint[3.5] a; a; + } +} +// ---- +// TypeError: (55-58): Array with fractional length specified. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/319_invalid_array_declaration_with_signed_fixed_type.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/319_invalid_array_declaration_with_signed_fixed_type.sol new file mode 100644 index 00000000..83f0950d --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/319_invalid_array_declaration_with_signed_fixed_type.sol @@ -0,0 +1,7 @@ +contract test { + function f() public { + uint[fixed(3.5)] a; a; + } +} +// ---- +// TypeError: (55-65): Invalid array length, expected integer literal or constant expression. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/320_invalid_array_declaration_with_unsigned_fixed_type.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/320_invalid_array_declaration_with_unsigned_fixed_type.sol new file mode 100644 index 00000000..26d5a85e --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/320_invalid_array_declaration_with_unsigned_fixed_type.sol @@ -0,0 +1,7 @@ +contract test { + function f() public { + uint[ufixed(3.5)] a; a; + } +} +// ---- +// TypeError: (55-66): Invalid array length, expected integer literal or constant expression. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/321_rational_to_bytes_implicit_conversion.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/321_rational_to_bytes_implicit_conversion.sol new file mode 100644 index 00000000..d209eb76 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/321_rational_to_bytes_implicit_conversion.sol @@ -0,0 +1,7 @@ +contract test { + function f() public { + bytes32 c = 3.2; c; + } +} +// ---- +// TypeError: (50-65): Type rational_const 16 / 5 is not implicitly convertible to expected type bytes32. Try converting to type ufixed8x1 or use an explicit conversion. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/322_fixed_to_bytes_implicit_conversion.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/322_fixed_to_bytes_implicit_conversion.sol new file mode 100644 index 00000000..86736481 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/322_fixed_to_bytes_implicit_conversion.sol @@ -0,0 +1,8 @@ +contract test { + function f() public { + fixed a = 3.25; + bytes32 c = a; c; + } +} +// ---- +// TypeError: (74-87): Type fixed128x18 is not implicitly convertible to expected type bytes32. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/323_mapping_with_fixed_literal.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/323_mapping_with_fixed_literal.sol new file mode 100644 index 00000000..8e28d4e1 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/323_mapping_with_fixed_literal.sol @@ -0,0 +1,6 @@ +contract test { + mapping(ufixed8x1 => string) fixedString; + function f() public { + fixedString[0.5] = "Half"; + } +} diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/324_fixed_points_inside_structs.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/324_fixed_points_inside_structs.sol new file mode 100644 index 00000000..8aa0abc2 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/324_fixed_points_inside_structs.sol @@ -0,0 +1,7 @@ +contract test { + struct myStruct { + ufixed a; + int b; + } + myStruct a = myStruct(3.125, 3); +} diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/325_inline_array_fixed_types.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/325_inline_array_fixed_types.sol new file mode 100644 index 00000000..c46297c3 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/325_inline_array_fixed_types.sol @@ -0,0 +1,8 @@ +contract test { + function f() public { + fixed[3] memory a = [fixed(3.5), fixed(-4.25), fixed(967.125)]; + } +} +// ---- +// Warning: (50-67): Unused local variable. +// Warning: (20-119): Function state mutability can be restricted to pure diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/326_inline_array_rationals.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/326_inline_array_rationals.sol new file mode 100644 index 00000000..bdc3c2c1 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/326_inline_array_rationals.sol @@ -0,0 +1,8 @@ +contract test { + function f() public { + ufixed128x3[4] memory a = [ufixed128x3(3.5), 4.125, 2.5, 4.0]; + } +} +// ---- +// Warning: (50-73): Unused local variable. +// Warning: (20-118): Function state mutability can be restricted to pure diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/327_rational_index_access.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/327_rational_index_access.sol new file mode 100644 index 00000000..46e58521 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/327_rational_index_access.sol @@ -0,0 +1,8 @@ +contract test { + function f() public { + uint[] memory a; + a[.5]; + } +} +// ---- +// TypeError: (77-79): Type rational_const 1 / 2 is not implicitly convertible to expected type uint256. Try converting to type ufixed8x1 or use an explicit conversion. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/328_rational_to_fixed_literal_expression.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/328_rational_to_fixed_literal_expression.sol new file mode 100644 index 00000000..35456fa6 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/328_rational_to_fixed_literal_expression.sol @@ -0,0 +1,15 @@ +contract test { + function f() public { + ufixed64x8 a = 3.5 * 3; + ufixed64x8 b = 4 - 2.5; + ufixed64x8 c = 11 / 4; + ufixed240x5 d = 599 + 0.21875; + ufixed256x80 e = ufixed256x80(35.245 % 12.9); + ufixed256x80 f = ufixed256x80(1.2 % 2); + fixed g = 2 ** -2; + a; b; c; d; e; f; g; + } +} +// ---- +// Warning: (238-252): This declaration shadows an existing declaration. +// Warning: (20-339): Function state mutability can be restricted to pure diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/329_rational_as_exponent_value_signed.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/329_rational_as_exponent_value_signed.sol new file mode 100644 index 00000000..b835e309 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/329_rational_as_exponent_value_signed.sol @@ -0,0 +1,7 @@ +contract test { + function f() public { + fixed g = 2 ** -2.2; + } +} +// ---- +// TypeError: (60-69): Operator ** not compatible with types int_const 2 and rational_const -11 / 5 diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/330_rational_as_exponent_value_unsigned.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/330_rational_as_exponent_value_unsigned.sol new file mode 100644 index 00000000..04ddf0fd --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/330_rational_as_exponent_value_unsigned.sol @@ -0,0 +1,7 @@ +contract test { + function f() public { + ufixed b = 3 ** 2.5; + } +} +// ---- +// TypeError: (61-69): Operator ** not compatible with types int_const 3 and rational_const 5 / 2 diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/331_rational_as_exponent_half.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/331_rational_as_exponent_half.sol new file mode 100644 index 00000000..4e0894c5 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/331_rational_as_exponent_half.sol @@ -0,0 +1,7 @@ +contract test { + function f() public { + 2 ** (1/2); + } +} +// ---- +// TypeError: (50-60): Operator ** not compatible with types int_const 2 and rational_const 1 / 2 diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/332_rational_as_exponent_value_neg_quarter.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/332_rational_as_exponent_value_neg_quarter.sol new file mode 100644 index 00000000..bc127bf5 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/332_rational_as_exponent_value_neg_quarter.sol @@ -0,0 +1,7 @@ +contract test { + function f() public { + 42 ** (-1/4); + } +} +// ---- +// TypeError: (50-62): Operator ** not compatible with types int_const 42 and rational_const -1 / 4 diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/333_fixed_point_casting_exponents_15.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/333_fixed_point_casting_exponents_15.sol new file mode 100644 index 00000000..0fd5f331 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/333_fixed_point_casting_exponents_15.sol @@ -0,0 +1,7 @@ +contract test { + function f() public { + ufixed a = 3 ** ufixed(1.5); + } +} +// ---- +// TypeError: (61-77): Operator ** not compatible with types int_const 3 and ufixed128x18 diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/334_fixed_point_casting_exponents_neg.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/334_fixed_point_casting_exponents_neg.sol new file mode 100644 index 00000000..03d10f7c --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/334_fixed_point_casting_exponents_neg.sol @@ -0,0 +1,7 @@ +contract test { + function f() public { + ufixed c = 42 ** fixed(-1/4); + } +} +// ---- +// TypeError: (61-78): Operator ** not compatible with types int_const 42 and fixed128x18 diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/338_rational_bitnot_unary_operation.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/338_rational_bitnot_unary_operation.sol new file mode 100644 index 00000000..44a6ab5e --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/338_rational_bitnot_unary_operation.sol @@ -0,0 +1,7 @@ +contract test { + function f() public { + ~fixed(3.5); + } +} +// ---- +// TypeError: (50-61): Unary operator ~ cannot be applied to type fixed128x18 diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/339_rational_bitor_binary_operation.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/339_rational_bitor_binary_operation.sol new file mode 100644 index 00000000..29871b04 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/339_rational_bitor_binary_operation.sol @@ -0,0 +1,7 @@ +contract test { + function f() public { + fixed(1.5) | 3; + } +} +// ---- +// TypeError: (50-64): Operator | not compatible with types fixed128x18 and int_const 3 diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/340_rational_bitxor_binary_operation.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/340_rational_bitxor_binary_operation.sol new file mode 100644 index 00000000..1fa7f38f --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/340_rational_bitxor_binary_operation.sol @@ -0,0 +1,7 @@ +contract test { + function f() public { + fixed(1.75) ^ 3; + } +} +// ---- +// TypeError: (50-65): Operator ^ not compatible with types fixed128x18 and int_const 3 diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/341_rational_bitand_binary_operation.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/341_rational_bitand_binary_operation.sol new file mode 100644 index 00000000..5a433a61 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/341_rational_bitand_binary_operation.sol @@ -0,0 +1,7 @@ +contract test { + function f() public { + fixed(1.75) & 3; + } +} +// ---- +// TypeError: (50-65): Operator & not compatible with types fixed128x18 and int_const 3 diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/342_missing_bool_conversion.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/342_missing_bool_conversion.sol new file mode 100644 index 00000000..5546a099 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/342_missing_bool_conversion.sol @@ -0,0 +1,7 @@ +contract test { + function b(uint a) public { + bool(a == 1); + } +} +// ---- +// Warning: (20-75): Function state mutability can be restricted to pure diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/343_integer_and_fixed_interaction.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/343_integer_and_fixed_interaction.sol new file mode 100644 index 00000000..af4d048b --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/343_integer_and_fixed_interaction.sol @@ -0,0 +1,8 @@ +contract test { + function f() public { + ufixed a = uint64(1) + ufixed(2); + } +} +// ---- +// Warning: (50-58): Unused local variable. +// Warning: (20-89): Function state mutability can be restricted to pure diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/344_one_divided_by_three_integer_conversion.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/344_one_divided_by_three_integer_conversion.sol new file mode 100644 index 00000000..ffc168eb --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/344_one_divided_by_three_integer_conversion.sol @@ -0,0 +1,7 @@ +contract test { + function f() public { + uint a = 1/3; + } +} +// ---- +// TypeError: (50-62): Type rational_const 1 / 3 is not implicitly convertible to expected type uint256. Try converting to type ufixed256x77 or use an explicit conversion. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/345_unused_return_value.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/345_unused_return_value.sol new file mode 100644 index 00000000..7f640505 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/345_unused_return_value.sol @@ -0,0 +1,8 @@ +contract test { + function g() public returns (uint) {} + function f() public { + g(); + } +} +// ---- +// Warning: (20-57): Function state mutability can be restricted to pure diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/346_unused_return_value_send.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/346_unused_return_value_send.sol new file mode 100644 index 00000000..929e54b2 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/346_unused_return_value_send.sol @@ -0,0 +1,7 @@ +contract test { + function f() public { + address(0x12).send(1); + } +} +// ---- +// Warning: (50-71): Failure condition of 'send' ignored. Consider using 'transfer' instead. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/347_unused_return_value_call.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/347_unused_return_value_call.sol new file mode 100644 index 00000000..994a5bdf --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/347_unused_return_value_call.sol @@ -0,0 +1,7 @@ +contract test { + function f() public { + address(0x12).call("abc"); + } +} +// ---- +// Warning: (50-75): Return value of low-level calls not used. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/348_unused_return_value_call_value.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/348_unused_return_value_call_value.sol new file mode 100644 index 00000000..1ac7c6f3 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/348_unused_return_value_call_value.sol @@ -0,0 +1,7 @@ +contract test { + function f() public { + address(0x12).call.value(2)("abc"); + } +} +// ---- +// Warning: (50-84): Return value of low-level calls not used. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/350_unused_return_value_delegatecall.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/350_unused_return_value_delegatecall.sol new file mode 100644 index 00000000..701b6e7d --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/350_unused_return_value_delegatecall.sol @@ -0,0 +1,7 @@ +contract test { + function f() public { + address(0x12).delegatecall("abc"); + } +} +// ---- +// Warning: (50-83): Return value of low-level calls not used. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/351_callcode_deprecated.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/351_callcode_deprecated.sol new file mode 100644 index 00000000..554f2e11 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/351_callcode_deprecated.sol @@ -0,0 +1,7 @@ +contract test { + function f() pure public { + address(0x12).callcode; + } +} +// ---- +// TypeError: (55-77): "callcode" has been deprecated in favour of "delegatecall". diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/353_callcode_not_deprecated_as_function.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/353_callcode_not_deprecated_as_function.sol new file mode 100644 index 00000000..714014f8 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/353_callcode_not_deprecated_as_function.sol @@ -0,0 +1,5 @@ +contract test { + function callcode() pure public { + test.callcode(); + } +} diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/354_payable_in_library.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/354_payable_in_library.sol new file mode 100644 index 00000000..410842cb --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/354_payable_in_library.sol @@ -0,0 +1,5 @@ +library test { + function f() payable public {} +} +// ---- +// TypeError: (19-49): Library functions cannot be payable. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/355_payable_external.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/355_payable_external.sol new file mode 100644 index 00000000..3b75e1b7 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/355_payable_external.sol @@ -0,0 +1,3 @@ +contract test { + function f() payable external {} +} diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/356_payable_internal.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/356_payable_internal.sol new file mode 100644 index 00000000..f6ccf6b6 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/356_payable_internal.sol @@ -0,0 +1,5 @@ +contract test { + function f() payable internal {} +} +// ---- +// TypeError: (20-52): Internal functions cannot be payable. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/357_payable_private.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/357_payable_private.sol new file mode 100644 index 00000000..7b00ea6c --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/357_payable_private.sol @@ -0,0 +1,5 @@ +contract test { + function f() payable private {} +} +// ---- +// TypeError: (20-51): Internal functions cannot be payable. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/358_illegal_override_payable.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/358_illegal_override_payable.sol new file mode 100644 index 00000000..6696772e --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/358_illegal_override_payable.sol @@ -0,0 +1,4 @@ +contract B { function f() payable public {} } +contract C is B { function f() public {} } +// ---- +// TypeError: (64-86): Overriding function changes state mutability from "payable" to "nonpayable". diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/359_illegal_override_payable_nonpayable.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/359_illegal_override_payable_nonpayable.sol new file mode 100644 index 00000000..99b45fdd --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/359_illegal_override_payable_nonpayable.sol @@ -0,0 +1,4 @@ +contract B { function f() public {} } +contract C is B { function f() payable public {} } +// ---- +// TypeError: (56-86): Overriding function changes state mutability from "nonpayable" to "payable". diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/360_function_variable_mixin.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/360_function_variable_mixin.sol new file mode 100644 index 00000000..583e0d46 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/360_function_variable_mixin.sol @@ -0,0 +1,12 @@ +// bug #1798 (cpp-ethereum), related to #1286 (solidity) +contract attribute { + bool ok = false; +} +contract func { + function ok() public returns (bool) { return true; } +} +contract attr_func is attribute, func { + function checkOk() public returns (bool) { return ok(); } +} +// ---- +// DeclarationError: (121-173): Identifier already declared. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/361_calling_payable.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/361_calling_payable.sol new file mode 100644 index 00000000..8ef4d579 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/361_calling_payable.sol @@ -0,0 +1,6 @@ +contract receiver { function pay() payable public {} } +contract test { + function f() public { (new receiver()).pay.value(10)(); } + receiver r = new receiver(); + function g() public { r.pay.value(10)(); } +} diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/362_calling_nonpayable.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/362_calling_nonpayable.sol new file mode 100644 index 00000000..d47ea2bd --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/362_calling_nonpayable.sol @@ -0,0 +1,6 @@ +contract receiver { function nopay() public {} } +contract test { + function f() public { (new receiver()).nopay.value(10)(); } +} +// ---- +// TypeError: (91-119): Member "value" not found or not visible after argument-dependent lookup in function () external - did you forget the "payable" modifier? diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/363_non_payable_constructor.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/363_non_payable_constructor.sol new file mode 100644 index 00000000..27381904 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/363_non_payable_constructor.sol @@ -0,0 +1,11 @@ +contract C { + constructor() public { } +} +contract D { + function f() public returns (uint) { + (new C).value(2)(); + return 2; + } +} +// ---- +// TypeError: (106-119): Member "value" not found or not visible after argument-dependent lookup in function () returns (contract C) - did you forget the "payable" modifier? diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/366_invalid_array_as_statement.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/366_invalid_array_as_statement.sol new file mode 100644 index 00000000..cc2839cd --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/366_invalid_array_as_statement.sol @@ -0,0 +1,6 @@ +contract test { + struct S { uint x; } + constructor(uint k) public { S[k]; } +} +// ---- +// TypeError: (76-77): Integer constant expected. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/367_using_directive_for_missing_selftype.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/367_using_directive_for_missing_selftype.sol new file mode 100644 index 00000000..3d9bc3fc --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/367_using_directive_for_missing_selftype.sol @@ -0,0 +1,14 @@ +library B { + function b() public {} +} + +contract A { + using B for bytes; + + function a() public { + bytes memory x; + x.b(); + } +} +// ---- +// TypeError: (137-140): Member "b" not found or not visible after argument-dependent lookup in bytes memory. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/368_shift_constant_left_negative_rvalue.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/368_shift_constant_left_negative_rvalue.sol new file mode 100644 index 00000000..9c941a68 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/368_shift_constant_left_negative_rvalue.sol @@ -0,0 +1,5 @@ +contract C { + uint public a = 0x42 << -8; +} +// ---- +// TypeError: (33-43): Operator << not compatible with types int_const 66 and int_const -8 diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/369_shift_constant_right_negative_rvalue.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/369_shift_constant_right_negative_rvalue.sol new file mode 100644 index 00000000..55f385c6 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/369_shift_constant_right_negative_rvalue.sol @@ -0,0 +1,5 @@ +contract C { + uint public a = 0x42 >> -8; +} +// ---- +// TypeError: (33-43): Operator >> not compatible with types int_const 66 and int_const -8 diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/370_shift_constant_left_excessive_rvalue.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/370_shift_constant_left_excessive_rvalue.sol new file mode 100644 index 00000000..e23c7a84 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/370_shift_constant_left_excessive_rvalue.sol @@ -0,0 +1,5 @@ +contract C { + uint public a = 0x42 << 0x100000000; +} +// ---- +// TypeError: (33-52): Operator << not compatible with types int_const 66 and int_const 4294967296 diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/371_shift_constant_right_excessive_rvalue.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/371_shift_constant_right_excessive_rvalue.sol new file mode 100644 index 00000000..5533644f --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/371_shift_constant_right_excessive_rvalue.sol @@ -0,0 +1,5 @@ +contract C { + uint public a = 0x42 >> 0x100000000; +} +// ---- +// TypeError: (33-52): Operator >> not compatible with types int_const 66 and int_const 4294967296 diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/372_shift_constant_right_fractional.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/372_shift_constant_right_fractional.sol new file mode 100644 index 00000000..38d9b051 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/372_shift_constant_right_fractional.sol @@ -0,0 +1,5 @@ +contract C { + uint public a = 0x42 >> (1 / 2); +} +// ---- +// TypeError: (33-48): Operator >> not compatible with types int_const 66 and rational_const 1 / 2 diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/373_inline_assembly_unbalanced_positive_stack.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/373_inline_assembly_unbalanced_positive_stack.sol new file mode 100644 index 00000000..e9599f4b --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/373_inline_assembly_unbalanced_positive_stack.sol @@ -0,0 +1,10 @@ +contract test { + function f() public { + assembly { + 1 + } + } +} +// ---- +// SyntaxError: (73-74): Top-level expressions are not supposed to return values (this expression returns 1 value). Use ``pop()`` or assign them. +// DeclarationError: (59-84): Unbalanced stack at the end of a block: 1 surplus item(s). diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/374_inline_assembly_unbalanced_negative_stack.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/374_inline_assembly_unbalanced_negative_stack.sol new file mode 100644 index 00000000..342afc46 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/374_inline_assembly_unbalanced_negative_stack.sol @@ -0,0 +1,10 @@ +contract test { + function f() public { + assembly { + pop + } + } +} +// ---- +// SyntaxError: (73-76): The use of non-functional instructions is disallowed. Please use functional notation instead. +// DeclarationError: (59-86): Unbalanced stack at the end of a block: 1 missing item(s). diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/375_inline_assembly_unbalanced_two_stack_load.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/375_inline_assembly_unbalanced_two_stack_load.sol new file mode 100644 index 00000000..ca1e15a9 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/375_inline_assembly_unbalanced_two_stack_load.sol @@ -0,0 +1,8 @@ +contract c { + uint8 x; + function f() public { + assembly { pop(x) } + } +} +// ---- +// TypeError: (75-76): Only local variables are supported. To access storage variables, use the _slot and _offset suffixes. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/376_inline_assembly_in_modifier.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/376_inline_assembly_in_modifier.sol new file mode 100644 index 00000000..0032f99e --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/376_inline_assembly_in_modifier.sol @@ -0,0 +1,13 @@ +contract test { + modifier m { + uint a = 1; + assembly { + a := 2 + } + _; + } + function f() public m { + } +} +// ---- +// Warning: (122-151): Function state mutability can be restricted to pure diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/377_inline_assembly_storage.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/377_inline_assembly_storage.sol new file mode 100644 index 00000000..3dfb458a --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/377_inline_assembly_storage.sol @@ -0,0 +1,10 @@ +contract test { + uint x = 1; + function f() public { + assembly { + x := 2 + } + } +} +// ---- +// TypeError: (89-90): Only local variables are supported. To access storage variables, use the _slot and _offset suffixes. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/378_inline_assembly_storage_in_modifiers.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/378_inline_assembly_storage_in_modifiers.sol new file mode 100644 index 00000000..b9b92d47 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/378_inline_assembly_storage_in_modifiers.sol @@ -0,0 +1,13 @@ +contract test { + uint x = 1; + modifier m { + assembly { + x := 2 + } + _; + } + function f() public m { + } +} +// ---- +// TypeError: (80-81): Only local variables are supported. To access storage variables, use the _slot and _offset suffixes. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/379_inline_assembly_constant_assign.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/379_inline_assembly_constant_assign.sol new file mode 100644 index 00000000..c8928804 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/379_inline_assembly_constant_assign.sol @@ -0,0 +1,10 @@ +contract test { + uint constant x = 1; + function f() public { + assembly { + x := 2 + } + } +} +// ---- +// TypeError: (98-99): Constant variables not supported by inline assembly. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/380_inline_assembly_constant_access.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/380_inline_assembly_constant_access.sol new file mode 100644 index 00000000..03ff9166 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/380_inline_assembly_constant_access.sol @@ -0,0 +1,10 @@ +contract test { + uint constant x = 1; + function f() public { + assembly { + let y := x + } + } +} +// ---- +// TypeError: (107-108): Constant variables not supported by inline assembly. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/381_inline_assembly_local_variable_access_out_of_functions.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/381_inline_assembly_local_variable_access_out_of_functions.sol new file mode 100644 index 00000000..877f5783 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/381_inline_assembly_local_variable_access_out_of_functions.sol @@ -0,0 +1,10 @@ +contract test { + function f() public { + uint a; + assembly { + function g() -> x { x := a } + } + } +} +// ---- +// DeclarationError: (114-115): Cannot access local Solidity variables from inside an inline assembly function. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/382_inline_assembly_local_variable_access_out_of_functions_storage_ptr.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/382_inline_assembly_local_variable_access_out_of_functions_storage_ptr.sol new file mode 100644 index 00000000..65d614a3 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/382_inline_assembly_local_variable_access_out_of_functions_storage_ptr.sol @@ -0,0 +1,11 @@ +contract test { + uint[] r; + function f() public { + uint[] storage a = r; + assembly { + function g() -> x { x := a_offset } + } + } +} +// ---- +// DeclarationError: (142-150): Cannot access local Solidity variables from inside an inline assembly function. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/383_inline_assembly_storage_variable_access_out_of_functions.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/383_inline_assembly_storage_variable_access_out_of_functions.sol new file mode 100644 index 00000000..abe9067a --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/383_inline_assembly_storage_variable_access_out_of_functions.sol @@ -0,0 +1,8 @@ +contract test { + uint a; + function f() pure public { + assembly { + function g() -> x { x := a_slot } + } + } +} diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/384_inline_assembly_constant_variable_via_offset.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/384_inline_assembly_constant_variable_via_offset.sol new file mode 100644 index 00000000..6470a210 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/384_inline_assembly_constant_variable_via_offset.sol @@ -0,0 +1,10 @@ +contract test { + uint constant x = 2; + function f() pure public { + assembly { + let r := x_offset + } + } +} +// ---- +// TypeError: (112-120): Constant variables not supported by inline assembly. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/385_inline_assembly_calldata_variables.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/385_inline_assembly_calldata_variables.sol new file mode 100644 index 00000000..952b9af6 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/385_inline_assembly_calldata_variables.sol @@ -0,0 +1,9 @@ +contract C { + function f(bytes calldata bytesAsCalldata) external { + assembly { + let x := bytesAsCalldata + } + } +} +// ---- +// TypeError: (111-126): Call data elements cannot be accessed directly. Copy to a local variable first or use "calldataload" or "calldatacopy" with manually determined offsets and sizes. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/387_inline_assembly_literals_on_stack.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/387_inline_assembly_literals_on_stack.sol new file mode 100644 index 00000000..62fe7171 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/387_inline_assembly_literals_on_stack.sol @@ -0,0 +1,10 @@ +contract C { + function f() pure public { + assembly { + 1 + } + } +} +// ---- +// SyntaxError: (75-76): Top-level expressions are not supposed to return values (this expression returns 1 value). Use ``pop()`` or assign them. +// DeclarationError: (61-86): Unbalanced stack at the end of a block: 1 surplus item(s). diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/389_inline_assembly_bare_instructions.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/389_inline_assembly_bare_instructions.sol new file mode 100644 index 00000000..7315d5d1 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/389_inline_assembly_bare_instructions.sol @@ -0,0 +1,11 @@ +contract C { + function f() view public { + assembly { + address + pop + } + } +} +// ---- +// SyntaxError: (75-82): The use of non-functional instructions is disallowed. Please use functional notation instead. +// SyntaxError: (95-98): The use of non-functional instructions is disallowed. Please use functional notation instead. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/391_inline_assembly_labels.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/391_inline_assembly_labels.sol new file mode 100644 index 00000000..0d7bacb4 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/391_inline_assembly_labels.sol @@ -0,0 +1,10 @@ +contract C { + function f() pure public { + assembly { + label: + } + } +} +// ---- +// SyntaxError: (75-80): The use of labels is disallowed. Please use "if", "switch", "for" or function calls instead. +// SyntaxError: (75-80): Jump instructions and labels are low-level EVM features that can lead to incorrect stack access. Because of that they are discouraged. Please consider using "switch", "if" or "for" statements instead. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/393_inline_assembly_jump.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/393_inline_assembly_jump.sol new file mode 100644 index 00000000..6cb35d6d --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/393_inline_assembly_jump.sol @@ -0,0 +1,9 @@ +contract C { + function f() pure public { + assembly { + jump(2) + } + } +} +// ---- +// SyntaxError: (75-82): Jump instructions and labels are low-level EVM features that can lead to incorrect stack access. Because of that they are discouraged. Please consider using "switch", "if" or "for" statements instead. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/395_inline_assembly_leave_items_on_stack.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/395_inline_assembly_leave_items_on_stack.sol new file mode 100644 index 00000000..8538a2a0 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/395_inline_assembly_leave_items_on_stack.sol @@ -0,0 +1,10 @@ +contract C { + function f() pure public { + assembly { + mload(0) + } + } +} +// ---- +// SyntaxError: (75-83): Top-level expressions are not supposed to return values (this expression returns 1 value). Use ``pop()`` or assign them. +// DeclarationError: (61-93): Unbalanced stack at the end of a block: 1 surplus item(s). diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/396_invalid_mobile_type.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/396_invalid_mobile_type.sol new file mode 100644 index 00000000..536dd317 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/396_invalid_mobile_type.sol @@ -0,0 +1,8 @@ + contract C { + function f() public { + // Invalid number + [1, 78901234567890123456789012345678901234567890123456789345678901234567890012345678012345678901234567]; + } + } +// ---- +// TypeError: (93-191): Invalid rational number. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/397_warns_msg_value_in_non_payable_public_function.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/397_warns_msg_value_in_non_payable_public_function.sol new file mode 100644 index 00000000..c56ad25f --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/397_warns_msg_value_in_non_payable_public_function.sol @@ -0,0 +1,7 @@ +contract C { + function f() view public { + msg.value; + } +} +// ---- +// TypeError: (52-61): "msg.value" can only be used in payable public functions. Make the function "payable" or use an internal function to avoid this error. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/398_does_not_warn_msg_value_in_payable_function.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/398_does_not_warn_msg_value_in_payable_function.sol new file mode 100644 index 00000000..f14e86ed --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/398_does_not_warn_msg_value_in_payable_function.sol @@ -0,0 +1,5 @@ +contract C { + function f() payable public { + msg.value; + } +} diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/399_does_not_warn_msg_value_in_internal_function.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/399_does_not_warn_msg_value_in_internal_function.sol new file mode 100644 index 00000000..8492e691 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/399_does_not_warn_msg_value_in_internal_function.sol @@ -0,0 +1,5 @@ +contract C { + function f() view internal { + msg.value; + } +} diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/400_does_not_warn_msg_value_in_library.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/400_does_not_warn_msg_value_in_library.sol new file mode 100644 index 00000000..ce59047e --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/400_does_not_warn_msg_value_in_library.sol @@ -0,0 +1,5 @@ +library C { + function f() view public { + msg.value; + } +} diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/401_does_not_warn_msg_value_in_modifier_following_non_payable_public_function.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/401_does_not_warn_msg_value_in_modifier_following_non_payable_public_function.sol new file mode 100644 index 00000000..dc1da7c4 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/401_does_not_warn_msg_value_in_modifier_following_non_payable_public_function.sol @@ -0,0 +1,4 @@ +contract c { + function f() pure public { } + modifier m() { msg.value; _; } +} diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/402_assignment_to_constant.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/402_assignment_to_constant.sol new file mode 100644 index 00000000..7433bdea --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/402_assignment_to_constant.sol @@ -0,0 +1,6 @@ +contract c { + uint constant a = 1; + function f() public { a = 2; } +} +// ---- +// TypeError: (64-65): Cannot assign to a constant variable. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/403_return_structs.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/403_return_structs.sol new file mode 100644 index 00000000..2575954e --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/403_return_structs.sol @@ -0,0 +1,10 @@ +pragma experimental ABIEncoderV2; +contract C { + struct S { uint a; T[] sub; } + struct T { uint[] x; } + function f() public returns (uint, S memory) { + } +} +// ---- +// Warning: (0-33): Experimental features are turned on. Do not use experimental features on live deployments. +// Warning: (112-164): Function state mutability can be restricted to pure diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/404_read_returned_struct.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/404_read_returned_struct.sol new file mode 100644 index 00000000..52d1bd13 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/404_read_returned_struct.sol @@ -0,0 +1,12 @@ +pragma experimental ABIEncoderV2; +contract A { + struct T { + int x; + int y; + } + function g() public returns (T memory) { + return this.g(); + } +} +// ---- +// Warning: (0-33): Experimental features are turned on. Do not use experimental features on live deployments. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/405_address_checksum_type_deduction.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/405_address_checksum_type_deduction.sol new file mode 100644 index 00000000..81cc7d0d --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/405_address_checksum_type_deduction.sol @@ -0,0 +1,6 @@ +contract C { + function f() public { + (0xfA0bFc97E48458494Ccd857e1A85DC91F7F0046E).transfer(2); + } +} +// ---- diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/406_invalid_address_checksum.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/406_invalid_address_checksum.sol new file mode 100644 index 00000000..fe4691c2 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/406_invalid_address_checksum.sol @@ -0,0 +1,8 @@ +contract C { + function f() pure public { + address x = 0xFA0bFc97E48458494Ccd857e1A85DC91F7F0046E; + x; + } +} +// ---- +// SyntaxError: (64-106): This looks like an address but has an invalid checksum. Correct checksummed address: "0xfA0bFc97E48458494Ccd857e1A85DC91F7F0046E". If this is not used as an address, please prepend '00'. For more information please see https://solidity.readthedocs.io/en/develop/types.html#address-literals diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/407_invalid_address_no_checksum.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/407_invalid_address_no_checksum.sol new file mode 100644 index 00000000..6f4ac730 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/407_invalid_address_no_checksum.sol @@ -0,0 +1,8 @@ +contract C { + function f() pure public { + address x = 0xfa0bfc97e48458494ccd857e1a85dc91f7f0046e; + x; + } +} +// ---- +// SyntaxError: (64-106): This looks like an address but has an invalid checksum. Correct checksummed address: "0xfA0bFc97E48458494Ccd857e1A85DC91F7F0046E". If this is not used as an address, please prepend '00'. For more information please see https://solidity.readthedocs.io/en/develop/types.html#address-literals diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/408_invalid_address_length_short.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/408_invalid_address_length_short.sol new file mode 100644 index 00000000..da5dc380 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/408_invalid_address_length_short.sol @@ -0,0 +1,8 @@ +contract C { + function f() pure public { + address x = 0xA0bFc97E48458494Ccd857e1A85DC91F7F0046E; + x; + } +} +// ---- +// SyntaxError: (64-105): This looks like an address but is not exactly 40 hex digits. It is 39 hex digits. If this is not used as an address, please prepend '00'. For more information please see https://solidity.readthedocs.io/en/develop/types.html#address-literals diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/409_invalid_address_length_long.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/409_invalid_address_length_long.sol new file mode 100644 index 00000000..749612c9 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/409_invalid_address_length_long.sol @@ -0,0 +1,8 @@ +contract C { + function f() pure public { + address x = 0xFA0bFc97E48458494Ccd857e1A85DC91F7F0046E0; + x; + } +} +// ---- +// SyntaxError: (64-107): This looks like an address but is not exactly 40 hex digits. It is 41 hex digits. If this is not used as an address, please prepend '00'. For more information please see https://solidity.readthedocs.io/en/develop/types.html#address-literals diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/410_string_literal_not_convertible_to_address_as_assignment.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/410_string_literal_not_convertible_to_address_as_assignment.sol new file mode 100644 index 00000000..13bd1a8f --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/410_string_literal_not_convertible_to_address_as_assignment.sol @@ -0,0 +1,6 @@ +// A previous implementation claimed the string would be an address +contract AddrString { + address public test = "0xCA35b7d915458EF540aDe6068dFe2F44E8fa733c"; +} +// ---- +// TypeError: (116-160): Type literal_string "0xCA35b7d915458EF540aDe6068dFe2F44E8fa733c" is not implicitly convertible to expected type address. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/411_string_literal_not_convertible_to_address_as_return_value.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/411_string_literal_not_convertible_to_address_as_return_value.sol new file mode 100644 index 00000000..d6b7b987 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/411_string_literal_not_convertible_to_address_as_return_value.sol @@ -0,0 +1,8 @@ +// A previous implementation claimed the string would be an address +contract AddrString { + function f() public returns (address) { + return "0xCA35b7d915458EF540aDe6068dFe2F44E8fa733c"; + } +} +// ---- +// TypeError: (149-193): Return argument type literal_string "0xCA35b7d915458EF540aDe6068dFe2F44E8fa733c" is not implicitly convertible to expected type (type of first return variable) address. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/412_early_exit_on_fatal_errors.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/412_early_exit_on_fatal_errors.sol new file mode 100644 index 00000000..d052dab5 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/412_early_exit_on_fatal_errors.sol @@ -0,0 +1,12 @@ +// This tests a crash that occurred because we did not stop for fatal errors. +contract C { + struct S { + ftring a; + } + S public s; + function s() public s { + } +} +// ---- +// DeclarationError: (150-179): Identifier already declared. +// DeclarationError: (114-120): Identifier not found or not unique. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/413_address_methods.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/413_address_methods.sol new file mode 100644 index 00000000..ad57224c --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/413_address_methods.sol @@ -0,0 +1,12 @@ +contract C { + function f() public { + address payable addr; + uint balance = addr.balance; + (bool callSuc,) = addr.call(""); + (bool delegatecallSuc,) = addr.delegatecall(""); + bool sendRet = addr.send(1); + addr.transfer(1); + balance; callSuc; delegatecallSuc; sendRet; + } +} +// ---- diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/414_interface.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/414_interface.sol new file mode 100644 index 00000000..77baf7bf --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/414_interface.sol @@ -0,0 +1,2 @@ +interface I { +} diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/415_interface_functions.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/415_interface_functions.sol new file mode 100644 index 00000000..a5d6561e --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/415_interface_functions.sol @@ -0,0 +1,5 @@ +interface I { + function() external; + function f() external; +} +// ---- diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/416_interface_function_bodies.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/416_interface_function_bodies.sol new file mode 100644 index 00000000..fee2525e --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/416_interface_function_bodies.sol @@ -0,0 +1,6 @@ +interface I { + function f() external pure { + } +} +// ---- +// TypeError: (18-52): Functions in interfaces cannot have an implementation. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/417_interface_events.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/417_interface_events.sol new file mode 100644 index 00000000..5959f50d --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/417_interface_events.sol @@ -0,0 +1,3 @@ +interface I { + event E(); +} diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/418_interface_inheritance.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/418_interface_inheritance.sol new file mode 100644 index 00000000..92683cda --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/418_interface_inheritance.sol @@ -0,0 +1,6 @@ +interface A { +} +interface I is A { +} +// ---- +// TypeError: (31-32): Interfaces cannot inherit. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/419_interface_structs.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/419_interface_structs.sol new file mode 100644 index 00000000..385ed18e --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/419_interface_structs.sol @@ -0,0 +1,6 @@ +interface I { + struct A { + int dummy; + } +} +// ---- diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/420_interface_variables.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/420_interface_variables.sol new file mode 100644 index 00000000..a4292c41 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/420_interface_variables.sol @@ -0,0 +1,5 @@ +interface I { + uint a; +} +// ---- +// TypeError: (18-24): Variables cannot be declared in interfaces. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/421_interface_function_parameters.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/421_interface_function_parameters.sol new file mode 100644 index 00000000..9722e936 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/421_interface_function_parameters.sol @@ -0,0 +1,4 @@ +interface I { + function f(uint a) external returns (bool); +} +// ---- diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/422_interface_enums.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/422_interface_enums.sol new file mode 100644 index 00000000..1533e7ff --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/422_interface_enums.sol @@ -0,0 +1,4 @@ +interface I { + enum A { B, C } +} +// ---- diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/423_using_interface.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/423_using_interface.sol new file mode 100644 index 00000000..d576bb60 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/423_using_interface.sol @@ -0,0 +1,8 @@ +interface I { + function f() external; +} +contract C is I { + function f() public { + } +} +// ---- diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/424_using_interface_complex.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/424_using_interface_complex.sol new file mode 100644 index 00000000..a3dca996 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/424_using_interface_complex.sol @@ -0,0 +1,11 @@ +interface I { + event A(); + function f() external; + function g() external; + function() external; +} +contract C is I { + function f() public { + } +} +// ---- diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/425_interface_implement_public_contract.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/425_interface_implement_public_contract.sol new file mode 100644 index 00000000..d8540288 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/425_interface_implement_public_contract.sol @@ -0,0 +1,7 @@ +interface I { + function f() external; +} +contract C is I { + function f() public { + } +} diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/426_throw_is_deprecated.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/426_throw_is_deprecated.sol new file mode 100644 index 00000000..24f36c5b --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/426_throw_is_deprecated.sol @@ -0,0 +1,7 @@ +contract C { + function f() pure public { + throw; + } +} +// ---- +// SyntaxError: (52-57): "throw" is deprecated in favour of "revert()", "require()" and "assert()". diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/428_bare_revert.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/428_bare_revert.sol new file mode 100644 index 00000000..8e7817ff --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/428_bare_revert.sol @@ -0,0 +1,8 @@ +contract C { + function f(uint x) pure public { + if (x > 7) + revert; + } +} +// ---- +// TypeError: (81-87): No matching declaration found after variable lookup. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/429_revert_with_reason.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/429_revert_with_reason.sol new file mode 100644 index 00000000..36c238be --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/429_revert_with_reason.sol @@ -0,0 +1,8 @@ +contract C { + function f(uint x) pure public { + if (x > 7) + revert("abc"); + else + revert(); + } +} diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/430_bare_selfdestruct.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/430_bare_selfdestruct.sol new file mode 100644 index 00000000..9adc3d39 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/430_bare_selfdestruct.sol @@ -0,0 +1,5 @@ +contract C { + function f() pure public { selfdestruct; } +} +// ---- +// Warning: (44-56): Statement has no effect. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/431_bare_assert.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/431_bare_assert.sol new file mode 100644 index 00000000..38cea057 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/431_bare_assert.sol @@ -0,0 +1,5 @@ +contract C { + function f() pure public { assert; } +} +// ---- +// Warning: (44-50): Statement has no effect. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/432_bare_require.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/432_bare_require.sol new file mode 100644 index 00000000..62fe8baf --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/432_bare_require.sol @@ -0,0 +1,6 @@ +contract C { + // This is different because it does have overloads. + function f() pure public { require; } +} +// ---- +// TypeError: (101-108): No matching declaration found after variable lookup. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/433_pure_statement_in_for_loop.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/433_pure_statement_in_for_loop.sol new file mode 100644 index 00000000..8cb090bb --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/433_pure_statement_in_for_loop.sol @@ -0,0 +1,8 @@ +contract C { + function f() pure public { + for (uint x = 0; x < 10; true) + x++; + } +} +// ---- +// Warning: (77-81): Statement has no effect. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/434_pure_statement_check_for_regular_for_loop.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/434_pure_statement_check_for_regular_for_loop.sol new file mode 100644 index 00000000..319e4202 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/434_pure_statement_check_for_regular_for_loop.sol @@ -0,0 +1,6 @@ +contract C { + function f() pure public { + for (uint x = 0; true; x++) + {} + } +} diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/435_warn_unused_local.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/435_warn_unused_local.sol new file mode 100644 index 00000000..7d7f5728 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/435_warn_unused_local.sol @@ -0,0 +1,7 @@ +contract C { + function f() pure public { + uint a; + } +} +// ---- +// Warning: (52-58): Unused local variable. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/436_warn_unused_local_assigned.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/436_warn_unused_local_assigned.sol new file mode 100644 index 00000000..b3d28374 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/436_warn_unused_local_assigned.sol @@ -0,0 +1,7 @@ +contract C { + function f() pure public { + uint a = 1; + } +} +// ---- +// Warning: (52-58): Unused local variable. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/437_warn_unused_function_parameter.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/437_warn_unused_function_parameter.sol new file mode 100644 index 00000000..8a36eaad --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/437_warn_unused_function_parameter.sol @@ -0,0 +1,6 @@ +contract C { + function f(uint a) pure public { + } +} +// ---- +// Warning: (28-34): Unused function parameter. Remove or comment out the variable name to silence this warning. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/438_unused_unnamed_function_parameter.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/438_unused_unnamed_function_parameter.sol new file mode 100644 index 00000000..5d059b93 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/438_unused_unnamed_function_parameter.sol @@ -0,0 +1,4 @@ +contract C { + function f(uint) pure public { + } +} diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/439_warn_unused_return_parameter.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/439_warn_unused_return_parameter.sol new file mode 100644 index 00000000..b1422c4f --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/439_warn_unused_return_parameter.sol @@ -0,0 +1,6 @@ +contract C { + function f() pure public returns (uint a) { + } +} +// ---- +// Warning: (51-57): Unused function parameter. Remove or comment out the variable name to silence this warning. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/441_unused_unnamed_return_parameter.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/441_unused_unnamed_return_parameter.sol new file mode 100644 index 00000000..8c47484b --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/441_unused_unnamed_return_parameter.sol @@ -0,0 +1,4 @@ +contract C { + function f() pure public returns (uint) { + } +} diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/442_named_return_parameter.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/442_named_return_parameter.sol new file mode 100644 index 00000000..a2faf06a --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/442_named_return_parameter.sol @@ -0,0 +1,5 @@ +contract C { + function f() pure public returns (uint a) { + a = 1; + } +} diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/443_named_return_parameter_with_explicit_return.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/443_named_return_parameter_with_explicit_return.sol new file mode 100644 index 00000000..93851e7c --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/443_named_return_parameter_with_explicit_return.sol @@ -0,0 +1,5 @@ +contract C { + function f() pure public returns (uint a) { + return 1; + } +} diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/444_unnamed_return_parameter_with_explicit_return.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/444_unnamed_return_parameter_with_explicit_return.sol new file mode 100644 index 00000000..b552a745 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/444_unnamed_return_parameter_with_explicit_return.sol @@ -0,0 +1,5 @@ +contract C { + function f() pure public returns (uint) { + return 1; + } +} diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/445_no_unused_warning_interface_arguments.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/445_no_unused_warning_interface_arguments.sol new file mode 100644 index 00000000..203217ce --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/445_no_unused_warning_interface_arguments.sol @@ -0,0 +1,3 @@ +interface I { + function f(uint a) pure external returns (uint b); +} diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/446_no_unused_warning_abstract_arguments.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/446_no_unused_warning_abstract_arguments.sol new file mode 100644 index 00000000..fbb6e079 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/446_no_unused_warning_abstract_arguments.sol @@ -0,0 +1,3 @@ +contract C { + function f(uint a) pure public returns (uint b); +} diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/447_no_unused_warnings.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/447_no_unused_warnings.sol new file mode 100644 index 00000000..f549308a --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/447_no_unused_warnings.sol @@ -0,0 +1,6 @@ +contract C { + function f(uint a) pure public returns (uint b) { + uint c = 1; + b = a + c; + } +} diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/449_no_unused_inline_asm.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/449_no_unused_inline_asm.sol new file mode 100644 index 00000000..2b39b9fd --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/449_no_unused_inline_asm.sol @@ -0,0 +1,8 @@ +contract C { + function f() pure public { + uint a; + assembly { + a := 1 + } + } +} diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/450_shadowing_builtins_with_functions.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/450_shadowing_builtins_with_functions.sol new file mode 100644 index 00000000..33ccb356 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/450_shadowing_builtins_with_functions.sol @@ -0,0 +1,5 @@ +contract C { + function keccak256() pure public {} +} +// ---- +// Warning: (17-52): This declaration shadows a builtin symbol. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/451_shadowing_builtins_with_variables.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/451_shadowing_builtins_with_variables.sol new file mode 100644 index 00000000..1d6f098e --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/451_shadowing_builtins_with_variables.sol @@ -0,0 +1,8 @@ +contract C { + function f() pure public { + uint msg; + msg; + } +} +// ---- +// Warning: (52-60): This declaration shadows a builtin symbol. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/452_shadowing_builtins_with_storage_variables.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/452_shadowing_builtins_with_storage_variables.sol new file mode 100644 index 00000000..d5635887 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/452_shadowing_builtins_with_storage_variables.sol @@ -0,0 +1,5 @@ +contract C { + uint msg; +} +// ---- +// Warning: (17-25): This declaration shadows a builtin symbol. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/453_shadowing_builtin_at_global_scope.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/453_shadowing_builtin_at_global_scope.sol new file mode 100644 index 00000000..0946dc57 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/453_shadowing_builtin_at_global_scope.sol @@ -0,0 +1,4 @@ +contract msg { +} +// ---- +// Warning: (0-16): This declaration shadows a builtin symbol. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/454_shadowing_builtins_with_parameters.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/454_shadowing_builtins_with_parameters.sol new file mode 100644 index 00000000..454929d1 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/454_shadowing_builtins_with_parameters.sol @@ -0,0 +1,7 @@ +contract C { + function f(uint require) pure public { + require = 2; + } +} +// ---- +// Warning: (28-40): This declaration shadows a builtin symbol. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/455_shadowing_builtins_with_return_parameters.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/455_shadowing_builtins_with_return_parameters.sol new file mode 100644 index 00000000..7931053f --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/455_shadowing_builtins_with_return_parameters.sol @@ -0,0 +1,7 @@ +contract C { + function f() pure public returns (uint require) { + require = 2; + } +} +// ---- +// Warning: (51-63): This declaration shadows a builtin symbol. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/456_shadowing_builtins_with_events.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/456_shadowing_builtins_with_events.sol new file mode 100644 index 00000000..e5b635df --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/456_shadowing_builtins_with_events.sol @@ -0,0 +1,5 @@ +contract C { + event keccak256(); +} +// ---- +// Warning: (17-35): This declaration shadows a builtin symbol. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/457_shadowing_builtins_ignores_struct.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/457_shadowing_builtins_ignores_struct.sol new file mode 100644 index 00000000..4c70b4ce --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/457_shadowing_builtins_ignores_struct.sol @@ -0,0 +1,5 @@ +contract C { + struct a { + uint msg; + } +} diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/458_shadowing_builtins_ignores_constructor.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/458_shadowing_builtins_ignores_constructor.sol new file mode 100644 index 00000000..86c0b4f0 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/458_shadowing_builtins_ignores_constructor.sol @@ -0,0 +1,3 @@ +contract C { + constructor() public {} +} diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/459_function_overload_is_not_shadowing.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/459_function_overload_is_not_shadowing.sol new file mode 100644 index 00000000..1b44b5c6 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/459_function_overload_is_not_shadowing.sol @@ -0,0 +1,4 @@ +contract C { + function f() pure public {} + function f(uint) pure public {} +} diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/460_function_override_is_not_shadowing.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/460_function_override_is_not_shadowing.sol new file mode 100644 index 00000000..c765ff00 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/460_function_override_is_not_shadowing.sol @@ -0,0 +1,4 @@ +contract D { function f() pure public {} } +contract C is D { + function f(uint) pure public {} +} diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/461_event_parameter_cannot_shadow_state_variable.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/461_event_parameter_cannot_shadow_state_variable.sol new file mode 100644 index 00000000..6e1f654d --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/461_event_parameter_cannot_shadow_state_variable.sol @@ -0,0 +1,4 @@ +contract C { + address a; + event E(address a); +} diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/462_callable_crash.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/462_callable_crash.sol new file mode 100644 index 00000000..188d00e0 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/462_callable_crash.sol @@ -0,0 +1,9 @@ +contract C { + struct S { uint a; bool x; } + S public s; + constructor() public { + 3({a: 1, x: true}); + } +} +// ---- +// TypeError: (97-115): Type is not callable diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/466_does_not_error_transfer_payable_fallback.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/466_does_not_error_transfer_payable_fallback.sol new file mode 100644 index 00000000..c343995f --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/466_does_not_error_transfer_payable_fallback.sol @@ -0,0 +1,15 @@ +// This used to be a test for a.transfer to generate a warning +// because A does not have a payable fallback function. + +contract A { + function() payable external {} +} + +contract B { + A a; + + function() external { + address(a).transfer(100); + } +} +// ---- diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/467_does_not_error_transfer_regular_function.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/467_does_not_error_transfer_regular_function.sol new file mode 100644 index 00000000..65b4a236 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/467_does_not_error_transfer_regular_function.sol @@ -0,0 +1,11 @@ +contract A { + function transfer() pure public {} +} + +contract B { + A a; + + function() external { + a.transfer(); + } +} diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/470_specified_storage_no_warn.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/470_specified_storage_no_warn.sol new file mode 100644 index 00000000..490a0032 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/470_specified_storage_no_warn.sol @@ -0,0 +1,8 @@ +contract C { + struct S { uint a; string b; } + S x; + function f() view public { + S storage y = x; + y; + } +} diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/471_unspecified_storage_fail.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/471_unspecified_storage_fail.sol new file mode 100644 index 00000000..de42ebd7 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/471_unspecified_storage_fail.sol @@ -0,0 +1,13 @@ +contract C { + struct S { uint a; } + S m_x; + uint[] m_y; + function f() view public { + S x = m_x; + uint[] y = m_y; + x; y; + } +} +// ---- +// TypeError: (104-107): Data location must be "storage" or "memory" for variable, but none was given. +// TypeError: (123-131): Data location must be "storage" or "memory" for variable, but none was given. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/473_storage_location_non_array_or_struct_disallowed.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/473_storage_location_non_array_or_struct_disallowed.sol new file mode 100644 index 00000000..fe846aa0 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/473_storage_location_non_array_or_struct_disallowed.sol @@ -0,0 +1,5 @@ +contract C { + function f(uint storage a) public { } +} +// ---- +// TypeError: (28-42): Data location can only be specified for array, struct or mapping types, but "storage" was given. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/474_storage_location_non_array_or_struct_disallowed_is_not_fatal.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/474_storage_location_non_array_or_struct_disallowed_is_not_fatal.sol new file mode 100644 index 00000000..e74db375 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/474_storage_location_non_array_or_struct_disallowed_is_not_fatal.sol @@ -0,0 +1,7 @@ +contract C { + function f(uint storage a) public { + a = f; + } +} +// ---- +// TypeError: (28-42): Data location can only be specified for array, struct or mapping types, but "storage" was given. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/475_implicit_conversion_disallowed.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/475_implicit_conversion_disallowed.sol new file mode 100644 index 00000000..232e701d --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/475_implicit_conversion_disallowed.sol @@ -0,0 +1,8 @@ +contract C { + function f() public returns (bytes4) { + uint32 tmp = 1; + return tmp; + } +} +// ---- +// TypeError: (95-98): Return argument type uint32 is not implicitly convertible to expected type (type of first return variable) bytes4. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/476_too_large_arrays_for_calldata_external.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/476_too_large_arrays_for_calldata_external.sol new file mode 100644 index 00000000..78c38aaf --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/476_too_large_arrays_for_calldata_external.sol @@ -0,0 +1,6 @@ +contract C { + function f(uint[85678901234] calldata a) pure external { + } +} +// ---- +// TypeError: (28-56): Array is too large to be encoded. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/477_too_large_arrays_for_calldata_internal.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/477_too_large_arrays_for_calldata_internal.sol new file mode 100644 index 00000000..7578246e --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/477_too_large_arrays_for_calldata_internal.sol @@ -0,0 +1,6 @@ +contract C { + function f(uint[85678901234] memory a) pure internal { + } +} +// ---- +// TypeError: (28-54): Array is too large to be encoded. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/478_too_large_arrays_for_calldata_public.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/478_too_large_arrays_for_calldata_public.sol new file mode 100644 index 00000000..2831b6fb --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/478_too_large_arrays_for_calldata_public.sol @@ -0,0 +1,6 @@ +contract C { + function f(uint[85678901234] memory a) pure public { + } +} +// ---- +// TypeError: (28-54): Array is too large to be encoded. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/479_explicit_literal_to_memory_string_assignment.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/479_explicit_literal_to_memory_string_assignment.sol new file mode 100644 index 00000000..508a9439 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/479_explicit_literal_to_memory_string_assignment.sol @@ -0,0 +1,6 @@ +contract C { + function f() pure public { + string memory x = "abc"; + x; + } +} diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/480_explicit_literal_to_storage_string_assignment.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/480_explicit_literal_to_storage_string_assignment.sol new file mode 100644 index 00000000..ee56204a --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/480_explicit_literal_to_storage_string_assignment.sol @@ -0,0 +1,7 @@ +contract C { + function f() pure public { + string storage x = "abc"; + } +} +// ---- +// TypeError: (52-76): Type literal_string "abc" is not implicitly convertible to expected type string storage pointer. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/481_explicit_literal_to_unspecified_string_assignment.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/481_explicit_literal_to_unspecified_string_assignment.sol new file mode 100644 index 00000000..ee56204a --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/481_explicit_literal_to_unspecified_string_assignment.sol @@ -0,0 +1,7 @@ +contract C { + function f() pure public { + string storage x = "abc"; + } +} +// ---- +// TypeError: (52-76): Type literal_string "abc" is not implicitly convertible to expected type string storage pointer. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/482_explicit_literal_to_unspecified_string.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/482_explicit_literal_to_unspecified_string.sol new file mode 100644 index 00000000..c44fab55 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/482_explicit_literal_to_unspecified_string.sol @@ -0,0 +1,7 @@ +contract C { + function f() pure public { + string("abc"); + } +} +// ---- +// Warning: (52-65): Statement has no effect. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/483_modifiers_access_storage_pointer.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/483_modifiers_access_storage_pointer.sol new file mode 100644 index 00000000..be1920e9 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/483_modifiers_access_storage_pointer.sol @@ -0,0 +1,7 @@ +contract C { + struct S { uint a; } + modifier m(S storage x) { + x; + _; + } +} diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/484_function_types_selector_1.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/484_function_types_selector_1.sol new file mode 100644 index 00000000..41ef95c5 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/484_function_types_selector_1.sol @@ -0,0 +1,7 @@ +contract C { + function f() public view returns (bytes4) { + return f.selector; + } +} +// ---- +// TypeError: (76-86): Member "selector" not found or not visible after argument-dependent lookup in function () view returns (bytes4). diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/485_function_types_selector_2.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/485_function_types_selector_2.sol new file mode 100644 index 00000000..d02b098d --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/485_function_types_selector_2.sol @@ -0,0 +1,9 @@ +contract C { + function g() pure internal { + } + function f() public view returns (bytes4) { + return g.selector; + } +} +// ---- +// TypeError: (115-125): Member "selector" not found or not visible after argument-dependent lookup in function () pure. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/486_function_types_selector_3.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/486_function_types_selector_3.sol new file mode 100644 index 00000000..d39fcc28 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/486_function_types_selector_3.sol @@ -0,0 +1,8 @@ +contract C { + function f() public view returns (bytes4) { + function () g; + return g.selector; + } +} +// ---- +// TypeError: (99-109): Member "selector" not found or not visible after argument-dependent lookup in function (). diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/487_function_types_selector_4.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/487_function_types_selector_4.sol new file mode 100644 index 00000000..4c3c72e8 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/487_function_types_selector_4.sol @@ -0,0 +1,5 @@ +contract C { + function f() pure external returns (bytes4) { + return this.f.selector; + } +} diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/488_function_types_selector_5.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/488_function_types_selector_5.sol new file mode 100644 index 00000000..5f601db2 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/488_function_types_selector_5.sol @@ -0,0 +1,8 @@ +contract C { + function h() pure external { + } + function f() pure external returns (bytes4) { + return this.h.selector; + } +} +// ---- diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/489_function_types_selector_6.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/489_function_types_selector_6.sol new file mode 100644 index 00000000..0114e282 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/489_function_types_selector_6.sol @@ -0,0 +1,8 @@ +contract C { + function h() pure external { + } + function f() view external returns (bytes4) { + function () pure external g = this.h; + return g.selector; + } +} diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/490_function_types_selector_7.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/490_function_types_selector_7.sol new file mode 100644 index 00000000..9ee7d9bb --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/490_function_types_selector_7.sol @@ -0,0 +1,9 @@ +contract C { + function h() pure external { + } + function f() view external returns (bytes4) { + function () pure external g = this.h; + return g.selector; + } +} +// ---- diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/491_using_this_in_constructor.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/491_using_this_in_constructor.sol new file mode 100644 index 00000000..7921a1fa --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/491_using_this_in_constructor.sol @@ -0,0 +1,9 @@ +contract C { + constructor() public { + this.f(); + } + function f() pure public { + } +} +// ---- +// Warning: (48-52): "this" used in constructor. Note that external functions of a contract cannot be called while it is being constructed. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/492_do_not_crash_on_not_lvalue.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/492_do_not_crash_on_not_lvalue.sol new file mode 100644 index 00000000..90275804 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/492_do_not_crash_on_not_lvalue.sol @@ -0,0 +1,11 @@ +// This checks for a bug that caused a crash because of continued analysis. +contract C { + mapping (uint => uint) m; + function f() public { + m(1) = 2; + } +} +// ---- +// TypeError: (153-157): Type is not callable +// TypeError: (153-157): Expression has to be an lvalue. +// TypeError: (160-161): Type int_const 2 is not implicitly convertible to expected type tuple(). diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/493_builtin_keccak256_reject_gas.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/493_builtin_keccak256_reject_gas.sol new file mode 100644 index 00000000..e4113906 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/493_builtin_keccak256_reject_gas.sol @@ -0,0 +1,7 @@ +contract C { + function f() public { + keccak256.gas(); + } +} +// ---- +// TypeError: (47-60): Member "gas" not found or not visible after argument-dependent lookup in function (bytes memory) pure returns (bytes32). diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/494_builtin_sha256_reject_gas.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/494_builtin_sha256_reject_gas.sol new file mode 100644 index 00000000..20031ea9 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/494_builtin_sha256_reject_gas.sol @@ -0,0 +1,7 @@ +contract C { + function f() public { + sha256.gas(); + } +} +// ---- +// TypeError: (47-57): Member "gas" not found or not visible after argument-dependent lookup in function (bytes memory) pure returns (bytes32). diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/495_builtin_ripemd160_reject_gas.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/495_builtin_ripemd160_reject_gas.sol new file mode 100644 index 00000000..3d37e988 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/495_builtin_ripemd160_reject_gas.sol @@ -0,0 +1,7 @@ +contract C { + function f() public { + ripemd160.gas(); + } +} +// ---- +// TypeError: (47-60): Member "gas" not found or not visible after argument-dependent lookup in function (bytes memory) pure returns (bytes20). diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/496_builtin_ecrecover_reject_gas.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/496_builtin_ecrecover_reject_gas.sol new file mode 100644 index 00000000..82b6c89d --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/496_builtin_ecrecover_reject_gas.sol @@ -0,0 +1,7 @@ +contract C { + function f() public { + ecrecover.gas(); + } +} +// ---- +// TypeError: (47-60): Member "gas" not found or not visible after argument-dependent lookup in function (bytes32,uint8,bytes32,bytes32) pure returns (address). diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/497_gasleft.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/497_gasleft.sol new file mode 100644 index 00000000..20f33887 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/497_gasleft.sol @@ -0,0 +1,3 @@ +contract C { + function f() public view returns (uint256 val) { return gasleft(); } +} diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/498_msg_gas_deprecated.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/498_msg_gas_deprecated.sol new file mode 100644 index 00000000..5efecd22 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/498_msg_gas_deprecated.sol @@ -0,0 +1,5 @@ +contract C { + function f() public view returns (uint256 val) { return msg.gas; } +} +// ---- +// TypeError: (73-80): "msg.gas" has been deprecated in favor of "gasleft()" diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/500_gasleft_shadowing_1.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/500_gasleft_shadowing_1.sol new file mode 100644 index 00000000..66b88c49 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/500_gasleft_shadowing_1.sol @@ -0,0 +1,6 @@ +contract C { + function gasleft() public pure returns (bytes32 val) { return "abc"; } + function f() public pure returns (bytes32 val) { return gasleft(); } +} +// ---- +// Warning: (17-87): This declaration shadows a builtin symbol. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/501_gasleft_shadowing_2.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/501_gasleft_shadowing_2.sol new file mode 100644 index 00000000..2679c89d --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/501_gasleft_shadowing_2.sol @@ -0,0 +1,6 @@ +contract C { + uint gasleft; + function f() public { gasleft = 42; } +} +// ---- +// Warning: (17-29): This declaration shadows a builtin symbol. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/502_builtin_keccak256_reject_value.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/502_builtin_keccak256_reject_value.sol new file mode 100644 index 00000000..61e51eff --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/502_builtin_keccak256_reject_value.sol @@ -0,0 +1,7 @@ +contract C { + function f() public { + keccak256.value(); + } +} +// ---- +// TypeError: (47-62): Member "value" not found or not visible after argument-dependent lookup in function (bytes memory) pure returns (bytes32) - did you forget the "payable" modifier? diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/503_builtin_sha256_reject_value.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/503_builtin_sha256_reject_value.sol new file mode 100644 index 00000000..11141a9b --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/503_builtin_sha256_reject_value.sol @@ -0,0 +1,7 @@ +contract C { + function f() public { + sha256.value(); + } +} +// ---- +// TypeError: (47-59): Member "value" not found or not visible after argument-dependent lookup in function (bytes memory) pure returns (bytes32) - did you forget the "payable" modifier? diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/504_builtin_ripemd160_reject_value.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/504_builtin_ripemd160_reject_value.sol new file mode 100644 index 00000000..d120f3dd --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/504_builtin_ripemd160_reject_value.sol @@ -0,0 +1,7 @@ +contract C { + function f() public { + ripemd160.value(); + } +} +// ---- +// TypeError: (47-62): Member "value" not found or not visible after argument-dependent lookup in function (bytes memory) pure returns (bytes20) - did you forget the "payable" modifier? diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/505_builtin_ecrecover_reject_value.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/505_builtin_ecrecover_reject_value.sol new file mode 100644 index 00000000..e0215901 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/505_builtin_ecrecover_reject_value.sol @@ -0,0 +1,7 @@ +contract C { + function f() public { + ecrecover.value(); + } +} +// ---- +// TypeError: (47-62): Member "value" not found or not visible after argument-dependent lookup in function (bytes32,uint8,bytes32,bytes32) pure returns (address) - did you forget the "payable" modifier? diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/506_large_storage_array_fine.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/506_large_storage_array_fine.sol new file mode 100644 index 00000000..13e6dd80 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/506_large_storage_array_fine.sol @@ -0,0 +1,3 @@ +contract C { + uint[2**64 - 1] x; +} diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/507_large_storage_array_simple.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/507_large_storage_array_simple.sol new file mode 100644 index 00000000..3f8ee996 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/507_large_storage_array_simple.sol @@ -0,0 +1,5 @@ +contract C { + uint[2**64] x; +} +// ---- +// Warning: (17-30): Variable covers a large part of storage and thus makes collisions likely. Either use mappings or dynamic arrays and allow their size to be increased only in small quantities per transaction. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/508_large_storage_arrays_combined.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/508_large_storage_arrays_combined.sol new file mode 100644 index 00000000..917dcec1 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/508_large_storage_arrays_combined.sol @@ -0,0 +1,5 @@ +contract C { + uint[200][200][2**30][][2**30] x; +} +// ---- +// Warning: (17-49): Variable covers a large part of storage and thus makes collisions likely. Either use mappings or dynamic arrays and allow their size to be increased only in small quantities per transaction. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/509_large_storage_arrays_struct.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/509_large_storage_arrays_struct.sol new file mode 100644 index 00000000..656201f4 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/509_large_storage_arrays_struct.sol @@ -0,0 +1,6 @@ +contract C { + struct S { uint[2**30] x; uint[2**50] y; } + S[2**20] x; +} +// ---- +// Warning: (64-74): Variable covers a large part of storage and thus makes collisions likely. Either use mappings or dynamic arrays and allow their size to be increased only in small quantities per transaction. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/510_large_storage_array_mapping.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/510_large_storage_array_mapping.sol new file mode 100644 index 00000000..046a27f7 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/510_large_storage_array_mapping.sol @@ -0,0 +1,5 @@ +contract C { + mapping(uint => uint[2**100]) x; +} +// ---- +// Warning: (17-48): Variable covers a large part of storage and thus makes collisions likely. Either use mappings or dynamic arrays and allow their size to be increased only in small quantities per transaction. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/511_library_function_without_implementation_public.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/511_library_function_without_implementation_public.sol new file mode 100644 index 00000000..fe5e4955 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/511_library_function_without_implementation_public.sol @@ -0,0 +1,4 @@ +library L { + // This can be used as an "interface", hence it is allowed. + function f() public; +} diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/512_library_function_without_implementation_internal.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/512_library_function_without_implementation_internal.sol new file mode 100644 index 00000000..d5dfb260 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/512_library_function_without_implementation_internal.sol @@ -0,0 +1,5 @@ +library L { + function f() internal; +} +// ---- +// TypeError: (16-38): Internal library function must be implemented if declared. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/513_library_function_without_implementation_private.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/513_library_function_without_implementation_private.sol new file mode 100644 index 00000000..70585e8c --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/513_library_function_without_implementation_private.sol @@ -0,0 +1,5 @@ +library L { + function f() private; +} +// ---- +// TypeError: (16-37): Internal library function must be implemented if declared. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/514_using_for_with_non_library.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/514_using_for_with_non_library.sol new file mode 100644 index 00000000..7e9612d0 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/514_using_for_with_non_library.sol @@ -0,0 +1,10 @@ +// This tests a crash that was resolved by making the first error fatal. +library L { + struct S { uint d; } + using S for S; + function f(S memory _s) internal { + _s.d = 1; + } +} +// ---- +// TypeError: (120-121): Library name expected. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/515_experimental_pragma_empty.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/515_experimental_pragma_empty.sol new file mode 100644 index 00000000..66afb7a2 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/515_experimental_pragma_empty.sol @@ -0,0 +1,3 @@ +pragma experimental; +// ---- +// SyntaxError: (0-20): Experimental feature name is missing. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/516_experimental_pragma_unknown_number_literal.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/516_experimental_pragma_unknown_number_literal.sol new file mode 100644 index 00000000..445c6f54 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/516_experimental_pragma_unknown_number_literal.sol @@ -0,0 +1,3 @@ +pragma experimental 123; +// ---- +// SyntaxError: (0-24): Unsupported experimental feature name. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/517_experimental_pragma_unknown_string_literal.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/517_experimental_pragma_unknown_string_literal.sol new file mode 100644 index 00000000..48d8b968 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/517_experimental_pragma_unknown_string_literal.sol @@ -0,0 +1,3 @@ +pragma experimental unsupportedName; +// ---- +// SyntaxError: (0-36): Unsupported experimental feature name. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/518_experimental_pragma_unknown_quoted_string_literal.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/518_experimental_pragma_unknown_quoted_string_literal.sol new file mode 100644 index 00000000..6405f062 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/518_experimental_pragma_unknown_quoted_string_literal.sol @@ -0,0 +1,3 @@ +pragma experimental "unsupportedName"; +// ---- +// SyntaxError: (0-38): Unsupported experimental feature name. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/519_experimental_pragma_empy_string_literal.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/519_experimental_pragma_empy_string_literal.sol new file mode 100644 index 00000000..1a1fde9c --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/519_experimental_pragma_empy_string_literal.sol @@ -0,0 +1,3 @@ +pragma experimental ""; +// ---- +// SyntaxError: (0-23): Empty experimental feature name is invalid. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/520_experimental_pragma_multiple_same_line.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/520_experimental_pragma_multiple_same_line.sol new file mode 100644 index 00000000..2eb2bf2a --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/520_experimental_pragma_multiple_same_line.sol @@ -0,0 +1,3 @@ +pragma experimental unsupportedName unsupportedName; +// ---- +// SyntaxError: (0-52): Stray arguments. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/521_experimental_pragma_test_warning.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/521_experimental_pragma_test_warning.sol new file mode 100644 index 00000000..5f6962f4 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/521_experimental_pragma_test_warning.sol @@ -0,0 +1,3 @@ +pragma experimental __test; +// ---- +// Warning: (0-27): Experimental features are turned on. Do not use experimental features on live deployments. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/522_experimental_pragma_duplicate.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/522_experimental_pragma_duplicate.sol new file mode 100644 index 00000000..ba772a21 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/522_experimental_pragma_duplicate.sol @@ -0,0 +1,5 @@ +pragma experimental __test; +pragma experimental __test; +// ---- +// Warning: (0-27): Experimental features are turned on. Do not use experimental features on live deployments. +// SyntaxError: (28-55): Duplicate experimental feature name. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/523_reject_interface_creation.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/523_reject_interface_creation.sol new file mode 100644 index 00000000..35bba5b3 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/523_reject_interface_creation.sol @@ -0,0 +1,8 @@ +interface I {} +contract C { + function f() public { + new I(); + } +} +// ---- +// TypeError: (62-67): Cannot instantiate an interface. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/524_accept_library_creation.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/524_accept_library_creation.sol new file mode 100644 index 00000000..6a5e97af --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/524_accept_library_creation.sol @@ -0,0 +1,6 @@ +library L {} +contract C { + function f() public { + new L(); + } +} diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/525_reject_interface_constructors.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/525_reject_interface_constructors.sol new file mode 100644 index 00000000..ad08eca6 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/525_reject_interface_constructors.sol @@ -0,0 +1,4 @@ +interface I {} +contract C is I(2) {} +// ---- +// TypeError: (29-33): Wrong argument count for constructor call: 1 arguments given but expected 0. Remove parentheses if you do not want to provide arguments here. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/526_fallback_marked_external.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/526_fallback_marked_external.sol new file mode 100644 index 00000000..6ac551e1 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/526_fallback_marked_external.sol @@ -0,0 +1,3 @@ +contract C { + function () external { } +} diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/527_fallback_marked_internal.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/527_fallback_marked_internal.sol new file mode 100644 index 00000000..b8e1c654 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/527_fallback_marked_internal.sol @@ -0,0 +1,5 @@ +contract C { + function () internal { } +} +// ---- +// TypeError: (17-41): Fallback function must be defined as "external". diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/528_fallback_marked_private.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/528_fallback_marked_private.sol new file mode 100644 index 00000000..6038a99f --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/528_fallback_marked_private.sol @@ -0,0 +1,5 @@ +contract C { + function () private { } +} +// ---- +// TypeError: (17-40): Fallback function must be defined as "external". diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/529_fallback_marked_public.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/529_fallback_marked_public.sol new file mode 100644 index 00000000..d9c1580f --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/529_fallback_marked_public.sol @@ -0,0 +1,5 @@ +contract C { + function () public { } +} +// ---- +// TypeError: (17-39): Fallback function must be defined as "external". diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/530_tuple_invalid_literal_too_large_for_uint.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/530_tuple_invalid_literal_too_large_for_uint.sol new file mode 100644 index 00000000..bbfe2206 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/530_tuple_invalid_literal_too_large_for_uint.sol @@ -0,0 +1,8 @@ +contract C { + function f() pure public { + uint x; + (x, ) = (1E111); + } +} +// ---- +// TypeError: (76-83): Type int_const 1000...(104 digits omitted)...0000 is not implicitly convertible to expected type tuple(uint256,). diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/531_tuple_invalid_literal_too_large_unassigned.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/531_tuple_invalid_literal_too_large_unassigned.sol new file mode 100644 index 00000000..6b9cbf79 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/531_tuple_invalid_literal_too_large_unassigned.sol @@ -0,0 +1,8 @@ +contract C { + function f() pure public { + uint x; + (x, ) = (1, 1E111); + } +} +// ---- +// TypeError: (80-85): Invalid rational number. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/532_tuple_invalid_literal_too_large_for_uint_multi.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/532_tuple_invalid_literal_too_large_for_uint_multi.sol new file mode 100644 index 00000000..a26f9c04 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/532_tuple_invalid_literal_too_large_for_uint_multi.sol @@ -0,0 +1,8 @@ +contract C { + function f() pure public { + uint x; + (x, ) = (1E111, 1); + } +} +// ---- +// TypeError: (77-82): Invalid rational number. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/533_tuple_invalid_literal_too_large_exp.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/533_tuple_invalid_literal_too_large_exp.sol new file mode 100644 index 00000000..9384ec53 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/533_tuple_invalid_literal_too_large_exp.sol @@ -0,0 +1,7 @@ +contract C { + function f() pure public { + (2**270, 1); + } +} +// ---- +// TypeError: (53-59): Invalid rational number. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/534_tuple_invalid_literal_too_large_expression.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/534_tuple_invalid_literal_too_large_expression.sol new file mode 100644 index 00000000..3c322444 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/534_tuple_invalid_literal_too_large_expression.sol @@ -0,0 +1,7 @@ +contract C { + function f() pure public { + ((2**270) / 2**100, 1); + } +} +// ---- +// Warning: (52-74): Statement has no effect. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/535_address_overload_resolution.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/535_address_overload_resolution.sol new file mode 100644 index 00000000..01b7b294 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/535_address_overload_resolution.sol @@ -0,0 +1,20 @@ +contract C { + function balance() public returns (uint) { + this.balance; // to avoid pureness warning + return 1; + } + function transfer(uint amount) public { + address(this).transfer(amount); // to avoid pureness warning + } + function() payable external { + } +} +contract D { + function f() public { + uint x = (new C()).balance(); + x; + (new C()).transfer(5); + } +} +// ---- +// Warning: (17-134): Function state mutability can be restricted to view diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/536_array_length_invalid_expression_negative_bool.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/536_array_length_invalid_expression_negative_bool.sol new file mode 100644 index 00000000..c92861eb --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/536_array_length_invalid_expression_negative_bool.sol @@ -0,0 +1,5 @@ +contract C { + uint[-true] ids; +} +// ---- +// TypeError: (22-27): Invalid array length, expected integer literal or constant expression. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/537_array_length_invalid_expression_int_divides_bool.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/537_array_length_invalid_expression_int_divides_bool.sol new file mode 100644 index 00000000..92e3c3cf --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/537_array_length_invalid_expression_int_divides_bool.sol @@ -0,0 +1,5 @@ +contract C { + uint[true/1] ids; +} +// ---- +// TypeError: (22-28): Invalid array length, expected integer literal or constant expression. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/538_array_length_invalid_expression_bool_divides_int.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/538_array_length_invalid_expression_bool_divides_int.sol new file mode 100644 index 00000000..26add45c --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/538_array_length_invalid_expression_bool_divides_int.sol @@ -0,0 +1,5 @@ +contract C { + uint[1/true] ids; +} +// ---- +// TypeError: (22-28): Invalid array length, expected integer literal or constant expression. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/539_array_length_invalid_expression_scientific_literal.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/539_array_length_invalid_expression_scientific_literal.sol new file mode 100644 index 00000000..a0d58f4a --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/539_array_length_invalid_expression_scientific_literal.sol @@ -0,0 +1,5 @@ +contract C { + uint[1.111111E1111111111111] ids; +} +// ---- +// TypeError: (22-44): Invalid array length, expected integer literal or constant expression. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/540_array_length_invalid_expression_division_by_zero.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/540_array_length_invalid_expression_division_by_zero.sol new file mode 100644 index 00000000..38a80867 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/540_array_length_invalid_expression_division_by_zero.sol @@ -0,0 +1,5 @@ +contract C { + uint[3/0] ids; +} +// ---- +// TypeError: (22-25): Operator / not compatible with types int_const 3 and int_const 0 diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/541_warn_about_address_members_on_contract_balance.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/541_warn_about_address_members_on_contract_balance.sol new file mode 100644 index 00000000..39edaa2d --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/541_warn_about_address_members_on_contract_balance.sol @@ -0,0 +1,7 @@ +contract C { + function f() view public { + this.balance; + } +} +// ---- +// TypeError: (52-64): Member "balance" not found or not visible after argument-dependent lookup in contract C. Use "address(this).balance" to access this address member. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/542_warn_about_address_members_on_contract_transfer.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/542_warn_about_address_members_on_contract_transfer.sol new file mode 100644 index 00000000..a44cc6d2 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/542_warn_about_address_members_on_contract_transfer.sol @@ -0,0 +1,7 @@ +contract C { + function f() view public { + this.transfer; + } +} +// ---- +// TypeError: (52-65): Member "transfer" not found or not visible after argument-dependent lookup in contract C. Use "address(this).transfer" to access this address member. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/543_warn_about_address_members_on_contract_send.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/543_warn_about_address_members_on_contract_send.sol new file mode 100644 index 00000000..e9e26a00 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/543_warn_about_address_members_on_contract_send.sol @@ -0,0 +1,7 @@ +contract C { + function f() view public { + this.send; + } +} +// ---- +// TypeError: (52-61): Member "send" not found or not visible after argument-dependent lookup in contract C. Use "address(this).send" to access this address member. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/544_warn_about_address_members_on_contract_call.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/544_warn_about_address_members_on_contract_call.sol new file mode 100644 index 00000000..16da7578 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/544_warn_about_address_members_on_contract_call.sol @@ -0,0 +1,7 @@ +contract C { + function f() view public { + this.call; + } +} +// ---- +// TypeError: (52-61): Member "call" not found or not visible after argument-dependent lookup in contract C. Use "address(this).call" to access this address member. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/545_warn_about_address_members_on_contract_callcode.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/545_warn_about_address_members_on_contract_callcode.sol new file mode 100644 index 00000000..9292f9c0 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/545_warn_about_address_members_on_contract_callcode.sol @@ -0,0 +1,7 @@ +contract C { + function f() view public { + this.callcode; + } +} +// ---- +// TypeError: (52-65): Member "callcode" not found or not visible after argument-dependent lookup in contract C. Use "address(this).callcode" to access this address member. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/546_warn_about_address_members_on_contract_delegatecall.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/546_warn_about_address_members_on_contract_delegatecall.sol new file mode 100644 index 00000000..20354991 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/546_warn_about_address_members_on_contract_delegatecall.sol @@ -0,0 +1,7 @@ +contract C { + function f() view public { + this.delegatecall; + } +} +// ---- +// TypeError: (52-69): Member "delegatecall" not found or not visible after argument-dependent lookup in contract C. Use "address(this).delegatecall" to access this address member. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/547_warn_about_address_members_on_non_this_contract_balance.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/547_warn_about_address_members_on_non_this_contract_balance.sol new file mode 100644 index 00000000..8a4734e6 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/547_warn_about_address_members_on_non_this_contract_balance.sol @@ -0,0 +1,8 @@ +contract C { + function f() view public { + C c; + c.balance; + } +} +// ---- +// TypeError: (65-74): Member "balance" not found or not visible after argument-dependent lookup in contract C. Use "address(c).balance" to access this address member. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/548_warn_about_address_members_on_non_this_contract_transfer.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/548_warn_about_address_members_on_non_this_contract_transfer.sol new file mode 100644 index 00000000..e617f540 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/548_warn_about_address_members_on_non_this_contract_transfer.sol @@ -0,0 +1,8 @@ +contract C { + function f() view public { + C c; + c.transfer; + } +} +// ---- +// TypeError: (65-75): Member "transfer" not found or not visible after argument-dependent lookup in contract C. Use "address(c).transfer" to access this address member. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/549_warn_about_address_members_on_non_this_contract_send.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/549_warn_about_address_members_on_non_this_contract_send.sol new file mode 100644 index 00000000..54965d4b --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/549_warn_about_address_members_on_non_this_contract_send.sol @@ -0,0 +1,8 @@ +contract C { + function f() view public { + C c; + c.send; + } +} +// ---- +// TypeError: (65-71): Member "send" not found or not visible after argument-dependent lookup in contract C. Use "address(c).send" to access this address member. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/550_warn_about_address_members_on_non_this_contract_call.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/550_warn_about_address_members_on_non_this_contract_call.sol new file mode 100644 index 00000000..940f383c --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/550_warn_about_address_members_on_non_this_contract_call.sol @@ -0,0 +1,8 @@ +contract C { + function f() pure public { + C c; + c.call; + } +} +// ---- +// TypeError: (65-71): Member "call" not found or not visible after argument-dependent lookup in contract C. Use "address(c).call" to access this address member. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/551_warn_about_address_members_on_non_this_contract_callcode.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/551_warn_about_address_members_on_non_this_contract_callcode.sol new file mode 100644 index 00000000..9d4725bd --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/551_warn_about_address_members_on_non_this_contract_callcode.sol @@ -0,0 +1,8 @@ +contract C { + function f() pure public { + C c; + c.callcode; + } +} +// ---- +// TypeError: (65-75): Member "callcode" not found or not visible after argument-dependent lookup in contract C. Use "address(c).callcode" to access this address member. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/552_warn_about_address_members_on_non_this_contract_delegatecall.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/552_warn_about_address_members_on_non_this_contract_delegatecall.sol new file mode 100644 index 00000000..9941ce1f --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/552_warn_about_address_members_on_non_this_contract_delegatecall.sol @@ -0,0 +1,8 @@ +contract C { + function f() pure public { + C c; + c.delegatecall; + } +} +// ---- +// TypeError: (65-79): Member "delegatecall" not found or not visible after argument-dependent lookup in contract C. Use "address(c).delegatecall" to access this address member. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/559_no_warning_for_using_members_that_look_like_address_members.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/559_no_warning_for_using_members_that_look_like_address_members.sol new file mode 100644 index 00000000..4c1870f1 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/559_no_warning_for_using_members_that_look_like_address_members.sol @@ -0,0 +1,6 @@ +contract C { + function transfer(uint) public; + function f() public { + this.transfer(10); + } +} diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/560_event_emit_simple.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/560_event_emit_simple.sol new file mode 100644 index 00000000..445c9949 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/560_event_emit_simple.sol @@ -0,0 +1,6 @@ +contract C { + event e(); + function f() public { + emit e(); + } +} diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/561_event_emit_complex.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/561_event_emit_complex.sol new file mode 100644 index 00000000..19448615 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/561_event_emit_complex.sol @@ -0,0 +1,7 @@ +contract C { + event e(uint a, string b); + function f() public { + emit e(2, "abc"); + emit e({b: "abc", a: 8}); + } +} diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/562_event_emit_foreign_class.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/562_event_emit_foreign_class.sol new file mode 100644 index 00000000..afac609a --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/562_event_emit_foreign_class.sol @@ -0,0 +1,7 @@ +contract A { event e(uint a, string b); } +contract C is A { + function f() public { + emit A.e(2, "abc"); + emit A.e({b: "abc", a: 8}); + } +} diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/563_event_without_emit_deprecated.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/563_event_without_emit_deprecated.sol new file mode 100644 index 00000000..e9a56671 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/563_event_without_emit_deprecated.sol @@ -0,0 +1,8 @@ +contract C { + event e(); + function f() public { + e(); + } +} +// ---- +// TypeError: (62-65): Event invocations have to be prefixed by "emit". diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/568_blockhash.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/568_blockhash.sol new file mode 100644 index 00000000..f6cc63a5 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/568_blockhash.sol @@ -0,0 +1,3 @@ +contract C { + function f() public view returns (bytes32) { return blockhash(3); } +} diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/569_block_blockhash_deprecated.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/569_block_blockhash_deprecated.sol new file mode 100644 index 00000000..b8f5d6a8 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/569_block_blockhash_deprecated.sol @@ -0,0 +1,7 @@ +contract C { + function f() public view returns (bytes32) { + return block.blockhash(3); + } +} +// ---- +// TypeError: (77-92): "block.blockhash()" has been deprecated in favor of "blockhash()" diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/570_function_type_undeclared_type.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/570_function_type_undeclared_type.sol new file mode 100644 index 00000000..962f4fe4 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/570_function_type_undeclared_type.sol @@ -0,0 +1,5 @@ +contract C { + function a(function(Nested)) external pure {} +} +// ---- +// DeclarationError: (37-43): Identifier not found or not unique. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/571_function_type_undeclared_type_external.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/571_function_type_undeclared_type_external.sol new file mode 100644 index 00000000..735af9e9 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/571_function_type_undeclared_type_external.sol @@ -0,0 +1,5 @@ +contract C { + function a(function(Nested) external) external pure {} +} +// ---- +// DeclarationError: (37-43): Identifier not found or not unique. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/572_function_type_undeclared_type_multi_nested.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/572_function_type_undeclared_type_multi_nested.sol new file mode 100644 index 00000000..ffb467cd --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/572_function_type_undeclared_type_multi_nested.sol @@ -0,0 +1,5 @@ +contract C { + function a(function(function(function(Nested)))) external pure {} +} +// ---- +// DeclarationError: (55-61): Identifier not found or not unique. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/573_similar_name_longer_than_80_not_suggested.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/573_similar_name_longer_than_80_not_suggested.sol new file mode 100644 index 00000000..c6719d8c --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/573_similar_name_longer_than_80_not_suggested.sol @@ -0,0 +1,7 @@ +contract test { + function f() public { + int YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY = YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY; + } +} +// ---- +// DeclarationError: (146-236): Undeclared identifier. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/574_similar_name_shorter_than_80_suggested.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/574_similar_name_shorter_than_80_suggested.sol new file mode 100644 index 00000000..61fb2d82 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/574_similar_name_shorter_than_80_suggested.sol @@ -0,0 +1,7 @@ +contract test { + function f() public { + int YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY = YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY; + } +} +// ---- +// DeclarationError: (137-216): Undeclared identifier. Did you mean "YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY"?
\ No newline at end of file diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/575_member_member_getter_call_without_parentheses.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/575_member_member_getter_call_without_parentheses.sol new file mode 100644 index 00000000..61f43103 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/575_member_member_getter_call_without_parentheses.sol @@ -0,0 +1,19 @@ +contract A{ + function f() public pure{ + + } +} +contract B{ + A public a; +} +contract C{ + B public b; +} +contract D{ + C c; + function f() public view{ + c.b.a.f(); + } +} +// ---- +// TypeError: (170-175): Member "a" not found or not visible after argument-dependent lookup in function () view external returns (contract B). Did you intend to call the function? diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/576_member_getter_call_without_parentheses.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/576_member_getter_call_without_parentheses.sol new file mode 100644 index 00000000..bdc5fdc7 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/576_member_getter_call_without_parentheses.sol @@ -0,0 +1,17 @@ +contract A{ + function f() public pure{ + + } +} +contract B{ + A public a; +} +contract C{ + B b; + function f() public view{ + b.a.f(); + } +} + +// ---- +// TypeError: (140-145): Member "f" not found or not visible after argument-dependent lookup in function () view external returns (contract A). Did you intend to call the function? diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/577_member_getter_call_without_parentheses_missing_function.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/577_member_getter_call_without_parentheses_missing_function.sol new file mode 100644 index 00000000..d204d926 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/577_member_getter_call_without_parentheses_missing_function.sol @@ -0,0 +1,15 @@ +contract A{ + +} +contract B{ + A public a; +} +contract C{ + B b; + function f() public view{ + b.a.f(); + } +} + +// ---- +// TypeError: (104-109): Member "f" not found or not visible after argument-dependent lookup in function () view external returns (contract A). Did you intend to call the function? diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/578_private_member_getter_call_without_parentheses.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/578_private_member_getter_call_without_parentheses.sol new file mode 100644 index 00000000..e71da372 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/578_private_member_getter_call_without_parentheses.sol @@ -0,0 +1,17 @@ +contract A{ + function f() public pure{ + + } +} +contract B{ + A private a; +} +contract C{ + B b; + function f() public view{ + b.a.f(); + } +} + +// ---- +// TypeError: (141-144): Member "a" not found or not visible after argument-dependent lookup in contract B. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/579_member_getter_call_without_parentheses_private_function.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/579_member_getter_call_without_parentheses_private_function.sol new file mode 100644 index 00000000..18932238 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/579_member_getter_call_without_parentheses_private_function.sol @@ -0,0 +1,17 @@ +contract A{ + function f() private pure{ + + } +} +contract B{ + A public a; +} +contract C{ + B b; + function f() public view{ + b.a.f(); + } +} + +// ---- +// TypeError: (141-146): Member "f" not found or not visible after argument-dependent lookup in function () view external returns (contract A). Did you intend to call the function? diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/580_improve_name_suggestion_one_and_two_letters.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/580_improve_name_suggestion_one_and_two_letters.sol new file mode 100644 index 00000000..8d2d071b --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/580_improve_name_suggestion_one_and_two_letters.sol @@ -0,0 +1,14 @@ +contract c { + function f () public + { + a = ac; + a = cd; + a = b; + } + uint256 a; + uint256 ab; +} +// ---- +// DeclarationError: (56-58): Undeclared identifier. Did you mean "ab"? +// DeclarationError: (72-74): Undeclared identifier. +// DeclarationError: (88-89): Undeclared identifier. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/581_improve_name_suggestion_three_letters.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/581_improve_name_suggestion_three_letters.sol new file mode 100644 index 00000000..69f5a7e2 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/581_improve_name_suggestion_three_letters.sol @@ -0,0 +1,12 @@ +contract c { + function f () public + { + a = abd; + a = ade; + } + uint256 a; + uint256 abc; +} +// ---- +// DeclarationError: (56-59): Undeclared identifier. Did you mean "abc" or "abi"? +// DeclarationError: (73-76): Undeclared identifier. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/582_improve_name_suggestion_four_letters.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/582_improve_name_suggestion_four_letters.sol new file mode 100644 index 00000000..547ea308 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/582_improve_name_suggestion_four_letters.sol @@ -0,0 +1,17 @@ +contract c { + function f () public + { + a = land; + a = lost; + a = lang; + } + uint256 long; + uint256 abc; +} +// ---- +// DeclarationError: (52-53): Undeclared identifier. +// DeclarationError: (56-60): Undeclared identifier. Did you mean "long"? +// DeclarationError: (70-71): Undeclared identifier. +// DeclarationError: (74-78): Undeclared identifier. Did you mean "long", "log0", "log1", "log2", "log3" or "log4"? +// DeclarationError: (88-89): Undeclared identifier. +// DeclarationError: (92-96): Undeclared identifier. Did you mean "long"? diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/583_abi_encode_packed_with_rational_number_constant.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/583_abi_encode_packed_with_rational_number_constant.sol new file mode 100644 index 00000000..6be591f6 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/583_abi_encode_packed_with_rational_number_constant.sol @@ -0,0 +1,5 @@ +contract C { + function f() pure public { abi.encodePacked(0/1); } +} +// ---- +// TypeError: (61-64): Cannot perform packed encoding for a literal. Please convert it to an explicit type first.
\ No newline at end of file diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/584_abi_decode_with_tuple_of_other_than_types.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/584_abi_decode_with_tuple_of_other_than_types.sol new file mode 100644 index 00000000..c95eeb35 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/584_abi_decode_with_tuple_of_other_than_types.sol @@ -0,0 +1,5 @@ +contract C { + function f() pure public { abi.decode("", (0)); } +} +// ---- +// TypeError: (60-61): Argument has to be a type name. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/constant_mapping.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/constant_mapping.sol new file mode 100644 index 00000000..61c0cc17 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/constant_mapping.sol @@ -0,0 +1,8 @@ +contract C { + // This should probably have a better error message at some point. + // Constant mappings should not be possible in general. + mapping(uint => uint) constant x; +} +// ---- +// TypeError: (148-180): Constants of non-value type not yet implemented. +// TypeError: (148-180): Uninitialized "constant" variable. diff --git a/test/libsolidity/syntaxTests/parsing/address_constant_payable.sol b/test/libsolidity/syntaxTests/parsing/address_constant_payable.sol new file mode 100644 index 00000000..d98f4ae3 --- /dev/null +++ b/test/libsolidity/syntaxTests/parsing/address_constant_payable.sol @@ -0,0 +1,5 @@ +contract C { + address constant payable b = address(0); +} +// ---- +// ParserError: (34-41): Expected identifier but got 'payable' diff --git a/test/libsolidity/syntaxTests/parsing/address_function_arguments_and_returns.sol b/test/libsolidity/syntaxTests/parsing/address_function_arguments_and_returns.sol new file mode 100644 index 00000000..c377600a --- /dev/null +++ b/test/libsolidity/syntaxTests/parsing/address_function_arguments_and_returns.sol @@ -0,0 +1,4 @@ +contract C { + function f(address) public pure returns (address) {} + function g(address payable) public pure returns (address payable) {} +} diff --git a/test/libsolidity/syntaxTests/parsing/address_in_struct.sol b/test/libsolidity/syntaxTests/parsing/address_in_struct.sol new file mode 100644 index 00000000..68049b50 --- /dev/null +++ b/test/libsolidity/syntaxTests/parsing/address_in_struct.sol @@ -0,0 +1,6 @@ +contract C { + struct S { + address payable a; + address b; + } +} diff --git a/test/libsolidity/syntaxTests/parsing/address_invalid_state_mutability.sol b/test/libsolidity/syntaxTests/parsing/address_invalid_state_mutability.sol new file mode 100644 index 00000000..606f5cd0 --- /dev/null +++ b/test/libsolidity/syntaxTests/parsing/address_invalid_state_mutability.sol @@ -0,0 +1,26 @@ +contract C { + address view m_a; + address pure m_b; + address view[] m_c; + mapping(uint => address view) m_d; + function f() public pure { + address view a; + address pure b; + a; b; + } + function g(address view) public pure {} + function h(address pure) public pure {} + function i() public pure returns (address view) {} + function j() public pure returns (address pure) {} +} +// ---- +// TypeError: (14-26): Address types can only be payable or non-payable. +// TypeError: (33-45): Address types can only be payable or non-payable. +// TypeError: (52-64): Address types can only be payable or non-payable. +// TypeError: (89-101): Address types can only be payable or non-payable. +// TypeError: (195-207): Address types can only be payable or non-payable. +// TypeError: (236-248): Address types can only be payable or non-payable. +// TypeError: (300-312): Address types can only be payable or non-payable. +// TypeError: (352-364): Address types can only be payable or non-payable. +// TypeError: (138-150): Address types can only be payable or non-payable. +// TypeError: (156-168): Address types can only be payable or non-payable. diff --git a/test/libsolidity/syntaxTests/parsing/address_nonpayable.sol b/test/libsolidity/syntaxTests/parsing/address_nonpayable.sol new file mode 100644 index 00000000..fea67138 --- /dev/null +++ b/test/libsolidity/syntaxTests/parsing/address_nonpayable.sol @@ -0,0 +1,7 @@ +contract C { + address a; + function f(address b) public pure returns (address c) { + address d = b; + return d; + } +} diff --git a/test/libsolidity/syntaxTests/parsing/address_payable.sol b/test/libsolidity/syntaxTests/parsing/address_payable.sol new file mode 100644 index 00000000..c29ae1b7 --- /dev/null +++ b/test/libsolidity/syntaxTests/parsing/address_payable.sol @@ -0,0 +1,7 @@ +contract C { + address payable a; + function f(address payable b) public pure returns (address payable c) { + address payable d = b; + return d; + } +} diff --git a/test/libsolidity/syntaxTests/parsing/address_payable_constant.sol b/test/libsolidity/syntaxTests/parsing/address_payable_constant.sol new file mode 100644 index 00000000..154bfb54 --- /dev/null +++ b/test/libsolidity/syntaxTests/parsing/address_payable_constant.sol @@ -0,0 +1,3 @@ +contract C { + address payable constant a = address(0); +} diff --git a/test/libsolidity/syntaxTests/parsing/address_payable_conversion.sol b/test/libsolidity/syntaxTests/parsing/address_payable_conversion.sol new file mode 100644 index 00000000..bf073a52 --- /dev/null +++ b/test/libsolidity/syntaxTests/parsing/address_payable_conversion.sol @@ -0,0 +1,7 @@ +contract C { + function f() public pure { + address payable a = address payable(this); + } +} +// ---- +// ParserError: (80-87): Expected ';' but got 'payable' diff --git a/test/libsolidity/syntaxTests/parsing/address_payable_function_type.sol b/test/libsolidity/syntaxTests/parsing/address_payable_function_type.sol new file mode 100644 index 00000000..234b528a --- /dev/null +++ b/test/libsolidity/syntaxTests/parsing/address_payable_function_type.sol @@ -0,0 +1,6 @@ +contract C { + function (address payable) view internal returns (address payable) f; + function g(function (address payable) payable external returns (address payable)) public payable returns (function (address payable) payable external returns (address payable)) { + function (address payable) payable external returns (address payable) h; h; + } +} diff --git a/test/libsolidity/syntaxTests/parsing/address_payable_library.sol b/test/libsolidity/syntaxTests/parsing/address_payable_library.sol new file mode 100644 index 00000000..33787d33 --- /dev/null +++ b/test/libsolidity/syntaxTests/parsing/address_payable_library.sol @@ -0,0 +1,5 @@ +library L { +} +contract C { + using L for address payable; +}
\ No newline at end of file diff --git a/test/libsolidity/syntaxTests/parsing/address_payable_local.sol b/test/libsolidity/syntaxTests/parsing/address_payable_local.sol new file mode 100644 index 00000000..544f7c21 --- /dev/null +++ b/test/libsolidity/syntaxTests/parsing/address_payable_local.sol @@ -0,0 +1,11 @@ +contract C { + mapping(uint => address payable) m; + mapping(uint => address payable[]) n; + function f() public view { + address payable a; + address payable[] memory b; + mapping(uint => address payable) storage c = m; + mapping(uint => address payable[]) storage d = n; + a; b; c; d; + } +} diff --git a/test/libsolidity/syntaxTests/parsing/address_payable_state_variable.sol b/test/libsolidity/syntaxTests/parsing/address_payable_state_variable.sol new file mode 100644 index 00000000..12c2468d --- /dev/null +++ b/test/libsolidity/syntaxTests/parsing/address_payable_state_variable.sol @@ -0,0 +1,8 @@ +contract C { + address payable a; + address payable public b; + address payable[] c; + address payable[] public d; + mapping(uint => address payable) e; + mapping(uint => address payable[]) f; +} diff --git a/test/libsolidity/syntaxTests/parsing/address_payable_struct.sol b/test/libsolidity/syntaxTests/parsing/address_payable_struct.sol new file mode 100644 index 00000000..6c281bc7 --- /dev/null +++ b/test/libsolidity/syntaxTests/parsing/address_payable_struct.sol @@ -0,0 +1,8 @@ +contract C { + struct S { + address payable a; + address payable[] b; + mapping(uint => address payable) c; + mapping(uint => address payable[]) d; + } +}
\ No newline at end of file diff --git a/test/libsolidity/syntaxTests/parsing/address_payable_type_expression.sol b/test/libsolidity/syntaxTests/parsing/address_payable_type_expression.sol new file mode 100644 index 00000000..394b39c0 --- /dev/null +++ b/test/libsolidity/syntaxTests/parsing/address_payable_type_expression.sol @@ -0,0 +1,7 @@ +contract C { + function f() public pure { + address payable; + } +} +// ---- +// ParserError: (67-68): Expected identifier but got ';' diff --git a/test/libsolidity/syntaxTests/parsing/address_public_payable_error.sol b/test/libsolidity/syntaxTests/parsing/address_public_payable_error.sol new file mode 100644 index 00000000..0acf5e4b --- /dev/null +++ b/test/libsolidity/syntaxTests/parsing/address_public_payable_error.sol @@ -0,0 +1,5 @@ +contract C { + address public payable a; +} +// ---- +// ParserError: (32-39): Expected identifier but got 'payable' diff --git a/test/libsolidity/syntaxTests/parsing/arrays_in_events.sol b/test/libsolidity/syntaxTests/parsing/arrays_in_events.sol new file mode 100644 index 00000000..edbc9665 --- /dev/null +++ b/test/libsolidity/syntaxTests/parsing/arrays_in_events.sol @@ -0,0 +1,3 @@ +contract c { + event e(uint[10] a, bytes7[8] indexed b, c[3] x); +} diff --git a/test/libsolidity/syntaxTests/parsing/arrays_in_expressions.sol b/test/libsolidity/syntaxTests/parsing/arrays_in_expressions.sol new file mode 100644 index 00000000..4c1f96e6 --- /dev/null +++ b/test/libsolidity/syntaxTests/parsing/arrays_in_expressions.sol @@ -0,0 +1,6 @@ +contract c { + function f() public { c[10] storage a = 7; uint8[10 * 2] storage x; } +} +// ---- +// TypeError: (39-58): Type int_const 7 is not implicitly convertible to expected type contract c[10] storage pointer. +// DeclarationError: (60-83): Uninitialized storage pointer. diff --git a/test/libsolidity/syntaxTests/parsing/arrays_in_storage.sol b/test/libsolidity/syntaxTests/parsing/arrays_in_storage.sol new file mode 100644 index 00000000..9adf6f12 --- /dev/null +++ b/test/libsolidity/syntaxTests/parsing/arrays_in_storage.sol @@ -0,0 +1,6 @@ +contract c { + uint[10] a; + uint[] a2; + struct x { uint[2**20] b; y[1] c; } + struct y { uint d; mapping(uint=>x)[] e; } +} diff --git a/test/libsolidity/syntaxTests/parsing/assembly_evmasm_type.sol b/test/libsolidity/syntaxTests/parsing/assembly_evmasm_type.sol new file mode 100644 index 00000000..28a07e63 --- /dev/null +++ b/test/libsolidity/syntaxTests/parsing/assembly_evmasm_type.sol @@ -0,0 +1,5 @@ +contract C { + function f() public pure { + assembly "evmasm" {} + } +} diff --git a/test/libsolidity/syntaxTests/parsing/assembly_invalid_type.sol b/test/libsolidity/syntaxTests/parsing/assembly_invalid_type.sol new file mode 100644 index 00000000..c2d39279 --- /dev/null +++ b/test/libsolidity/syntaxTests/parsing/assembly_invalid_type.sol @@ -0,0 +1,7 @@ +contract C { + function f() public pure { + assembly "failasm" {} + } +} +// ---- +// ParserError: (55-64): Only "evmasm" supported. diff --git a/test/libsolidity/syntaxTests/parsing/calling_function.sol b/test/libsolidity/syntaxTests/parsing/calling_function.sol new file mode 100644 index 00000000..9e88c451 --- /dev/null +++ b/test/libsolidity/syntaxTests/parsing/calling_function.sol @@ -0,0 +1,7 @@ +contract test { + function f() public { + function() returns(function() returns(function() returns(function() returns(uint)))) x; + uint y; + y = x()()()(); + } +} diff --git a/test/libsolidity/syntaxTests/parsing/conditional_multiple.sol b/test/libsolidity/syntaxTests/parsing/conditional_multiple.sol new file mode 100644 index 00000000..0e348f5b --- /dev/null +++ b/test/libsolidity/syntaxTests/parsing/conditional_multiple.sol @@ -0,0 +1,8 @@ +contract A { + function f() public { + uint x = 3 < 0 ? 2 > 1 ? 2 : 1 : 7 > 2 ? 7 : 6; + } +} +// ---- +// Warning: (47-53): Unused local variable. +// Warning: (17-100): Function state mutability can be restricted to pure diff --git a/test/libsolidity/syntaxTests/parsing/conditional_true_false_literal.sol b/test/libsolidity/syntaxTests/parsing/conditional_true_false_literal.sol new file mode 100644 index 00000000..40aaa917 --- /dev/null +++ b/test/libsolidity/syntaxTests/parsing/conditional_true_false_literal.sol @@ -0,0 +1,10 @@ +contract A { + function f() public { + uint x = true ? 1 : 0; + uint y = false ? 0 : 1; + } +} +// ---- +// Warning: (47-53): Unused local variable. +// Warning: (78-84): Unused local variable. +// Warning: (17-107): Function state mutability can be restricted to pure diff --git a/test/libsolidity/syntaxTests/parsing/conditional_with_assignment.sol b/test/libsolidity/syntaxTests/parsing/conditional_with_assignment.sol new file mode 100644 index 00000000..7489aaf9 --- /dev/null +++ b/test/libsolidity/syntaxTests/parsing/conditional_with_assignment.sol @@ -0,0 +1,8 @@ +contract A { + function f() public pure { + uint y = 1; + uint x = 3 < 0 ? y = 3 : 6; + true ? x = 3 : 4; + } +} +// ---- diff --git a/test/libsolidity/syntaxTests/parsing/conditional_with_constants.sol b/test/libsolidity/syntaxTests/parsing/conditional_with_constants.sol new file mode 100644 index 00000000..705fbadf --- /dev/null +++ b/test/libsolidity/syntaxTests/parsing/conditional_with_constants.sol @@ -0,0 +1,10 @@ +contract A { + function f() public { + uint x = 3 > 0 ? 3 : 0; + uint y = (3 > 0) ? 3 : 0; + } +} +// ---- +// Warning: (47-53): Unused local variable. +// Warning: (79-85): Unused local variable. +// Warning: (17-110): Function state mutability can be restricted to pure diff --git a/test/libsolidity/syntaxTests/parsing/conditional_with_variables.sol b/test/libsolidity/syntaxTests/parsing/conditional_with_variables.sol new file mode 100644 index 00000000..bbabf957 --- /dev/null +++ b/test/libsolidity/syntaxTests/parsing/conditional_with_variables.sol @@ -0,0 +1,12 @@ +contract A { + function f() public { + uint x = 3; + uint y = 1; + uint z = (x > y) ? x : y; + uint w = x > y ? x : y; + } +} +// ---- +// Warning: (87-93): Unused local variable. +// Warning: (121-127): Unused local variable. +// Warning: (17-150): Function state mutability can be restricted to pure diff --git a/test/libsolidity/syntaxTests/parsing/constant_state_modifier.sol b/test/libsolidity/syntaxTests/parsing/constant_state_modifier.sol index da068351..8fddc988 100644 --- a/test/libsolidity/syntaxTests/parsing/constant_state_modifier.sol +++ b/test/libsolidity/syntaxTests/parsing/constant_state_modifier.sol @@ -1,7 +1,8 @@ contract C { uint s; - // this test should fail starting from 0.5.0 function f() public constant returns (uint) { return s; } } +// ---- +// ParserError: (43-51): The state mutability modifier "constant" was removed in version 0.5.0. Use "view" or "pure" instead. diff --git a/test/libsolidity/syntaxTests/parsing/constructor_allowed_this.sol b/test/libsolidity/syntaxTests/parsing/constructor_allowed_this.sol index 9f714aea..b66253e4 100644 --- a/test/libsolidity/syntaxTests/parsing/constructor_allowed_this.sol +++ b/test/libsolidity/syntaxTests/parsing/constructor_allowed_this.sol @@ -3,9 +3,9 @@ contract A { } } contract B { - constructor(address) public { + constructor(C) public { } - function b(address) public returns (A) { + function b(C) public returns (A) { return new A(); } } diff --git a/test/libsolidity/syntaxTests/parsing/declaring_fixed_and_ufixed_variables.sol b/test/libsolidity/syntaxTests/parsing/declaring_fixed_and_ufixed_variables.sol new file mode 100644 index 00000000..6d88669a --- /dev/null +++ b/test/libsolidity/syntaxTests/parsing/declaring_fixed_and_ufixed_variables.sol @@ -0,0 +1,13 @@ +contract A { + fixed40x40 storeMe; + function f(ufixed x, fixed32x32 y) public { + ufixed8x8 a; + fixed b; + } +} +// ---- +// Warning: (52-60): Unused function parameter. Remove or comment out the variable name to silence this warning. +// Warning: (62-74): Unused function parameter. Remove or comment out the variable name to silence this warning. +// Warning: (93-104): Unused local variable. +// Warning: (114-121): Unused local variable. +// Warning: (41-128): Function state mutability can be restricted to pure diff --git a/test/libsolidity/syntaxTests/parsing/declaring_fixed_literal_variables.sol b/test/libsolidity/syntaxTests/parsing/declaring_fixed_literal_variables.sol new file mode 100644 index 00000000..b0d938a0 --- /dev/null +++ b/test/libsolidity/syntaxTests/parsing/declaring_fixed_literal_variables.sol @@ -0,0 +1,5 @@ +contract A { + fixed40x40 pi = 3.14; +} +// ---- +// TypeError: (33-37): Type rational_const 157 / 50 is not implicitly convertible to expected type fixed40x40. Try converting to type ufixed16x2 or use an explicit conversion. diff --git a/test/libsolidity/syntaxTests/parsing/elemantary_non_address_payable_state_variable.sol b/test/libsolidity/syntaxTests/parsing/elemantary_non_address_payable_state_variable.sol new file mode 100644 index 00000000..41b2762b --- /dev/null +++ b/test/libsolidity/syntaxTests/parsing/elemantary_non_address_payable_state_variable.sol @@ -0,0 +1,29 @@ +contract C { + bool payable a; + string payable b; + int payable c; + int256 payable d; + uint payable e; + uint256 payable f; + byte payable g; + bytes payable h; + bytes32 payable i; + fixed payable j; + fixed80x80 payable k; + ufixed payable l; + ufixed80x80 payable m; +} +// ---- +// ParserError: (22-29): State mutability can only be specified for address types. +// ParserError: (44-51): State mutability can only be specified for address types. +// ParserError: (63-70): State mutability can only be specified for address types. +// ParserError: (85-92): State mutability can only be specified for address types. +// ParserError: (105-112): State mutability can only be specified for address types. +// ParserError: (128-135): State mutability can only be specified for address types. +// ParserError: (148-155): State mutability can only be specified for address types. +// ParserError: (169-176): State mutability can only be specified for address types. +// ParserError: (192-199): State mutability can only be specified for address types. +// ParserError: (213-220): State mutability can only be specified for address types. +// ParserError: (239-246): State mutability can only be specified for address types. +// ParserError: (261-268): State mutability can only be specified for address types. +// ParserError: (288-295): State mutability can only be specified for address types. diff --git a/test/libsolidity/syntaxTests/parsing/elementary_non_address_payable_argument.sol b/test/libsolidity/syntaxTests/parsing/elementary_non_address_payable_argument.sol new file mode 100644 index 00000000..9cb0fb4f --- /dev/null +++ b/test/libsolidity/syntaxTests/parsing/elementary_non_address_payable_argument.sol @@ -0,0 +1,29 @@ +contract C { + function a(bool payable) public pure {} + function b(string payable) public pure {} + function c(int payable) public pure {} + function d(int256 payable) public pure {} + function e(uint payable) public pure {} + function f(uint256 payable) public pure {} + function g(byte payable) public pure {} + function h(bytes payable) public pure {} + function i(bytes32 payable) public pure {} + function j(fixed payable) public pure {} + function k(fixed80x80 payable) public pure {} + function l(ufixed payable) public pure {} + function m(ufixed80x80 payable) public pure {} +} +// ---- +// ParserError: (33-40): State mutability can only be specified for address types. +// ParserError: (79-86): State mutability can only be specified for address types. +// ParserError: (122-129): State mutability can only be specified for address types. +// ParserError: (168-175): State mutability can only be specified for address types. +// ParserError: (212-219): State mutability can only be specified for address types. +// ParserError: (259-266): State mutability can only be specified for address types. +// ParserError: (303-310): State mutability can only be specified for address types. +// ParserError: (348-355): State mutability can only be specified for address types. +// ParserError: (395-402): State mutability can only be specified for address types. +// ParserError: (440-447): State mutability can only be specified for address types. +// ParserError: (490-497): State mutability can only be specified for address types. +// ParserError: (536-543): State mutability can only be specified for address types. +// ParserError: (587-594): State mutability can only be specified for address types. diff --git a/test/libsolidity/syntaxTests/parsing/elementary_non_address_payable_local.sol b/test/libsolidity/syntaxTests/parsing/elementary_non_address_payable_local.sol new file mode 100644 index 00000000..3d47f1c7 --- /dev/null +++ b/test/libsolidity/syntaxTests/parsing/elementary_non_address_payable_local.sol @@ -0,0 +1,31 @@ +contract C { + function f() public pure { + bool payable a; + string payable b; + int payable c; + int256 payable d; + uint payable e; + uint256 payable f; + byte payable g; + bytes payable h; + bytes32 payable i; + fixed payable j; + fixed80x80 payable k; + ufixed payable l; + ufixed80x80 payable m; + } +} +// ---- +// ParserError: (57-64): State mutability can only be specified for address types. +// ParserError: (83-90): State mutability can only be specified for address types. +// ParserError: (106-113): State mutability can only be specified for address types. +// ParserError: (132-139): State mutability can only be specified for address types. +// ParserError: (156-163): State mutability can only be specified for address types. +// ParserError: (183-190): State mutability can only be specified for address types. +// ParserError: (207-214): State mutability can only be specified for address types. +// ParserError: (232-239): State mutability can only be specified for address types. +// ParserError: (259-266): State mutability can only be specified for address types. +// ParserError: (284-291): State mutability can only be specified for address types. +// ParserError: (314-321): State mutability can only be specified for address types. +// ParserError: (340-347): State mutability can only be specified for address types. +// ParserError: (371-378): State mutability can only be specified for address types. diff --git a/test/libsolidity/syntaxTests/parsing/elementary_non_address_payable_return.sol b/test/libsolidity/syntaxTests/parsing/elementary_non_address_payable_return.sol new file mode 100644 index 00000000..fc7a3c25 --- /dev/null +++ b/test/libsolidity/syntaxTests/parsing/elementary_non_address_payable_return.sol @@ -0,0 +1,29 @@ +contract C { + function a() public pure returns (bool payable) {} + function b() public pure returns (string payable) {} + function c() public pure returns (int payable) {} + function d() public pure returns (int256 payable) {} + function e() public pure returns (uint payable) {} + function f() public pure returns (uint256 payable) {} + function g() public pure returns (byte payable) {} + function h() public pure returns (bytes payable) {} + function i() public pure returns (bytes32 payable) {} + function j() public pure returns (fixed payable) {} + function k() public pure returns (fixed80x80 payable) {} + function l() public pure returns (ufixed payable) {} + function m() public pure returns (ufixed80x80 payable) {} +} +// ---- +// ParserError: (56-63): State mutability can only be specified for address types. +// ParserError: (113-120): State mutability can only be specified for address types. +// ParserError: (167-174): State mutability can only be specified for address types. +// ParserError: (224-231): State mutability can only be specified for address types. +// ParserError: (279-286): State mutability can only be specified for address types. +// ParserError: (337-344): State mutability can only be specified for address types. +// ParserError: (392-399): State mutability can only be specified for address types. +// ParserError: (448-455): State mutability can only be specified for address types. +// ParserError: (506-513): State mutability can only be specified for address types. +// ParserError: (562-569): State mutability can only be specified for address types. +// ParserError: (623-630): State mutability can only be specified for address types. +// ParserError: (680-687): State mutability can only be specified for address types. +// ParserError: (742-749): State mutability can only be specified for address types. diff --git a/test/libsolidity/syntaxTests/parsing/else_if_statement.sol b/test/libsolidity/syntaxTests/parsing/else_if_statement.sol new file mode 100644 index 00000000..c2a1aaeb --- /dev/null +++ b/test/libsolidity/syntaxTests/parsing/else_if_statement.sol @@ -0,0 +1,7 @@ +contract test { + function fun(uint256 a) public returns (uint8 b) { + if (a < 0) b = 0x67; else if (a == 0) b = 0x12; else b = 0x78; + } +} +// ---- +// Warning: (20-147): Function state mutability can be restricted to pure diff --git a/test/libsolidity/syntaxTests/parsing/empty_function.sol b/test/libsolidity/syntaxTests/parsing/empty_function.sol index 218fd9a7..320a0bcc 100644 --- a/test/libsolidity/syntaxTests/parsing/empty_function.sol +++ b/test/libsolidity/syntaxTests/parsing/empty_function.sol @@ -1,10 +1,9 @@ contract test { uint256 stateVar; - function functionName(bytes20 arg1, address addr) view returns (int id) { } + function functionName(bytes20 arg1, address addr) public view returns (int id) { } } // ---- -// Warning: (36-111): No visibility specified. Defaulting to "public". // Warning: (58-70): Unused function parameter. Remove or comment out the variable name to silence this warning. // Warning: (72-84): Unused function parameter. Remove or comment out the variable name to silence this warning. -// Warning: (100-106): Unused function parameter. Remove or comment out the variable name to silence this warning. -// Warning: (36-111): Function state mutability can be restricted to pure +// Warning: (107-113): Unused function parameter. Remove or comment out the variable name to silence this warning. +// Warning: (36-118): Function state mutability can be restricted to pure diff --git a/test/libsolidity/syntaxTests/parsing/enum_from_interface.sol b/test/libsolidity/syntaxTests/parsing/enum_from_interface.sol new file mode 100644 index 00000000..0fe0fbae --- /dev/null +++ b/test/libsolidity/syntaxTests/parsing/enum_from_interface.sol @@ -0,0 +1,9 @@ +interface I { + enum Direction { Left, Right } +} + +contract D { + function f() public pure returns (I.Direction) { + return I.Direction.Left; + } +} diff --git a/test/libsolidity/syntaxTests/parsing/enum_from_interface_in_library.sol b/test/libsolidity/syntaxTests/parsing/enum_from_interface_in_library.sol new file mode 100644 index 00000000..8d9003eb --- /dev/null +++ b/test/libsolidity/syntaxTests/parsing/enum_from_interface_in_library.sol @@ -0,0 +1,12 @@ +interface I { + enum Direction { Left, Right } +} + +library L { + function f() public pure returns (I.Direction) { + return I.Direction.Left; + } + function g() internal pure returns (I.Direction) { + return I.Direction.Left; + } +} diff --git a/test/libsolidity/syntaxTests/parsing/enum_from_library.sol b/test/libsolidity/syntaxTests/parsing/enum_from_library.sol new file mode 100644 index 00000000..ab762a82 --- /dev/null +++ b/test/libsolidity/syntaxTests/parsing/enum_from_library.sol @@ -0,0 +1,9 @@ +library L { + enum Direction { Left, Right } +} + +contract D { + function f() public pure returns (L.Direction) { + return L.Direction.Left; + } +} diff --git a/test/libsolidity/syntaxTests/parsing/enum_inheritance_contract.sol b/test/libsolidity/syntaxTests/parsing/enum_inheritance_contract.sol new file mode 100644 index 00000000..e5b98352 --- /dev/null +++ b/test/libsolidity/syntaxTests/parsing/enum_inheritance_contract.sol @@ -0,0 +1,9 @@ +contract C { + enum Direction { Left, Right } +} + +contract D is C { + function f() public pure returns (Direction) { + return Direction.Left; + } +} diff --git a/test/libsolidity/syntaxTests/parsing/enum_inheritance_interface.sol b/test/libsolidity/syntaxTests/parsing/enum_inheritance_interface.sol new file mode 100644 index 00000000..75858744 --- /dev/null +++ b/test/libsolidity/syntaxTests/parsing/enum_inheritance_interface.sol @@ -0,0 +1,9 @@ +interface I { + enum Direction { Left, Right } +} + +contract D is I { + function f() public pure returns (Direction) { + return Direction.Left; + } +} diff --git a/test/libsolidity/syntaxTests/parsing/enum_valid_declaration.sol b/test/libsolidity/syntaxTests/parsing/enum_valid_declaration.sol new file mode 100644 index 00000000..606f59d7 --- /dev/null +++ b/test/libsolidity/syntaxTests/parsing/enum_valid_declaration.sol @@ -0,0 +1,7 @@ +contract c { + enum validEnum { Value1, Value2, Value3, Value4 } + constructor() public { + a = validEnum.Value3; + } + validEnum a; +} diff --git a/test/libsolidity/syntaxTests/parsing/event.sol b/test/libsolidity/syntaxTests/parsing/event.sol new file mode 100644 index 00000000..2aaa873f --- /dev/null +++ b/test/libsolidity/syntaxTests/parsing/event.sol @@ -0,0 +1,3 @@ +contract c { + event e(); +} diff --git a/test/libsolidity/syntaxTests/parsing/event_arguments.sol b/test/libsolidity/syntaxTests/parsing/event_arguments.sol new file mode 100644 index 00000000..3228853a --- /dev/null +++ b/test/libsolidity/syntaxTests/parsing/event_arguments.sol @@ -0,0 +1,3 @@ +contract c { + event e(uint a, bytes32 s); +} diff --git a/test/libsolidity/syntaxTests/parsing/event_arguments_indexed.sol b/test/libsolidity/syntaxTests/parsing/event_arguments_indexed.sol new file mode 100644 index 00000000..d603fc08 --- /dev/null +++ b/test/libsolidity/syntaxTests/parsing/event_arguments_indexed.sol @@ -0,0 +1,3 @@ +contract c { + event e(uint a, bytes32 indexed s, bool indexed b); +} diff --git a/test/libsolidity/syntaxTests/parsing/exp_expression.sol b/test/libsolidity/syntaxTests/parsing/exp_expression.sol new file mode 100644 index 00000000..6b307ea0 --- /dev/null +++ b/test/libsolidity/syntaxTests/parsing/exp_expression.sol @@ -0,0 +1,8 @@ +contract test { + function fun(uint256 a) public { + uint256 x = 3 ** a; + } +} +// ---- +// Warning: (61-70): Unused local variable. +// Warning: (20-86): Function state mutability can be restricted to pure diff --git a/test/libsolidity/syntaxTests/parsing/external_function.sol b/test/libsolidity/syntaxTests/parsing/external_function.sol new file mode 100644 index 00000000..3aa3ceec --- /dev/null +++ b/test/libsolidity/syntaxTests/parsing/external_function.sol @@ -0,0 +1,5 @@ +contract c { + function x() external {} +} +// ---- +// Warning: (17-41): Function state mutability can be restricted to pure diff --git a/test/libsolidity/syntaxTests/parsing/fallback_function.sol b/test/libsolidity/syntaxTests/parsing/fallback_function.sol new file mode 100644 index 00000000..054f57de --- /dev/null +++ b/test/libsolidity/syntaxTests/parsing/fallback_function.sol @@ -0,0 +1,4 @@ +contract c { + function() external { } +} +// ---- diff --git a/test/libsolidity/syntaxTests/parsing/for_loop_simple_initexpr.sol b/test/libsolidity/syntaxTests/parsing/for_loop_simple_initexpr.sol new file mode 100644 index 00000000..fce669dd --- /dev/null +++ b/test/libsolidity/syntaxTests/parsing/for_loop_simple_initexpr.sol @@ -0,0 +1,12 @@ +contract test { + function fun(uint256 a) public { + uint256 i =0; + for (i = 0; i < 10; i++) { + uint256 x = i; break; continue; + } + } +} +// ---- +// Warning: (33-42): Unused function parameter. Remove or comment out the variable name to silence this warning. +// Warning: (122-131): Unused local variable. +// Warning: (20-169): Function state mutability can be restricted to pure diff --git a/test/libsolidity/syntaxTests/parsing/for_loop_simple_noexpr.sol b/test/libsolidity/syntaxTests/parsing/for_loop_simple_noexpr.sol new file mode 100644 index 00000000..4adf0948 --- /dev/null +++ b/test/libsolidity/syntaxTests/parsing/for_loop_simple_noexpr.sol @@ -0,0 +1,12 @@ +contract test { + function fun(uint256 a) public { + uint256 i =0; + for (;;) { + uint256 x = i; break; continue; + } + } + } +// ---- +// Warning: (37-46): Unused function parameter. Remove or comment out the variable name to silence this warning. +// Warning: (122-131): Unused local variable. +// Warning: (24-177): Function state mutability can be restricted to pure diff --git a/test/libsolidity/syntaxTests/parsing/for_loop_single_stmt_body.sol b/test/libsolidity/syntaxTests/parsing/for_loop_single_stmt_body.sol new file mode 100644 index 00000000..c6af519c --- /dev/null +++ b/test/libsolidity/syntaxTests/parsing/for_loop_single_stmt_body.sol @@ -0,0 +1,10 @@ +contract test { + function fun(uint256 a) public { + uint256 i = 0; + for (i = 0; i < 10; i++) + continue; + } +} +// ---- +// Warning: (33-42): Unused function parameter. Remove or comment out the variable name to silence this warning. +// Warning: (20-136): Function state mutability can be restricted to pure diff --git a/test/libsolidity/syntaxTests/parsing/for_loop_vardef_initexpr.sol b/test/libsolidity/syntaxTests/parsing/for_loop_vardef_initexpr.sol new file mode 100644 index 00000000..c22ae42f --- /dev/null +++ b/test/libsolidity/syntaxTests/parsing/for_loop_vardef_initexpr.sol @@ -0,0 +1,11 @@ +contract test { + function fun(uint256 a) public { + for (uint256 i = 0; i < 10; i++) { + uint256 x = i; break; continue; + } + } +} +// ---- +// Warning: (33-42): Unused function parameter. Remove or comment out the variable name to silence this warning. +// Warning: (108-117): Unused local variable. +// Warning: (20-155): Function state mutability can be restricted to pure diff --git a/test/libsolidity/syntaxTests/parsing/from_is_not_keyword.sol b/test/libsolidity/syntaxTests/parsing/from_is_not_keyword.sol new file mode 100644 index 00000000..38175572 --- /dev/null +++ b/test/libsolidity/syntaxTests/parsing/from_is_not_keyword.sol @@ -0,0 +1,3 @@ +// "from" is not a keyword although it is used as a keyword in import directives. +contract from { +} diff --git a/test/libsolidity/syntaxTests/parsing/function_no_body.sol b/test/libsolidity/syntaxTests/parsing/function_no_body.sol index 0424ebd8..c4a686dc 100644 --- a/test/libsolidity/syntaxTests/parsing/function_no_body.sol +++ b/test/libsolidity/syntaxTests/parsing/function_no_body.sol @@ -1,5 +1,3 @@ contract test { - function functionName(bytes32 input) returns (bytes32 out); + function functionName(bytes32 input) public returns (bytes32 out); } -// ---- -// Warning: (17-76): No visibility specified. Defaulting to "public". diff --git a/test/libsolidity/syntaxTests/parsing/function_normal_comments.sol b/test/libsolidity/syntaxTests/parsing/function_normal_comments.sol new file mode 100644 index 00000000..94e1e60a --- /dev/null +++ b/test/libsolidity/syntaxTests/parsing/function_normal_comments.sol @@ -0,0 +1,9 @@ +contract test { + uint256 stateVar; + // We won't see this comment + function functionName(bytes32 input) public returns (bytes32 out) {} +} +// ---- +// Warning: (97-110): Unused function parameter. Remove or comment out the variable name to silence this warning. +// Warning: (128-139): Unused function parameter. Remove or comment out the variable name to silence this warning. +// Warning: (75-143): Function state mutability can be restricted to pure diff --git a/test/libsolidity/syntaxTests/parsing/function_type_as_parameter.sol b/test/libsolidity/syntaxTests/parsing/function_type_as_parameter.sol new file mode 100644 index 00000000..4075d74a --- /dev/null +++ b/test/libsolidity/syntaxTests/parsing/function_type_as_parameter.sol @@ -0,0 +1,5 @@ +contract test { + function f(function(uint) external returns (uint) g) internal returns (uint a) { + return g(1); + } +} diff --git a/test/libsolidity/syntaxTests/parsing/function_type_as_storage_variable.sol b/test/libsolidity/syntaxTests/parsing/function_type_as_storage_variable.sol new file mode 100644 index 00000000..e3d41f48 --- /dev/null +++ b/test/libsolidity/syntaxTests/parsing/function_type_as_storage_variable.sol @@ -0,0 +1,3 @@ +contract test { + function (uint, uint) internal returns (uint) f1; +} diff --git a/test/libsolidity/syntaxTests/parsing/function_type_as_storage_variable_with_assignment.sol b/test/libsolidity/syntaxTests/parsing/function_type_as_storage_variable_with_assignment.sol new file mode 100644 index 00000000..11e77f25 --- /dev/null +++ b/test/libsolidity/syntaxTests/parsing/function_type_as_storage_variable_with_assignment.sol @@ -0,0 +1,9 @@ +contract test { + function f(uint x, uint y) public returns (uint a) {} + function (uint, uint) internal returns (uint) f1 = f; +} +// ---- +// Warning: (31-37): Unused function parameter. Remove or comment out the variable name to silence this warning. +// Warning: (39-45): Unused function parameter. Remove or comment out the variable name to silence this warning. +// Warning: (63-69): Unused function parameter. Remove or comment out the variable name to silence this warning. +// Warning: (20-73): Function state mutability can be restricted to pure diff --git a/test/libsolidity/syntaxTests/parsing/function_type_in_expression.sol b/test/libsolidity/syntaxTests/parsing/function_type_in_expression.sol new file mode 100644 index 00000000..3defb5ea --- /dev/null +++ b/test/libsolidity/syntaxTests/parsing/function_type_in_expression.sol @@ -0,0 +1,13 @@ +contract test { + function f(uint x, uint y) public returns (uint a) {} + function g() public { + function (uint, uint) internal returns (uint) f1 = f; + } +} +// ---- +// Warning: (31-37): Unused function parameter. Remove or comment out the variable name to silence this warning. +// Warning: (39-45): Unused function parameter. Remove or comment out the variable name to silence this warning. +// Warning: (63-69): Unused function parameter. Remove or comment out the variable name to silence this warning. +// Warning: (108-156): Unused local variable. +// Warning: (20-73): Function state mutability can be restricted to pure +// Warning: (78-167): Function state mutability can be restricted to pure diff --git a/test/libsolidity/syntaxTests/parsing/function_type_in_struct.sol b/test/libsolidity/syntaxTests/parsing/function_type_in_struct.sol new file mode 100644 index 00000000..c7703b47 --- /dev/null +++ b/test/libsolidity/syntaxTests/parsing/function_type_in_struct.sol @@ -0,0 +1,10 @@ +contract test { + struct S { + function (uint x, uint y) internal returns (uint) f; + function (uint, uint) external returns (uint) g; + uint d; + } +} +// ---- +// Warning: (49-55): Naming function type parameters is deprecated. +// Warning: (57-63): Naming function type parameters is deprecated. diff --git a/test/libsolidity/syntaxTests/parsing/if_statement.sol b/test/libsolidity/syntaxTests/parsing/if_statement.sol new file mode 100644 index 00000000..b3269785 --- /dev/null +++ b/test/libsolidity/syntaxTests/parsing/if_statement.sol @@ -0,0 +1,8 @@ +contract test { + function fun(uint256 a) public returns (uint) { + if (a >= 8) { return 2; } else { uint b = 7; } + } +} +// ---- +// Warning: (109-115): Unused local variable. +// Warning: (20-128): Function state mutability can be restricted to pure diff --git a/test/libsolidity/syntaxTests/parsing/import_complex.sol b/test/libsolidity/syntaxTests/parsing/import_complex.sol new file mode 100644 index 00000000..8bbb0a88 --- /dev/null +++ b/test/libsolidity/syntaxTests/parsing/import_complex.sol @@ -0,0 +1,3 @@ +import {hello, world} from "hello"; +// ---- +// ParserError: (0-35): Source "hello" not found: File not supplied initially. diff --git a/test/libsolidity/syntaxTests/parsing/import_complex_invalid_from.sol b/test/libsolidity/syntaxTests/parsing/import_complex_invalid_from.sol new file mode 100644 index 00000000..c4667606 --- /dev/null +++ b/test/libsolidity/syntaxTests/parsing/import_complex_invalid_from.sol @@ -0,0 +1,3 @@ +import {hello, world} from function; +// ---- +// ParserError: (27-35): Expected import path. diff --git a/test/libsolidity/syntaxTests/parsing/import_complex_without_from.sol b/test/libsolidity/syntaxTests/parsing/import_complex_without_from.sol new file mode 100644 index 00000000..961acb22 --- /dev/null +++ b/test/libsolidity/syntaxTests/parsing/import_complex_without_from.sol @@ -0,0 +1,3 @@ +import {hello, world}; +// ---- +// ParserError: (21-22): Expected "from". diff --git a/test/libsolidity/syntaxTests/parsing/import_invalid_token.sol b/test/libsolidity/syntaxTests/parsing/import_invalid_token.sol new file mode 100644 index 00000000..df837e28 --- /dev/null +++ b/test/libsolidity/syntaxTests/parsing/import_invalid_token.sol @@ -0,0 +1,3 @@ +import function; +// ---- +// ParserError: (7-15): Expected string literal (path), "*" or alias list. diff --git a/test/libsolidity/syntaxTests/parsing/import_simple.sol b/test/libsolidity/syntaxTests/parsing/import_simple.sol new file mode 100644 index 00000000..5d61a8bb --- /dev/null +++ b/test/libsolidity/syntaxTests/parsing/import_simple.sol @@ -0,0 +1,3 @@ +import "hello"; +// ---- +// ParserError: (0-15): Source "hello" not found: File not supplied initially. diff --git a/test/libsolidity/syntaxTests/parsing/inline_array_declaration.sol b/test/libsolidity/syntaxTests/parsing/inline_array_declaration.sol new file mode 100644 index 00000000..4730f950 --- /dev/null +++ b/test/libsolidity/syntaxTests/parsing/inline_array_declaration.sol @@ -0,0 +1,8 @@ +contract c { + uint[] a; + function f() public returns (uint, uint) { + a = [1,2,3]; + return (a[3], [2,3,4][0]); + } +} +// ---- diff --git a/test/libsolidity/syntaxTests/parsing/interface_basic.sol b/test/libsolidity/syntaxTests/parsing/interface_basic.sol index c25b48ba..0742c24f 100644 --- a/test/libsolidity/syntaxTests/parsing/interface_basic.sol +++ b/test/libsolidity/syntaxTests/parsing/interface_basic.sol @@ -1,6 +1,4 @@ interface Interface { - function f(); + function f() external; } // ---- -// Warning: (23-36): Functions in interfaces should be declared external. -// Warning: (23-36): No visibility specified. Defaulting to "public". In interfaces it defaults to external. diff --git a/test/libsolidity/syntaxTests/parsing/lexer_numbers_with_underscores_decimal.sol b/test/libsolidity/syntaxTests/parsing/lexer_numbers_with_underscores_decimal.sol new file mode 100644 index 00000000..d4d3299f --- /dev/null +++ b/test/libsolidity/syntaxTests/parsing/lexer_numbers_with_underscores_decimal.sol @@ -0,0 +1,13 @@ +contract C { + function f() public pure { + uint d1 = 654_321; + uint d2 = 54_321; + uint d3 = 4_321; + uint d4 = 5_43_21; + uint d5 = 1_2e10; + uint d6 = 12e1_0; + + d1; d2; d3; d4; d5; d6; + } +} +// ---- diff --git a/test/libsolidity/syntaxTests/parsing/lexer_numbers_with_underscores_decimal_fail.sol b/test/libsolidity/syntaxTests/parsing/lexer_numbers_with_underscores_decimal_fail.sol new file mode 100644 index 00000000..8978996d --- /dev/null +++ b/test/libsolidity/syntaxTests/parsing/lexer_numbers_with_underscores_decimal_fail.sol @@ -0,0 +1,13 @@ +contract C { + function f() public pure { + uint D1 = 1234_; + uint D2 = 12__34; + uint D3 = 12_e34; + uint D4 = 12e_34; + } +} +// ---- +// SyntaxError: (56-61): Invalid use of underscores in number literal. No trailing underscores allowed. +// SyntaxError: (77-83): Invalid use of underscores in number literal. Only one consecutive underscores between digits allowed. +// SyntaxError: (99-105): Invalid use of underscores in number literal. No underscore at the end of the mantissa allowed. +// SyntaxError: (121-127): Invalid use of underscores in number literal. No underscore in front of exponent allowed. diff --git a/test/libsolidity/syntaxTests/parsing/lexer_numbers_with_underscores_fixed.sol b/test/libsolidity/syntaxTests/parsing/lexer_numbers_with_underscores_fixed.sol new file mode 100644 index 00000000..4910ee82 --- /dev/null +++ b/test/libsolidity/syntaxTests/parsing/lexer_numbers_with_underscores_fixed.sol @@ -0,0 +1,9 @@ +contract C { + function f() public pure { + fixed f1 = 3.14_15; + fixed f2 = 3_1.4_15; + + f1; f2; + } +} +// ---- diff --git a/test/libsolidity/syntaxTests/parsing/lexer_numbers_with_underscores_fixed_fail.sol b/test/libsolidity/syntaxTests/parsing/lexer_numbers_with_underscores_fixed_fail.sol new file mode 100644 index 00000000..3b91895d --- /dev/null +++ b/test/libsolidity/syntaxTests/parsing/lexer_numbers_with_underscores_fixed_fail.sol @@ -0,0 +1,17 @@ +contract C { + function f() public pure { + fixed F1 = 3.1415_; + fixed F2 = 3__1.4__15; + fixed F3 = 1_.2; + fixed F4 = 1._2; + fixed F5 = 1.2e_12; + fixed F6 = 1._; + } +} +// ---- +// SyntaxError: (57-64): Invalid use of underscores in number literal. No trailing underscores allowed. +// SyntaxError: (81-91): Invalid use of underscores in number literal. Only one consecutive underscores between digits allowed. +// SyntaxError: (108-112): Invalid use of underscores in number literal. No underscores in front of the fraction part allowed. +// SyntaxError: (129-133): Invalid use of underscores in number literal. No underscores in front of the fraction part allowed. +// SyntaxError: (150-157): Invalid use of underscores in number literal. No underscore in front of exponent allowed. +// SyntaxError: (174-177): Invalid use of underscores in number literal. No trailing underscores allowed. diff --git a/test/libsolidity/syntaxTests/parsing/lexer_numbers_with_underscores_hex.sol b/test/libsolidity/syntaxTests/parsing/lexer_numbers_with_underscores_hex.sol new file mode 100644 index 00000000..999a4634 --- /dev/null +++ b/test/libsolidity/syntaxTests/parsing/lexer_numbers_with_underscores_hex.sol @@ -0,0 +1,13 @@ +contract C { + function f() public pure { + uint x1 = 0x8765_4321; + uint x2 = 0x765_4321; + uint x3 = 0x65_4321; + uint x4 = 0x5_4321; + uint x5 = 0x123_1234_1234_1234; + uint x6 = 0x123456_1234_1234; + + x1; x2; x3; x4; x5; x6; + } +} +// ---- diff --git a/test/libsolidity/syntaxTests/parsing/lexer_numbers_with_underscores_hex_fail.sol b/test/libsolidity/syntaxTests/parsing/lexer_numbers_with_underscores_hex_fail.sol new file mode 100644 index 00000000..a8a488c1 --- /dev/null +++ b/test/libsolidity/syntaxTests/parsing/lexer_numbers_with_underscores_hex_fail.sol @@ -0,0 +1,7 @@ +contract C { + function f() public pure { + uint X1 = 0x1234__1234__1234__123; + } +} +// ---- +// SyntaxError: (56-79): Invalid use of underscores in number literal. Only one consecutive underscores between digits allowed. diff --git a/test/libsolidity/syntaxTests/parsing/library_simple.sol b/test/libsolidity/syntaxTests/parsing/library_simple.sol new file mode 100644 index 00000000..006ff307 --- /dev/null +++ b/test/libsolidity/syntaxTests/parsing/library_simple.sol @@ -0,0 +1,5 @@ +library Lib { + function f() public { } +} +// ---- +// Warning: (18-41): Function state mutability can be restricted to pure diff --git a/test/libsolidity/syntaxTests/parsing/literal_constants_with_ether_subdenominations.sol b/test/libsolidity/syntaxTests/parsing/literal_constants_with_ether_subdenominations.sol new file mode 100644 index 00000000..64116b88 --- /dev/null +++ b/test/libsolidity/syntaxTests/parsing/literal_constants_with_ether_subdenominations.sol @@ -0,0 +1,15 @@ +contract c { + function f() public + { + a = 1 wei; + b = 2 szabo; + c = 3 finney; + b = 4 ether; + } + uint256 a; + uint256 b; + uint256 c; + uint256 d; +} +// ---- +// Warning: (170-179): This declaration shadows an existing declaration. diff --git a/test/libsolidity/syntaxTests/parsing/literal_constants_with_ether_subdenominations_in_expressions.sol b/test/libsolidity/syntaxTests/parsing/literal_constants_with_ether_subdenominations_in_expressions.sol new file mode 100644 index 00000000..2f2302ed --- /dev/null +++ b/test/libsolidity/syntaxTests/parsing/literal_constants_with_ether_subdenominations_in_expressions.sol @@ -0,0 +1,7 @@ +contract c { + constructor() public + { + a = 1 wei * 100 wei + 7 szabo - 3; + } + uint256 a; +} diff --git a/test/libsolidity/syntaxTests/parsing/location_specifiers_for_locals.sol b/test/libsolidity/syntaxTests/parsing/location_specifiers_for_locals.sol new file mode 100644 index 00000000..38de7b1c --- /dev/null +++ b/test/libsolidity/syntaxTests/parsing/location_specifiers_for_locals.sol @@ -0,0 +1,9 @@ +contract Foo { + uint[] m_x; + function f() public view { + uint[] storage x = m_x; + uint[] memory y; + x; y; + } +} +// ---- diff --git a/test/libsolidity/syntaxTests/parsing/location_specifiers_for_locals_multi.sol b/test/libsolidity/syntaxTests/parsing/location_specifiers_for_locals_multi.sol new file mode 100644 index 00000000..d53208ef --- /dev/null +++ b/test/libsolidity/syntaxTests/parsing/location_specifiers_for_locals_multi.sol @@ -0,0 +1,12 @@ +contract Foo { + uint[] m_x; + function f() public view { + uint[] storage memory x = m_x; + uint[] memory storage calldata y; + x; y; + } +} +// ---- +// ParserError: (85-91): Location already specified. +// ParserError: (123-130): Location already specified. +// ParserError: (131-139): Location already specified. diff --git a/test/libsolidity/syntaxTests/parsing/location_specifiers_for_params.sol b/test/libsolidity/syntaxTests/parsing/location_specifiers_for_params.sol new file mode 100644 index 00000000..bf78e59c --- /dev/null +++ b/test/libsolidity/syntaxTests/parsing/location_specifiers_for_params.sol @@ -0,0 +1,7 @@ +contract Foo { + function f(uint[] storage constant x, uint[] memory y) internal { } +} +// ---- +// DeclarationError: (30-55): The "constant" keyword can only be used for state variables. +// TypeError: (30-55): Constants of non-value type not yet implemented. +// TypeError: (30-55): Uninitialized "constant" variable. diff --git a/test/libsolidity/syntaxTests/parsing/location_specifiers_for_params_multi.sol b/test/libsolidity/syntaxTests/parsing/location_specifiers_for_params_multi.sol new file mode 100644 index 00000000..c6155c09 --- /dev/null +++ b/test/libsolidity/syntaxTests/parsing/location_specifiers_for_params_multi.sol @@ -0,0 +1,6 @@ +contract Foo { + function f(uint[] storage memory constant x, uint[] memory calldata y) internal { } +} +// ---- +// ParserError: (45-51): Location already specified. +// ParserError: (78-86): Location already specified. diff --git a/test/libsolidity/syntaxTests/parsing/location_specifiers_for_state_variables_multi.sol b/test/libsolidity/syntaxTests/parsing/location_specifiers_for_state_variables_multi.sol new file mode 100644 index 00000000..e34df2b2 --- /dev/null +++ b/test/libsolidity/syntaxTests/parsing/location_specifiers_for_state_variables_multi.sol @@ -0,0 +1,5 @@ +contract Foo { + uint[] memory storage calldata x; +} +// ---- +// ParserError: (23-29): Expected identifier but got 'memory' diff --git a/test/libsolidity/syntaxTests/parsing/mapping.sol b/test/libsolidity/syntaxTests/parsing/mapping.sol new file mode 100644 index 00000000..d6bf3f14 --- /dev/null +++ b/test/libsolidity/syntaxTests/parsing/mapping.sol @@ -0,0 +1,3 @@ +contract test { + mapping(address => bytes32) names; +} diff --git a/test/libsolidity/syntaxTests/parsing/mapping_and_array_of_functions.sol b/test/libsolidity/syntaxTests/parsing/mapping_and_array_of_functions.sol new file mode 100644 index 00000000..b7cf34e4 --- /dev/null +++ b/test/libsolidity/syntaxTests/parsing/mapping_and_array_of_functions.sol @@ -0,0 +1,6 @@ +contract test { + mapping (address => function() internal returns (uint)) a; + mapping (address => function() external) b; + mapping (address => function() external[]) c; + function() external[] d; +} diff --git a/test/libsolidity/syntaxTests/parsing/mapping_from_address_payable.sol b/test/libsolidity/syntaxTests/parsing/mapping_from_address_payable.sol new file mode 100644 index 00000000..7e6f98d7 --- /dev/null +++ b/test/libsolidity/syntaxTests/parsing/mapping_from_address_payable.sol @@ -0,0 +1,5 @@ +contract C { + mapping(address payable => uint) m; +} +// ---- +// ParserError: (33-40): Expected '=>' but got 'payable' diff --git a/test/libsolidity/syntaxTests/parsing/mapping_in_struct.sol b/test/libsolidity/syntaxTests/parsing/mapping_in_struct.sol new file mode 100644 index 00000000..980d5750 --- /dev/null +++ b/test/libsolidity/syntaxTests/parsing/mapping_in_struct.sol @@ -0,0 +1,7 @@ +contract test { + struct test_struct { + address addr; + uint256 count; + mapping(bytes32 => test_struct) self_reference; + } +} diff --git a/test/libsolidity/syntaxTests/parsing/mapping_nonelementary_key_1.sol b/test/libsolidity/syntaxTests/parsing/mapping_nonelementary_key_1.sol new file mode 100644 index 00000000..ea2d282c --- /dev/null +++ b/test/libsolidity/syntaxTests/parsing/mapping_nonelementary_key_1.sol @@ -0,0 +1,5 @@ +contract c { + mapping(uint[] => uint) data; +} +// ---- +// ParserError: (26-27): Expected '=>' but got '[' diff --git a/test/libsolidity/syntaxTests/parsing/mapping_nonelementary_key_2.sol b/test/libsolidity/syntaxTests/parsing/mapping_nonelementary_key_2.sol new file mode 100644 index 00000000..713cddeb --- /dev/null +++ b/test/libsolidity/syntaxTests/parsing/mapping_nonelementary_key_2.sol @@ -0,0 +1,8 @@ +contract c { + struct S { + uint x; + } + mapping(S => uint) data; +} +// ---- +// ParserError: (47-48): Expected elementary type name for mapping key type diff --git a/test/libsolidity/syntaxTests/parsing/mapping_nonelementary_key_3.sol b/test/libsolidity/syntaxTests/parsing/mapping_nonelementary_key_3.sol new file mode 100644 index 00000000..655af9de --- /dev/null +++ b/test/libsolidity/syntaxTests/parsing/mapping_nonelementary_key_3.sol @@ -0,0 +1,8 @@ +contract c { + struct S { + string s; + } + mapping(S => uint) data; +} +// ---- +// ParserError: (49-50): Expected elementary type name for mapping key type diff --git a/test/libsolidity/syntaxTests/parsing/mapping_nonelementary_key_4.sol b/test/libsolidity/syntaxTests/parsing/mapping_nonelementary_key_4.sol new file mode 100644 index 00000000..f4dcb00a --- /dev/null +++ b/test/libsolidity/syntaxTests/parsing/mapping_nonelementary_key_4.sol @@ -0,0 +1,5 @@ +contract c { + mapping(string[] => uint) data; +} +// ---- +// ParserError: (28-29): Expected '=>' but got '[' diff --git a/test/libsolidity/syntaxTests/parsing/mapping_to_mapping_in_struct.sol b/test/libsolidity/syntaxTests/parsing/mapping_to_mapping_in_struct.sol new file mode 100644 index 00000000..c06220b7 --- /dev/null +++ b/test/libsolidity/syntaxTests/parsing/mapping_to_mapping_in_struct.sol @@ -0,0 +1,6 @@ +contract test { + struct test_struct { + address addr; + mapping (uint64 => mapping (bytes32 => uint)) complex_mapping; + } +} diff --git a/test/libsolidity/syntaxTests/parsing/modifier.sol b/test/libsolidity/syntaxTests/parsing/modifier.sol new file mode 100644 index 00000000..b995ce89 --- /dev/null +++ b/test/libsolidity/syntaxTests/parsing/modifier.sol @@ -0,0 +1,3 @@ +contract c { + modifier mod { if (msg.sender == 0x0000000000000000000000000000000000000000) _; } +} diff --git a/test/libsolidity/syntaxTests/parsing/modifier_arguments.sol b/test/libsolidity/syntaxTests/parsing/modifier_arguments.sol new file mode 100644 index 00000000..27b5b13b --- /dev/null +++ b/test/libsolidity/syntaxTests/parsing/modifier_arguments.sol @@ -0,0 +1,3 @@ +contract c { + modifier mod(address a) { if (msg.sender == a) _; } +} diff --git a/test/libsolidity/syntaxTests/parsing/modifier_invocation.sol b/test/libsolidity/syntaxTests/parsing/modifier_invocation.sol new file mode 100644 index 00000000..cf986efe --- /dev/null +++ b/test/libsolidity/syntaxTests/parsing/modifier_invocation.sol @@ -0,0 +1,7 @@ +contract c { + modifier mod1(uint a) { if (msg.sender == address(a)) _; } + modifier mod2 { if (msg.sender == address(2)) _; } + function f() public mod1(7) mod2 { } +} +// ---- +// Warning: (135-171): Function state mutability can be restricted to view diff --git a/test/libsolidity/syntaxTests/parsing/multi_arrays.sol b/test/libsolidity/syntaxTests/parsing/multi_arrays.sol new file mode 100644 index 00000000..ef20ecee --- /dev/null +++ b/test/libsolidity/syntaxTests/parsing/multi_arrays.sol @@ -0,0 +1,3 @@ +contract c { + mapping(uint => mapping(uint => int8)[8][][9])[] x; +} diff --git a/test/libsolidity/syntaxTests/parsing/multi_variable_declarations.sol b/test/libsolidity/syntaxTests/parsing/multi_variable_declarations.sol index 818999df..56c2e280 100644 --- a/test/libsolidity/syntaxTests/parsing/multi_variable_declarations.sol +++ b/test/libsolidity/syntaxTests/parsing/multi_variable_declarations.sol @@ -1,29 +1,13 @@ contract C { - function f() { - var (a,b,c) = g(); - var (d) = 2; - var (,e) = 3; - var (f,) = 4; - var (x,,) = g(); - var (,y,) = g(); - var () = g(); - var (,,) = g(); + function f() pure public { + (uint a, uint b, uint c) = g(); + (uint d) = 2; + (, uint e) = (3,4); + (uint h,) = (4,5); + (uint x,,) = g(); + (, uint y,) = g(); + a; b; c; d; e; h; x; y; } - function g() returns (uint, uint, uint) {} + function g() pure public returns (uint, uint, uint) {} } // ---- -// Warning: (36-37): Use of the "var" keyword is deprecated. -// Warning: (38-39): Use of the "var" keyword is deprecated. -// Warning: (40-41): Use of the "var" keyword is deprecated. -// Warning: (57-58): Use of the "var" keyword is deprecated. -// Warning: (73-74): Use of the "var" keyword is deprecated. -// Warning: (88-89): Use of the "var" keyword is deprecated. -// Warning: (104-105): Use of the "var" keyword is deprecated. -// Warning: (124-125): Use of the "var" keyword is deprecated. -// Warning: (88-89): This declaration shadows an existing declaration. -// Warning: (52-63): The type of this variable was inferred as uint8, which can hold values between 0 and 255. This is probably not desired. Use an explicit type to silence this warning. -// Warning: (67-79): Different number of components on the left hand side (2) than on the right hand side (1). -// Warning: (67-79): The type of this variable was inferred as uint8, which can hold values between 0 and 255. This is probably not desired. Use an explicit type to silence this warning. -// Warning: (83-95): Different number of components on the left hand side (2) than on the right hand side (1). -// Warning: (83-95): The type of this variable was inferred as uint8, which can hold values between 0 and 255. This is probably not desired. Use an explicit type to silence this warning. -// TypeError: (137-149): Too many components (3) in value for variable assignment (0) needed diff --git a/test/libsolidity/syntaxTests/parsing/multiple_event_arg_trailing_comma.sol b/test/libsolidity/syntaxTests/parsing/multiple_event_arg_trailing_comma.sol new file mode 100644 index 00000000..bfbe7e5c --- /dev/null +++ b/test/libsolidity/syntaxTests/parsing/multiple_event_arg_trailing_comma.sol @@ -0,0 +1,6 @@ +contract test { + event Test(uint a, uint b,); + function(uint a) {} +} +// ---- +// ParserError: (45-46): Unexpected trailing comma in parameter list. diff --git a/test/libsolidity/syntaxTests/parsing/multiple_functions_natspec_documentation.sol b/test/libsolidity/syntaxTests/parsing/multiple_functions_natspec_documentation.sol new file mode 100644 index 00000000..85d9e6a8 --- /dev/null +++ b/test/libsolidity/syntaxTests/parsing/multiple_functions_natspec_documentation.sol @@ -0,0 +1,24 @@ +contract test { + uint256 stateVar; + /// This is test function 1 + function functionName1(bytes32 input) public returns (bytes32 out) {} + /// This is test function 2 + function functionName2(bytes32 input) public returns (bytes32 out) {} + // nothing to see here + function functionName3(bytes32 input) public returns (bytes32 out) {} + /// This is test function 4 + function functionName4(bytes32 input) public returns (bytes32 out) {} +} +// ---- +// Warning: (97-110): Unused function parameter. Remove or comment out the variable name to silence this warning. +// Warning: (128-139): Unused function parameter. Remove or comment out the variable name to silence this warning. +// Warning: (203-216): Unused function parameter. Remove or comment out the variable name to silence this warning. +// Warning: (234-245): Unused function parameter. Remove or comment out the variable name to silence this warning. +// Warning: (304-317): Unused function parameter. Remove or comment out the variable name to silence this warning. +// Warning: (335-346): Unused function parameter. Remove or comment out the variable name to silence this warning. +// Warning: (410-423): Unused function parameter. Remove or comment out the variable name to silence this warning. +// Warning: (441-452): Unused function parameter. Remove or comment out the variable name to silence this warning. +// Warning: (74-143): Function state mutability can be restricted to pure +// Warning: (180-249): Function state mutability can be restricted to pure +// Warning: (281-350): Function state mutability can be restricted to pure +// Warning: (387-456): Function state mutability can be restricted to pure diff --git a/test/libsolidity/syntaxTests/parsing/multiple_modifier_arg_trailing_comma.sol b/test/libsolidity/syntaxTests/parsing/multiple_modifier_arg_trailing_comma.sol new file mode 100644 index 00000000..eb206fb7 --- /dev/null +++ b/test/libsolidity/syntaxTests/parsing/multiple_modifier_arg_trailing_comma.sol @@ -0,0 +1,6 @@ +contract test { + modifier modTest(uint a, uint b,) { _; } + function(uint a) {} +} +// ---- +// ParserError: (51-52): Unexpected trailing comma in parameter list. diff --git a/test/libsolidity/syntaxTests/parsing/multiple_return_param_trailing_comma.sol b/test/libsolidity/syntaxTests/parsing/multiple_return_param_trailing_comma.sol new file mode 100644 index 00000000..2dd8f196 --- /dev/null +++ b/test/libsolidity/syntaxTests/parsing/multiple_return_param_trailing_comma.sol @@ -0,0 +1,5 @@ +contract test { + function() returns (uint a, uint b,) {} +} +// ---- +// ParserError: (54-55): Unexpected trailing comma in parameter list. diff --git a/test/libsolidity/syntaxTests/parsing/new_address_payable.sol b/test/libsolidity/syntaxTests/parsing/new_address_payable.sol new file mode 100644 index 00000000..a52001db --- /dev/null +++ b/test/libsolidity/syntaxTests/parsing/new_address_payable.sol @@ -0,0 +1,5 @@ +contract C { + function f() public pure returns(address payable[] memory m) { + m = new address payable[](10); + } +}
\ No newline at end of file diff --git a/test/libsolidity/syntaxTests/parsing/no_function_params.sol b/test/libsolidity/syntaxTests/parsing/no_function_params.sol index 020f1233..5a024bdb 100644 --- a/test/libsolidity/syntaxTests/parsing/no_function_params.sol +++ b/test/libsolidity/syntaxTests/parsing/no_function_params.sol @@ -1,7 +1,6 @@ contract test { uint256 stateVar; - function functionName() {} + function functionName() public {} } // ---- -// Warning: (36-62): No visibility specified. Defaulting to "public". -// Warning: (36-62): Function state mutability can be restricted to pure +// Warning: (36-69): Function state mutability can be restricted to pure diff --git a/test/libsolidity/syntaxTests/parsing/overloaded_functions.sol b/test/libsolidity/syntaxTests/parsing/overloaded_functions.sol index 1a78d155..fe050d1b 100644 --- a/test/libsolidity/syntaxTests/parsing/overloaded_functions.sol +++ b/test/libsolidity/syntaxTests/parsing/overloaded_functions.sol @@ -1,9 +1,7 @@ contract test { - function fun(uint a) returns(uint r) { return a; } - function fun(uint a, uint b) returns(uint r) { return a + b; } + function fun(uint a) public returns(uint r) { return a; } + function fun(uint a, uint b) public returns(uint r) { return a + b; } } // ---- -// Warning: (17-67): No visibility specified. Defaulting to "public". -// Warning: (69-131): No visibility specified. Defaulting to "public". -// Warning: (17-67): Function state mutability can be restricted to pure -// Warning: (69-131): Function state mutability can be restricted to pure +// Warning: (17-74): Function state mutability can be restricted to pure +// Warning: (76-145): Function state mutability can be restricted to pure diff --git a/test/libsolidity/syntaxTests/parsing/payable_accessor.sol b/test/libsolidity/syntaxTests/parsing/payable_accessor.sol index 6504004b..0bc85784 100644 --- a/test/libsolidity/syntaxTests/parsing/payable_accessor.sol +++ b/test/libsolidity/syntaxTests/parsing/payable_accessor.sol @@ -2,4 +2,4 @@ contract test { uint payable x; } // ---- -// ParserError: (22-29): Expected identifier but got 'payable' +// ParserError: (22-29): State mutability can only be specified for address types. diff --git a/test/libsolidity/syntaxTests/parsing/placeholder_in_function_context.sol b/test/libsolidity/syntaxTests/parsing/placeholder_in_function_context.sol new file mode 100644 index 00000000..a50855c0 --- /dev/null +++ b/test/libsolidity/syntaxTests/parsing/placeholder_in_function_context.sol @@ -0,0 +1,8 @@ +contract c { + function fun() public returns (uint r) { + uint _ = 8; + return _ + 1; + } +} +// ---- +// Warning: (17-105): Function state mutability can be restricted to pure diff --git a/test/libsolidity/syntaxTests/parsing/pragma_illegal.sol b/test/libsolidity/syntaxTests/parsing/pragma_illegal.sol new file mode 100644 index 00000000..3395f6be --- /dev/null +++ b/test/libsolidity/syntaxTests/parsing/pragma_illegal.sol @@ -0,0 +1,4 @@ +pragma ``; +// ---- +// ParserError: (7-8): Token incompatible with Solidity parser as part of pragma directive. +// ParserError: (8-9): Token incompatible with Solidity parser as part of pragma directive. diff --git a/test/libsolidity/syntaxTests/parsing/single_function_param.sol b/test/libsolidity/syntaxTests/parsing/single_function_param.sol index 08e531f1..955f20f0 100644 --- a/test/libsolidity/syntaxTests/parsing/single_function_param.sol +++ b/test/libsolidity/syntaxTests/parsing/single_function_param.sol @@ -1,9 +1,8 @@ contract test { uint256 stateVar; - function functionName(bytes32 input) returns (bytes32 out) {} + function functionName(bytes32 input) public returns (bytes32 out) {} } // ---- -// Warning: (36-97): No visibility specified. Defaulting to "public". // Warning: (58-71): Unused function parameter. Remove or comment out the variable name to silence this warning. -// Warning: (82-93): Unused function parameter. Remove or comment out the variable name to silence this warning. -// Warning: (36-97): Function state mutability can be restricted to pure +// Warning: (89-100): Unused function parameter. Remove or comment out the variable name to silence this warning. +// Warning: (36-104): Function state mutability can be restricted to pure diff --git a/test/libsolidity/syntaxTests/parsing/struct_definition.sol b/test/libsolidity/syntaxTests/parsing/struct_definition.sol new file mode 100644 index 00000000..0c859e5d --- /dev/null +++ b/test/libsolidity/syntaxTests/parsing/struct_definition.sol @@ -0,0 +1,7 @@ +contract test { + uint256 stateVar; + struct MyStructName { + address addr; + uint256 count; + } +} diff --git a/test/libsolidity/syntaxTests/parsing/trailing_dot1.sol b/test/libsolidity/syntaxTests/parsing/trailing_dot1.sol new file mode 100644 index 00000000..d91c385a --- /dev/null +++ b/test/libsolidity/syntaxTests/parsing/trailing_dot1.sol @@ -0,0 +1,7 @@ +contract test { + uint256 a = 2.2e10; + uint256 b = .5E10; + uint256 c = 4.e-2; +} +// ---- +// TypeError: (70-73): Member "e" not found or not visible after argument-dependent lookup in int_const 4. diff --git a/test/libsolidity/syntaxTests/parsing/trailing_dot2.sol b/test/libsolidity/syntaxTests/parsing/trailing_dot2.sol new file mode 100644 index 00000000..38a7ab2d --- /dev/null +++ b/test/libsolidity/syntaxTests/parsing/trailing_dot2.sol @@ -0,0 +1,7 @@ +contract test { + uint256 a = 2.2e10; + uint256 b = .5E10; + uint256 c = 2 + 2.; +} +// ---- +// ParserError: (76-77): Expected identifier but got ';' diff --git a/test/libsolidity/syntaxTests/parsing/trailing_dot3.sol b/test/libsolidity/syntaxTests/parsing/trailing_dot3.sol new file mode 100644 index 00000000..6a126cb3 --- /dev/null +++ b/test/libsolidity/syntaxTests/parsing/trailing_dot3.sol @@ -0,0 +1,4 @@ +contract test { + uint a = 2. +// ---- +// ParserError: (29-29): Expected identifier but got end of source diff --git a/test/libsolidity/syntaxTests/parsing/tuples.sol b/test/libsolidity/syntaxTests/parsing/tuples.sol index 6f739740..875556e9 100644 --- a/test/libsolidity/syntaxTests/parsing/tuples.sol +++ b/test/libsolidity/syntaxTests/parsing/tuples.sol @@ -1,24 +1,11 @@ contract C { - function f() { + function f() public pure { uint a = (1); - var (b,) = (1,); - var (c,d) = (1, 2 + a); - var (e,) = (1, 2, b); + (uint b,) = (1,2); + (uint c, uint d) = (1, 2 + a); + (uint e,) = (1, b); (a) = 3; + a;b;c;d;e; } } // ---- -// Warning: (52-53): Use of the "var" keyword is deprecated. -// Warning: (71-72): Use of the "var" keyword is deprecated. -// Warning: (73-74): Use of the "var" keyword is deprecated. -// Warning: (97-98): Use of the "var" keyword is deprecated. -// Warning: (47-62): Different number of components on the left hand side (2) than on the right hand side (1). -// Warning: (47-62): The type of this variable was inferred as uint8, which can hold values between 0 and 255. This is probably not desired. Use an explicit type to silence this warning. -// Warning: (66-88): The type of this variable was inferred as uint8, which can hold values between 0 and 255. This is probably not desired. Use an explicit type to silence this warning. -// Warning: (92-112): Different number of components on the left hand side (2) than on the right hand side (3). -// Warning: (92-112): The type of this variable was inferred as uint8, which can hold values between 0 and 255. This is probably not desired. Use an explicit type to silence this warning. -// Warning: (14-127): No visibility specified. Defaulting to "public". -// Warning: (71-72): Unused local variable. -// Warning: (73-74): Unused local variable. -// Warning: (97-98): Unused local variable. -// Warning: (14-127): Function state mutability can be restricted to pure diff --git a/test/libsolidity/syntaxTests/parsing/tuples_decl_without_rhs.sol b/test/libsolidity/syntaxTests/parsing/tuples_decl_without_rhs.sol new file mode 100644 index 00000000..dba3e7ac --- /dev/null +++ b/test/libsolidity/syntaxTests/parsing/tuples_decl_without_rhs.sol @@ -0,0 +1,7 @@ +contract C { + function f() public pure { + (uint a, uint b, uint c); + } +} +// ---- +// ParserError: (76-77): Expected '=' but got ';' diff --git a/test/libsolidity/syntaxTests/parsing/two_exact_functions.sol b/test/libsolidity/syntaxTests/parsing/two_exact_functions.sol new file mode 100644 index 00000000..957740d0 --- /dev/null +++ b/test/libsolidity/syntaxTests/parsing/two_exact_functions.sol @@ -0,0 +1,9 @@ +// with support of overloaded functions, during parsing, +// we can't determine whether they match exactly, however +// it will throw DeclarationError in following stage. +contract test { + function fun(uint a) public returns(uint r) { return a; } + function fun(uint a) public returns(uint r) { return a; } +} +// ---- +// DeclarationError: (189-246): Function with same name and arguments defined twice. diff --git a/test/libsolidity/syntaxTests/parsing/unary_plus_expression.sol b/test/libsolidity/syntaxTests/parsing/unary_plus_expression.sol new file mode 100644 index 00000000..5646c43b --- /dev/null +++ b/test/libsolidity/syntaxTests/parsing/unary_plus_expression.sol @@ -0,0 +1,8 @@ +contract test { + function f(uint x) pure public { + uint y = +x; + y; + } +} +// ---- +// SyntaxError: (70-72): Use of unary + is disallowed. diff --git a/test/libsolidity/syntaxTests/parsing/visibility_specifiers.sol b/test/libsolidity/syntaxTests/parsing/visibility_specifiers.sol new file mode 100644 index 00000000..db890b37 --- /dev/null +++ b/test/libsolidity/syntaxTests/parsing/visibility_specifiers.sol @@ -0,0 +1,14 @@ +contract c { + uint private a; + uint internal b; + uint public c; + uint d; + function f() public {} + function f_priv() private {} + function f_internal() internal {} +} +// ---- +// Warning: (58-71): This declaration shadows an existing declaration. +// Warning: (89-111): Function state mutability can be restricted to pure +// Warning: (116-144): Function state mutability can be restricted to pure +// Warning: (149-182): Function state mutability can be restricted to pure diff --git a/test/libsolidity/syntaxTests/parsing/while_loop.sol b/test/libsolidity/syntaxTests/parsing/while_loop.sol new file mode 100644 index 00000000..dbb00a69 --- /dev/null +++ b/test/libsolidity/syntaxTests/parsing/while_loop.sol @@ -0,0 +1,7 @@ +contract test { + function fun() public pure { + uint256 x; + while (true) { x = 1; break; continue; } x = 9; + } +} +// ---- diff --git a/test/libsolidity/syntaxTests/pragma/invalid_pragma.sol b/test/libsolidity/syntaxTests/pragma/invalid_pragma.sol new file mode 100644 index 00000000..cb2585ba --- /dev/null +++ b/test/libsolidity/syntaxTests/pragma/invalid_pragma.sol @@ -0,0 +1,3 @@ +pragma 0; +// ---- +// SyntaxError: (0-9): Invalid pragma "0" diff --git a/test/libsolidity/syntaxTests/pragma/unknown_pragma.sol b/test/libsolidity/syntaxTests/pragma/unknown_pragma.sol new file mode 100644 index 00000000..3a48eddd --- /dev/null +++ b/test/libsolidity/syntaxTests/pragma/unknown_pragma.sol @@ -0,0 +1,3 @@ +pragma thisdoesntexist; +// ---- +// SyntaxError: (0-23): Unknown pragma "thisdoesntexist" diff --git a/test/libsolidity/syntaxTests/returnExpressions/single_return_mismatching_number.sol b/test/libsolidity/syntaxTests/returnExpressions/single_return_mismatching_number.sol new file mode 100644 index 00000000..9741fdfb --- /dev/null +++ b/test/libsolidity/syntaxTests/returnExpressions/single_return_mismatching_number.sol @@ -0,0 +1,14 @@ +contract C +{ + function f() public pure returns (uint) + { + return; + } + function g() public pure returns (uint) + { + return (1, 2); + } +} +// ---- +// TypeError: (71-78): Return arguments required. +// TypeError: (143-156): Different number of arguments in return statement than in returns declaration. diff --git a/test/libsolidity/syntaxTests/returnExpressions/single_return_mismatching_number_named.sol b/test/libsolidity/syntaxTests/returnExpressions/single_return_mismatching_number_named.sol new file mode 100644 index 00000000..53f2d994 --- /dev/null +++ b/test/libsolidity/syntaxTests/returnExpressions/single_return_mismatching_number_named.sol @@ -0,0 +1,14 @@ +contract C +{ + function f() public pure returns (uint a) + { + return; + } + function g() public pure returns (uint a) + { + return (1, 2); + } +} +// ---- +// TypeError: (73-80): Return arguments required. +// TypeError: (147-160): Different number of arguments in return statement than in returns declaration. diff --git a/test/libsolidity/syntaxTests/returnExpressions/tuple_return_mismatching_number.sol b/test/libsolidity/syntaxTests/returnExpressions/tuple_return_mismatching_number.sol new file mode 100644 index 00000000..4ea61c68 --- /dev/null +++ b/test/libsolidity/syntaxTests/returnExpressions/tuple_return_mismatching_number.sol @@ -0,0 +1,19 @@ +contract C +{ + function f() public pure returns (uint, uint) + { + return 1; + } + function g() public pure returns (uint, uint) + { + return (1, 2, 3); + } + function h() public pure returns (uint, uint) + { + return; + } +} +// ---- +// TypeError: (77-85): Different number of arguments in return statement than in returns declaration. +// TypeError: (157-173): Different number of arguments in return statement than in returns declaration. +// TypeError: (245-252): Return arguments required. diff --git a/test/libsolidity/syntaxTests/returnExpressions/tuple_return_mismatching_number_named.sol b/test/libsolidity/syntaxTests/returnExpressions/tuple_return_mismatching_number_named.sol new file mode 100644 index 00000000..86049719 --- /dev/null +++ b/test/libsolidity/syntaxTests/returnExpressions/tuple_return_mismatching_number_named.sol @@ -0,0 +1,19 @@ +contract C +{ + function f() public pure returns (uint a, uint b) + { + return 1; + } + function g() public pure returns (uint a, uint b) + { + return (1, 2, 3); + } + function h() public pure returns (uint a, uint b) + { + return; + } +} +// ---- +// TypeError: (81-89): Different number of arguments in return statement than in returns declaration. +// TypeError: (165-181): Different number of arguments in return statement than in returns declaration. +// TypeError: (257-264): Return arguments required. diff --git a/test/libsolidity/syntaxTests/returnExpressions/valid_returns.sol b/test/libsolidity/syntaxTests/returnExpressions/valid_returns.sol new file mode 100644 index 00000000..e30f9173 --- /dev/null +++ b/test/libsolidity/syntaxTests/returnExpressions/valid_returns.sol @@ -0,0 +1,18 @@ +contract C +{ + function f() public pure { + return; + } + function g() public pure returns (uint) { + return 1; + } + function h() public pure returns (uint a) { + return 1; + } + function i() public pure returns (uint, uint) { + return (1, 2); + } + function j() public pure returns (uint a, uint b) { + return (1, 2); + } +} diff --git a/test/libsolidity/syntaxTests/scoping/double_variable_declaration_disjoint_scope.sol b/test/libsolidity/syntaxTests/scoping/double_variable_declaration_disjoint_scope.sol index d90ec2d7..36bae6a8 100644 --- a/test/libsolidity/syntaxTests/scoping/double_variable_declaration_disjoint_scope.sol +++ b/test/libsolidity/syntaxTests/scoping/double_variable_declaration_disjoint_scope.sol @@ -5,4 +5,5 @@ contract test { } } // ---- -// DeclarationError: (77-83): Identifier already declared. +// Warning: (57-63): Unused local variable. +// Warning: (77-83): Unused local variable. diff --git a/test/libsolidity/syntaxTests/scoping/double_variable_declaration_disjoint_scope_050.sol b/test/libsolidity/syntaxTests/scoping/double_variable_declaration_disjoint_scope_050.sol deleted file mode 100644 index 06bfe7be..00000000 --- a/test/libsolidity/syntaxTests/scoping/double_variable_declaration_disjoint_scope_050.sol +++ /dev/null @@ -1,10 +0,0 @@ -pragma experimental "v0.5.0"; -contract test { - function f() pure public { - { uint x; } - { uint x; } - } -} -// ---- -// Warning: (87-93): Unused local variable. -// Warning: (107-113): Unused local variable. diff --git a/test/libsolidity/syntaxTests/scoping/double_variable_declaration_disjoint_scope_activation.sol b/test/libsolidity/syntaxTests/scoping/double_variable_declaration_disjoint_scope_activation.sol index 1a5ff2f9..0c03ec3e 100644 --- a/test/libsolidity/syntaxTests/scoping/double_variable_declaration_disjoint_scope_activation.sol +++ b/test/libsolidity/syntaxTests/scoping/double_variable_declaration_disjoint_scope_activation.sol @@ -5,4 +5,5 @@ contract test { } } // ---- -// DeclarationError: (75-81): Identifier already declared. +// Warning: (57-63): Unused local variable. +// Warning: (75-81): Unused local variable. diff --git a/test/libsolidity/syntaxTests/scoping/double_variable_declaration_disjoint_scope_activation_050.sol b/test/libsolidity/syntaxTests/scoping/double_variable_declaration_disjoint_scope_activation_050.sol deleted file mode 100644 index 20ea0349..00000000 --- a/test/libsolidity/syntaxTests/scoping/double_variable_declaration_disjoint_scope_activation_050.sol +++ /dev/null @@ -1,10 +0,0 @@ -pragma experimental "v0.5.0"; -contract test { - function f() pure public { - { uint x; } - uint x; - } -} -// ---- -// Warning: (87-93): Unused local variable. -// Warning: (105-111): Unused local variable. diff --git a/test/libsolidity/syntaxTests/scoping/double_variable_declaration_same_and_disjoint_scope.sol b/test/libsolidity/syntaxTests/scoping/double_variable_declaration_same_and_disjoint_scope.sol new file mode 100644 index 00000000..45c5ff2d --- /dev/null +++ b/test/libsolidity/syntaxTests/scoping/double_variable_declaration_same_and_disjoint_scope.sol @@ -0,0 +1,10 @@ +contract test { + function f() pure public { + uint x; + { uint x; } + uint x; + } +} +// ---- +// Warning: (73-79): This declaration shadows an existing declaration. +// DeclarationError: (91-97): Identifier already declared. diff --git a/test/libsolidity/syntaxTests/scoping/double_variable_declaration_same_scope.sol b/test/libsolidity/syntaxTests/scoping/double_variable_declaration_same_scope.sol new file mode 100644 index 00000000..72c31f73 --- /dev/null +++ b/test/libsolidity/syntaxTests/scoping/double_variable_declaration_same_scope.sol @@ -0,0 +1,8 @@ +contract test { + function f() pure public { + uint x; + uint x; + } +} +// ---- +// DeclarationError: (71-77): Identifier already declared. diff --git a/test/libsolidity/syntaxTests/scoping/function_state_variable_conflict.sol b/test/libsolidity/syntaxTests/scoping/function_state_variable_conflict.sol new file mode 100644 index 00000000..d717981b --- /dev/null +++ b/test/libsolidity/syntaxTests/scoping/function_state_variable_conflict.sol @@ -0,0 +1,6 @@ +contract C { + function f(uint) public pure {} + uint public f = 0; +} +// ---- +// DeclarationError: (53-70): Identifier already declared. diff --git a/test/libsolidity/syntaxTests/scoping/poly_variable_declaration_same_scope.sol b/test/libsolidity/syntaxTests/scoping/poly_variable_declaration_same_scope.sol new file mode 100644 index 00000000..e414f611 --- /dev/null +++ b/test/libsolidity/syntaxTests/scoping/poly_variable_declaration_same_scope.sol @@ -0,0 +1,16 @@ +contract test { + function f() pure public { + uint x; + uint x; + uint x; + uint x; + uint x; + uint x; + } +} +// ---- +// DeclarationError: (71-77): Identifier already declared. +// DeclarationError: (87-93): Identifier already declared. +// DeclarationError: (103-109): Identifier already declared. +// DeclarationError: (119-125): Identifier already declared. +// DeclarationError: (135-141): Identifier already declared. diff --git a/test/libsolidity/syntaxTests/scoping/scoping.sol b/test/libsolidity/syntaxTests/scoping/scoping.sol index 34b055d9..dae5a42d 100644 --- a/test/libsolidity/syntaxTests/scoping/scoping.sol +++ b/test/libsolidity/syntaxTests/scoping/scoping.sol @@ -1,4 +1,3 @@ -pragma experimental "v0.5.0"; contract test { function f() public { { @@ -8,4 +7,4 @@ contract test { } } // ---- -// DeclarationError: (123-124): Undeclared identifier. +// DeclarationError: (93-94): Undeclared identifier. diff --git a/test/libsolidity/syntaxTests/scoping/scoping_activation.sol b/test/libsolidity/syntaxTests/scoping/scoping_activation.sol index 7334bc49..8522a0e3 100644 --- a/test/libsolidity/syntaxTests/scoping/scoping_activation.sol +++ b/test/libsolidity/syntaxTests/scoping/scoping_activation.sol @@ -1,4 +1,3 @@ -pragma experimental "v0.5.0"; contract test { function f() pure public { x = 3; @@ -6,4 +5,4 @@ contract test { } } // ---- -// DeclarationError: (85-86): Undeclared identifier. Did you mean "x"? +// DeclarationError: (55-56): Undeclared identifier. "x" is not (or not yet) visible at this point. diff --git a/test/libsolidity/syntaxTests/scoping/scoping_activation_old.sol b/test/libsolidity/syntaxTests/scoping/scoping_activation_old.sol index d893a889..8522a0e3 100644 --- a/test/libsolidity/syntaxTests/scoping/scoping_activation_old.sol +++ b/test/libsolidity/syntaxTests/scoping/scoping_activation_old.sol @@ -4,3 +4,5 @@ contract test { uint x; } } +// ---- +// DeclarationError: (55-56): Undeclared identifier. "x" is not (or not yet) visible at this point. diff --git a/test/libsolidity/syntaxTests/scoping/scoping_for.sol b/test/libsolidity/syntaxTests/scoping/scoping_for.sol index 6e5b7095..a882d1ca 100644 --- a/test/libsolidity/syntaxTests/scoping/scoping_for.sol +++ b/test/libsolidity/syntaxTests/scoping/scoping_for.sol @@ -1,4 +1,3 @@ -pragma experimental "v0.5.0"; contract test { function f() pure public { for (uint x = 0; x < 10; x ++){ diff --git a/test/libsolidity/syntaxTests/scoping/scoping_for2.sol b/test/libsolidity/syntaxTests/scoping/scoping_for2.sol index eb74b8ab..f8c5c19b 100644 --- a/test/libsolidity/syntaxTests/scoping/scoping_for2.sol +++ b/test/libsolidity/syntaxTests/scoping/scoping_for2.sol @@ -1,4 +1,3 @@ -pragma experimental "v0.5.0"; contract test { function f() pure public { for (uint x = 0; x < 10; x ++) diff --git a/test/libsolidity/syntaxTests/scoping/scoping_for3.sol b/test/libsolidity/syntaxTests/scoping/scoping_for3.sol index 1814cb47..81e34562 100644 --- a/test/libsolidity/syntaxTests/scoping/scoping_for3.sol +++ b/test/libsolidity/syntaxTests/scoping/scoping_for3.sol @@ -1,4 +1,3 @@ -pragma experimental "v0.5.0"; contract test { function f() pure public { for (uint x = 0; x < 10; x ++){ @@ -8,4 +7,4 @@ contract test { } } // ---- -// DeclarationError: (154-155): Undeclared identifier. +// DeclarationError: (124-125): Undeclared identifier. diff --git a/test/libsolidity/syntaxTests/scoping/scoping_for_decl_in_body.sol b/test/libsolidity/syntaxTests/scoping/scoping_for_decl_in_body.sol index 3e80b385..28b88525 100644 --- a/test/libsolidity/syntaxTests/scoping/scoping_for_decl_in_body.sol +++ b/test/libsolidity/syntaxTests/scoping/scoping_for_decl_in_body.sol @@ -1,4 +1,3 @@ -pragma experimental "v0.5.0"; contract test { function f() pure public { for (;; y++){ @@ -7,4 +6,4 @@ contract test { } } // ---- -// DeclarationError: (93-94): Undeclared identifier. +// DeclarationError: (63-64): Undeclared identifier. diff --git a/test/libsolidity/syntaxTests/scoping/scoping_old.sol b/test/libsolidity/syntaxTests/scoping/scoping_old.sol index 83f6b60b..70e5ee0c 100644 --- a/test/libsolidity/syntaxTests/scoping/scoping_old.sol +++ b/test/libsolidity/syntaxTests/scoping/scoping_old.sol @@ -4,3 +4,5 @@ contract test { uint256 x = 2; } } +// ---- +// DeclarationError: (55-56): Undeclared identifier. "x" is not (or not yet) visible at this point. diff --git a/test/libsolidity/syntaxTests/scoping/scoping_self_use.sol b/test/libsolidity/syntaxTests/scoping/scoping_self_use.sol index 9e2c0171..a5087c57 100644 --- a/test/libsolidity/syntaxTests/scoping/scoping_self_use.sol +++ b/test/libsolidity/syntaxTests/scoping/scoping_self_use.sol @@ -3,3 +3,5 @@ contract test { uint a = a; } } +// ---- +// DeclarationError: (64-65): Undeclared identifier. "a" is not (or not yet) visible at this point. diff --git a/test/libsolidity/syntaxTests/scoping/scoping_self_use_050.sol b/test/libsolidity/syntaxTests/scoping/scoping_self_use_050.sol deleted file mode 100644 index ab3dcefb..00000000 --- a/test/libsolidity/syntaxTests/scoping/scoping_self_use_050.sol +++ /dev/null @@ -1,8 +0,0 @@ -pragma experimental "v0.5.0"; -contract test { - function f() pure public { - uint a = a; - } -} -// ---- -// DeclarationError: (94-95): Undeclared identifier. Did you mean "a"? diff --git a/test/libsolidity/syntaxTests/scoping/state_variable_function_conflict.sol b/test/libsolidity/syntaxTests/scoping/state_variable_function_conflict.sol new file mode 100644 index 00000000..0c732f7f --- /dev/null +++ b/test/libsolidity/syntaxTests/scoping/state_variable_function_conflict.sol @@ -0,0 +1,6 @@ +contract C { + uint public f = 0; + function f(uint) public pure {} +} +// ---- +// DeclarationError: (40-71): Identifier already declared. diff --git a/test/libsolidity/syntaxTests/scoping/state_variable_function_conflict_former_crash.sol b/test/libsolidity/syntaxTests/scoping/state_variable_function_conflict_former_crash.sol new file mode 100644 index 00000000..fb9180c6 --- /dev/null +++ b/test/libsolidity/syntaxTests/scoping/state_variable_function_conflict_former_crash.sol @@ -0,0 +1,14 @@ +// This used to crash with some compiler versions. +contract SomeContract { + + uint public balance = 0; + + function balance(uint number) public {} + + function doSomething() public { + balance(3); + } +} +// ---- +// DeclarationError: (106-145): Identifier already declared. +// TypeError: (185-195): Type is not callable diff --git a/test/libsolidity/syntaxTests/specialFunctions/abidecode/abi_decode_calldata.sol b/test/libsolidity/syntaxTests/specialFunctions/abidecode/abi_decode_calldata.sol new file mode 100644 index 00000000..28d2f2e7 --- /dev/null +++ b/test/libsolidity/syntaxTests/specialFunctions/abidecode/abi_decode_calldata.sol @@ -0,0 +1,8 @@ +// This restriction might be lifted in the future +contract C { + function f() public pure { + abi.decode("abc", (bytes calldata)); + } +} +// ---- +// ParserError: (121-129): Expected ',' but got 'calldata' diff --git a/test/libsolidity/syntaxTests/specialFunctions/abidecode/abi_decode_empty.sol b/test/libsolidity/syntaxTests/specialFunctions/abidecode/abi_decode_empty.sol new file mode 100644 index 00000000..9972f01d --- /dev/null +++ b/test/libsolidity/syntaxTests/specialFunctions/abidecode/abi_decode_empty.sol @@ -0,0 +1,7 @@ +contract C { + function f() public pure { + abi.decode("abc", ()); + } +} +// ---- +// Warning: (52-73): Statement has no effect. diff --git a/test/libsolidity/syntaxTests/specialFunctions/abidecode/abi_decode_invalid_arg_count.sol b/test/libsolidity/syntaxTests/specialFunctions/abidecode/abi_decode_invalid_arg_count.sol new file mode 100644 index 00000000..f903e1ed --- /dev/null +++ b/test/libsolidity/syntaxTests/specialFunctions/abidecode/abi_decode_invalid_arg_count.sol @@ -0,0 +1,12 @@ +contract C { + function f() public pure { + abi.decode(); + abi.decode(msg.data); + abi.decode(msg.data, uint, uint); + } +} +// ---- +// TypeError: (46-58): This function takes two arguments, but 0 were provided. +// TypeError: (64-84): This function takes two arguments, but 1 were provided. +// TypeError: (90-122): This function takes two arguments, but 3 were provided. +// TypeError: (111-115): The second argument to "abi.decode" has to be a tuple of types. diff --git a/test/libsolidity/syntaxTests/specialFunctions/abidecode/abi_decode_memory.sol b/test/libsolidity/syntaxTests/specialFunctions/abidecode/abi_decode_memory.sol new file mode 100644 index 00000000..e4667e34 --- /dev/null +++ b/test/libsolidity/syntaxTests/specialFunctions/abidecode/abi_decode_memory.sol @@ -0,0 +1,7 @@ +contract C { + function f() public pure { + abi.decode("abc", (bytes memory, uint[][2] memory)); + } +} +// ---- +// ParserError: (71-77): Expected ',' but got 'memory' diff --git a/test/libsolidity/syntaxTests/specialFunctions/abidecode/abi_decode_memory_v2.sol b/test/libsolidity/syntaxTests/specialFunctions/abidecode/abi_decode_memory_v2.sol new file mode 100644 index 00000000..8a7462d1 --- /dev/null +++ b/test/libsolidity/syntaxTests/specialFunctions/abidecode/abi_decode_memory_v2.sol @@ -0,0 +1,10 @@ +pragma experimental "ABIEncoderV2"; + +contract C { + struct S { uint x; uint[] b; } + function f() public pure returns (S memory, bytes memory, uint[][2] memory) { + return abi.decode("abc", (S, bytes, uint[][2])); + } +} +// ---- +// Warning: (0-35): Experimental features are turned on. Do not use experimental features on live deployments. diff --git a/test/libsolidity/syntaxTests/specialFunctions/abidecode/abi_decode_nontuple.sol b/test/libsolidity/syntaxTests/specialFunctions/abidecode/abi_decode_nontuple.sol new file mode 100644 index 00000000..d813c712 --- /dev/null +++ b/test/libsolidity/syntaxTests/specialFunctions/abidecode/abi_decode_nontuple.sol @@ -0,0 +1,11 @@ +contract C { + function f() public pure { + abi.decode("abc", uint); + abi.decode("abc", this); + abi.decode("abc", f()); + } +} +// ---- +// TypeError: (64-68): The second argument to "abi.decode" has to be a tuple of types. +// TypeError: (93-97): The second argument to "abi.decode" has to be a tuple of types. +// TypeError: (122-125): The second argument to "abi.decode" has to be a tuple of types. diff --git a/test/libsolidity/syntaxTests/specialFunctions/abidecode/abi_decode_simple.sol b/test/libsolidity/syntaxTests/specialFunctions/abidecode/abi_decode_simple.sol new file mode 100644 index 00000000..356ee91c --- /dev/null +++ b/test/libsolidity/syntaxTests/specialFunctions/abidecode/abi_decode_simple.sol @@ -0,0 +1,5 @@ +contract C { + function f() public pure returns (uint, bytes32, C) { + return abi.decode("abc", (uint, bytes32, C)); + } +} diff --git a/test/libsolidity/syntaxTests/specialFunctions/abidecode/abi_decode_single_return.sol b/test/libsolidity/syntaxTests/specialFunctions/abidecode/abi_decode_single_return.sol new file mode 100644 index 00000000..654b7873 --- /dev/null +++ b/test/libsolidity/syntaxTests/specialFunctions/abidecode/abi_decode_single_return.sol @@ -0,0 +1,5 @@ +contract C { + function f() public pure returns (bool) { + return abi.decode("abc", (uint)) == 2; + } +} diff --git a/test/libsolidity/syntaxTests/specialFunctions/abidecode/abi_decode_singletontuple.sol b/test/libsolidity/syntaxTests/specialFunctions/abidecode/abi_decode_singletontuple.sol new file mode 100644 index 00000000..57eccacf --- /dev/null +++ b/test/libsolidity/syntaxTests/specialFunctions/abidecode/abi_decode_singletontuple.sol @@ -0,0 +1,6 @@ +contract C { + function f() public pure returns (uint) { + return abi.decode("abc", (uint)); + } +} +// ---- diff --git a/test/libsolidity/syntaxTests/specialFunctions/abidecode/abi_decode_storage.sol b/test/libsolidity/syntaxTests/specialFunctions/abidecode/abi_decode_storage.sol new file mode 100644 index 00000000..d9910b64 --- /dev/null +++ b/test/libsolidity/syntaxTests/specialFunctions/abidecode/abi_decode_storage.sol @@ -0,0 +1,8 @@ +// This restriction might be lifted in the future +contract C { + function f() { + abi.decode("abc", (bytes storage)); + } +} +// ---- +// ParserError: (109-116): Expected ',' but got 'storage' diff --git a/test/libsolidity/syntaxTests/specialFunctions/encodePacked_array_of_structs.sol b/test/libsolidity/syntaxTests/specialFunctions/encodePacked_array_of_structs.sol new file mode 100644 index 00000000..036e108a --- /dev/null +++ b/test/libsolidity/syntaxTests/specialFunctions/encodePacked_array_of_structs.sol @@ -0,0 +1,9 @@ +contract C { + struct S { uint x; } + function f() public pure { + S[] memory s; + abi.encodePacked(s); + } +} +// ---- +// TypeError: (116-117): This type cannot be encoded. diff --git a/test/libsolidity/syntaxTests/specialFunctions/encode_array_of_struct.sol b/test/libsolidity/syntaxTests/specialFunctions/encode_array_of_struct.sol new file mode 100644 index 00000000..7a4d8250 --- /dev/null +++ b/test/libsolidity/syntaxTests/specialFunctions/encode_array_of_struct.sol @@ -0,0 +1,10 @@ +pragma experimental ABIEncoderV2; +contract C { + struct S { uint x; } + function f() public pure { + S[] memory s; + abi.encode(s); + } +} +// ---- +// Warning: (0-33): Experimental features are turned on. Do not use experimental features on live deployments. diff --git a/test/libsolidity/syntaxTests/specialFunctions/single_non_bytes_arg.sol b/test/libsolidity/syntaxTests/specialFunctions/single_non_bytes_arg.sol index a6ee4bf1..c17d0849 100644 --- a/test/libsolidity/syntaxTests/specialFunctions/single_non_bytes_arg.sol +++ b/test/libsolidity/syntaxTests/specialFunctions/single_non_bytes_arg.sol @@ -7,9 +7,6 @@ contract C { function g(bytes32) pure internal {} } // ---- -// Warning: (54-72): This function only accepts a single "bytes" argument. Please use "abi.encodePacked(...)" or a similar function to encode the data. -// Warning: (54-72): The provided argument of type uint256 is not implicitly convertible to expected type bytes memory. -// Warning: (85-100): This function only accepts a single "bytes" argument. Please use "abi.encodePacked(...)" or a similar function to encode the data. -// Warning: (85-100): The provided argument of type uint256 is not implicitly convertible to expected type bytes memory. -// Warning: (113-131): This function only accepts a single "bytes" argument. Please use "abi.encodePacked(...)" or a similar function to encode the data. -// Warning: (113-131): The provided argument of type uint256 is not implicitly convertible to expected type bytes memory. +// TypeError: (64-71): Invalid type for argument in function call. Invalid implicit conversion from uint256 to bytes memory requested. This function requires a single bytes argument. Use abi.encodePacked(...) to obtain the pre-0.5.0 behaviour or abi.encode(...) to use ABI encoding. +// TypeError: (92-99): Invalid type for argument in function call. Invalid implicit conversion from uint256 to bytes memory requested. This function requires a single bytes argument. Use abi.encodePacked(...) to obtain the pre-0.5.0 behaviour or abi.encode(...) to use ABI encoding. +// TypeError: (123-130): Invalid type for argument in function call. Invalid implicit conversion from uint256 to bytes memory requested. This function requires a single bytes argument. Use abi.encodePacked(...) to obtain the pre-0.5.0 behaviour or abi.encode(...) to use ABI encoding. diff --git a/test/libsolidity/syntaxTests/specialFunctions/types_with_unspecified_encoding_internal_functions.sol b/test/libsolidity/syntaxTests/specialFunctions/types_with_unspecified_encoding_internal_functions.sol index b94a4391..a30e428a 100644 --- a/test/libsolidity/syntaxTests/specialFunctions/types_with_unspecified_encoding_internal_functions.sol +++ b/test/libsolidity/syntaxTests/specialFunctions/types_with_unspecified_encoding_internal_functions.sol @@ -1,6 +1,6 @@ contract C { function f() public pure { - bytes32 h = keccak256(abi.encodePacked(keccak256, f, this.f.gas, block.blockhash)); + bytes32 h = keccak256(abi.encodePacked(keccak256, f, this.f.gas, blockhash)); h; } } @@ -8,4 +8,4 @@ contract C { // TypeError: (91-100): This type cannot be encoded. // TypeError: (102-103): This type cannot be encoded. // TypeError: (105-115): This type cannot be encoded. -// TypeError: (117-132): This type cannot be encoded. +// TypeError: (117-126): This type cannot be encoded. diff --git a/test/libsolidity/syntaxTests/specialFunctions/types_with_unspecified_encoding_special_types.sol b/test/libsolidity/syntaxTests/specialFunctions/types_with_unspecified_encoding_special_types.sol index f1b5606e..6e0b6db4 100644 --- a/test/libsolidity/syntaxTests/specialFunctions/types_with_unspecified_encoding_special_types.sol +++ b/test/libsolidity/syntaxTests/specialFunctions/types_with_unspecified_encoding_special_types.sol @@ -1,13 +1,13 @@ contract C { function f() public pure { - bool a = address(this).call(address(this).delegatecall, super); - bool b = address(this).delegatecall(log0, tx, mulmod); - a; b; + (bool a,) = address(this).call(abi.encode(address(this).delegatecall, super)); + (a,) = address(this).delegatecall(abi.encode(log0, tx, mulmod)); + a; } } // ---- -// TypeError: (80-106): This type cannot be encoded. -// TypeError: (108-113): This type cannot be encoded. -// TypeError: (160-164): This type cannot be encoded. -// TypeError: (166-168): This type cannot be encoded. -// TypeError: (170-176): This type cannot be encoded. +// TypeError: (94-120): This type cannot be encoded. +// TypeError: (122-127): This type cannot be encoded. +// TypeError: (184-188): This type cannot be encoded. +// TypeError: (190-192): This type cannot be encoded. +// TypeError: (194-200): This type cannot be encoded. diff --git a/test/libsolidity/syntaxTests/specialFunctions/types_with_unspecified_encoding_structs.sol b/test/libsolidity/syntaxTests/specialFunctions/types_with_unspecified_encoding_structs.sol index 05f5db0b..a1d3f5af 100644 --- a/test/libsolidity/syntaxTests/specialFunctions/types_with_unspecified_encoding_structs.sol +++ b/test/libsolidity/syntaxTests/specialFunctions/types_with_unspecified_encoding_structs.sol @@ -4,11 +4,10 @@ contract C { struct T { uint y; } T t; function f() public view { - bytes32 a = sha256(s, t); + bytes32 a = sha256(abi.encodePacked(s, t)); a; } } // ---- -// Warning: (132-144): This function only accepts a single "bytes" argument. Please use "abi.encodePacked(...)" or a similar function to encode the data. -// TypeError: (139-140): This type cannot be encoded. -// TypeError: (142-143): This type cannot be encoded. +// TypeError: (156-157): This type cannot be encoded. +// TypeError: (159-160): This type cannot be encoded. diff --git a/test/libsolidity/syntaxTests/specialFunctions/types_with_unspecified_encoding_structs_abiv2.sol b/test/libsolidity/syntaxTests/specialFunctions/types_with_unspecified_encoding_structs_abiv2.sol index 977a7d73..38702825 100644 --- a/test/libsolidity/syntaxTests/specialFunctions/types_with_unspecified_encoding_structs_abiv2.sol +++ b/test/libsolidity/syntaxTests/specialFunctions/types_with_unspecified_encoding_structs_abiv2.sol @@ -6,12 +6,11 @@ contract C { struct T { uint y; } T t; function f() public view { - bytes32 a = sha256(s, t); + bytes32 a = sha256(abi.encodePacked(s, t)); a; } } // ---- // Warning: (0-33): Experimental features are turned on. Do not use experimental features on live deployments. -// Warning: (167-179): This function only accepts a single "bytes" argument. Please use "abi.encodePacked(...)" or a similar function to encode the data. -// TypeError: (174-175): This type cannot be encoded. -// TypeError: (177-178): This type cannot be encoded. +// TypeError: (191-192): This type cannot be encoded. +// TypeError: (194-195): This type cannot be encoded. diff --git a/test/libsolidity/syntaxTests/specialFunctions/types_with_unspecified_encoding_types.sol b/test/libsolidity/syntaxTests/specialFunctions/types_with_unspecified_encoding_types.sol index d10c1718..b50d4449 100644 --- a/test/libsolidity/syntaxTests/specialFunctions/types_with_unspecified_encoding_types.sol +++ b/test/libsolidity/syntaxTests/specialFunctions/types_with_unspecified_encoding_types.sol @@ -1,17 +1,17 @@ contract C { struct S { uint x; } S s; - struct T { } + struct T { uint y; } T t; enum A { X, Y } function f() public pure { - bool a = address(this).delegatecall(S, A, A.X, T, uint, uint[]); + bytes memory a = abi.encodePacked(S, A, A.X, T, uint, uint[]); + a; } } // ---- -// Warning: (51-63): Defining empty structs is deprecated. -// TypeError: (168-169): This type cannot be encoded. -// TypeError: (171-172): This type cannot be encoded. -// TypeError: (179-180): This type cannot be encoded. -// TypeError: (182-186): This type cannot be encoded. -// TypeError: (188-194): This type cannot be encoded. +// TypeError: (174-175): This type cannot be encoded. +// TypeError: (177-178): This type cannot be encoded. +// TypeError: (185-186): This type cannot be encoded. +// TypeError: (188-192): This type cannot be encoded. +// TypeError: (194-200): This type cannot be encoded. diff --git a/test/libsolidity/syntaxTests/string/string_escapes.sol b/test/libsolidity/syntaxTests/string/string_escapes.sol new file mode 100644 index 00000000..51b90d73 --- /dev/null +++ b/test/libsolidity/syntaxTests/string/string_escapes.sol @@ -0,0 +1,7 @@ +contract test { + function f() public pure returns (bytes32) { + bytes32 escapeCharacters = "\t\b\n\r\f\'\"\\\b"; + return escapeCharacters; + } +} +// ---- diff --git a/test/libsolidity/syntaxTests/string/string_new_line.sol b/test/libsolidity/syntaxTests/string/string_new_line.sol new file mode 100644 index 00000000..da2240f7 --- /dev/null +++ b/test/libsolidity/syntaxTests/string/string_new_line.sol @@ -0,0 +1,9 @@ +contract test { + function f() public pure returns (bytes32) { + bytes32 escapeCharacters = "This a test + "; + return escapeCharacters; + } +} +// ---- +// ParserError: (100-112): Expected primary expression. diff --git a/test/libsolidity/syntaxTests/string/string_terminated_by_backslash.sol b/test/libsolidity/syntaxTests/string/string_terminated_by_backslash.sol new file mode 100644 index 00000000..3eaba6af --- /dev/null +++ b/test/libsolidity/syntaxTests/string/string_terminated_by_backslash.sol @@ -0,0 +1,8 @@ +contract test { + function f() public pure returns (bytes32) { + bytes32 escapeCharacters = "text \"; + return escapeCharacters; + } +} +// ---- +// ParserError: (100-109): Expected primary expression.
\ No newline at end of file diff --git a/test/libsolidity/syntaxTests/string/string_unterminated.sol b/test/libsolidity/syntaxTests/string/string_unterminated.sol new file mode 100644 index 00000000..3291781e --- /dev/null +++ b/test/libsolidity/syntaxTests/string/string_unterminated.sol @@ -0,0 +1,7 @@ +contract test { + function f() public pure returns (bytes32) { + bytes32 escapeCharacters = "This a test + } +} +// ---- +// ParserError: (100-112): Expected primary expression.
\ No newline at end of file diff --git a/test/libsolidity/syntaxTests/string/string_unterminated_no_new_line.sol b/test/libsolidity/syntaxTests/string/string_unterminated_no_new_line.sol new file mode 100644 index 00000000..e7be50d2 --- /dev/null +++ b/test/libsolidity/syntaxTests/string/string_unterminated_no_new_line.sol @@ -0,0 +1,4 @@ +contract test { + function f() pure public { "abc\ +// ---- +// ParserError: (47-53): Expected primary expression.
\ No newline at end of file diff --git a/test/libsolidity/syntaxTests/structs/recursion/multi_struct_composition.sol b/test/libsolidity/syntaxTests/structs/recursion/multi_struct_composition.sol index 895bb6c5..e8ece3bc 100644 --- a/test/libsolidity/syntaxTests/structs/recursion/multi_struct_composition.sol +++ b/test/libsolidity/syntaxTests/structs/recursion/multi_struct_composition.sol @@ -9,7 +9,7 @@ contract C { struct W { uint x; } - function f(T) public pure { } + function f(T memory) public pure { } } // ---- // Warning: (0-33): Experimental features are turned on. Do not use experimental features on live deployments. diff --git a/test/libsolidity/syntaxTests/structs/recursion/parallel_structs.sol b/test/libsolidity/syntaxTests/structs/recursion/parallel_structs.sol index 96362ef0..e9b25453 100644 --- a/test/libsolidity/syntaxTests/structs/recursion/parallel_structs.sol +++ b/test/libsolidity/syntaxTests/structs/recursion/parallel_structs.sol @@ -9,7 +9,7 @@ contract TestContract SubStruct subStruct1; SubStruct subStruct2; } - function addTestStruct(TestStruct) public pure {} + function addTestStruct(TestStruct memory) public pure {} } // ---- // Warning: (0-33): Experimental features are turned on. Do not use experimental features on live deployments. diff --git a/test/libsolidity/syntaxTests/structs/recursion/return_recursive_structs.sol b/test/libsolidity/syntaxTests/structs/recursion/return_recursive_structs.sol index 4966a731..c8f9185c 100644 --- a/test/libsolidity/syntaxTests/structs/recursion/return_recursive_structs.sol +++ b/test/libsolidity/syntaxTests/structs/recursion/return_recursive_structs.sol @@ -1,7 +1,7 @@ contract C { struct S { uint a; S[] sub; } - function f() public pure returns (uint, S) { + function f() public pure returns (uint, S memory) { } } // ---- -// TypeError: (91-92): Internal or recursive type is not allowed for public or external functions. +// TypeError: (91-99): Internal or recursive type is not allowed for public or external functions. diff --git a/test/libsolidity/syntaxTests/structs/recursion/return_recursive_structs2.sol b/test/libsolidity/syntaxTests/structs/recursion/return_recursive_structs2.sol index 68113924..a8b7ac75 100644 --- a/test/libsolidity/syntaxTests/structs/recursion/return_recursive_structs2.sol +++ b/test/libsolidity/syntaxTests/structs/recursion/return_recursive_structs2.sol @@ -1,7 +1,7 @@ contract C { struct S { uint a; S[2][] sub; } - function f() public pure returns (uint, S) { + function f() public pure returns (uint, S memory) { } } // ---- -// TypeError: (94-95): Internal or recursive type is not allowed for public or external functions. +// TypeError: (94-102): Internal or recursive type is not allowed for public or external functions. diff --git a/test/libsolidity/syntaxTests/structs/recursion/return_recursive_structs3.sol b/test/libsolidity/syntaxTests/structs/recursion/return_recursive_structs3.sol index 47690d9b..0a5b1bc8 100644 --- a/test/libsolidity/syntaxTests/structs/recursion/return_recursive_structs3.sol +++ b/test/libsolidity/syntaxTests/structs/recursion/return_recursive_structs3.sol @@ -1,8 +1,8 @@ contract C { struct S { uint a; S[][][] sub; } struct T { S s; } - function f() public pure returns (uint x, T t) { + function f() public pure returns (uint x, T memory t) { } } // ---- -// TypeError: (119-122): Internal or recursive type is not allowed for public or external functions. +// TypeError: (119-129): Internal or recursive type is not allowed for public or external functions. diff --git a/test/libsolidity/syntaxTests/structs/recursion/struct_definition_directly_recursive_dynamic_array.sol b/test/libsolidity/syntaxTests/structs/recursion/struct_definition_directly_recursive_dynamic_array.sol new file mode 100644 index 00000000..d847f17c --- /dev/null +++ b/test/libsolidity/syntaxTests/structs/recursion/struct_definition_directly_recursive_dynamic_array.sol @@ -0,0 +1,7 @@ +contract Test { + struct MyStructName { + address addr; + MyStructName[] x; + } +} +// ---- diff --git a/test/libsolidity/syntaxTests/structs/recursion/struct_definition_directly_recursive_fixed_array.sol b/test/libsolidity/syntaxTests/structs/recursion/struct_definition_directly_recursive_fixed_array.sol new file mode 100644 index 00000000..126dda4f --- /dev/null +++ b/test/libsolidity/syntaxTests/structs/recursion/struct_definition_directly_recursive_fixed_array.sol @@ -0,0 +1,8 @@ +contract Test { + struct MyStructName { + address addr; + MyStructName[1] x; + } +} +// ---- +// TypeError: (20-96): Recursive struct definition. diff --git a/test/libsolidity/syntaxTests/structs/recursion/struct_definition_indirectly_recursive_complex.sol b/test/libsolidity/syntaxTests/structs/recursion/struct_definition_indirectly_recursive_complex.sol new file mode 100644 index 00000000..6d35a5d3 --- /dev/null +++ b/test/libsolidity/syntaxTests/structs/recursion/struct_definition_indirectly_recursive_complex.sol @@ -0,0 +1,18 @@ +contract Test { + struct MyStructName1 { + address addr; + uint256 count; + MyStructName4[1] x; + } + struct MyStructName2 { + MyStructName1 x; + } + struct MyStructName3 { + MyStructName2[1] x; + } + struct MyStructName4 { + MyStructName3 x; + } +} +// ---- +// TypeError: (20-121): Recursive struct definition. diff --git a/test/libsolidity/syntaxTests/structs/recursion/struct_definition_indirectly_recursive_dynamic_array1.sol b/test/libsolidity/syntaxTests/structs/recursion/struct_definition_indirectly_recursive_dynamic_array1.sol new file mode 100644 index 00000000..10d7de2c --- /dev/null +++ b/test/libsolidity/syntaxTests/structs/recursion/struct_definition_indirectly_recursive_dynamic_array1.sol @@ -0,0 +1,11 @@ +contract Test { + struct MyStructName1 { + address addr; + uint256 count; + MyStructName2[] x; + } + struct MyStructName2 { + MyStructName1 x; + } +} +// ---- diff --git a/test/libsolidity/syntaxTests/structs/recursion/struct_definition_indirectly_recursive_dynamic_array2.sol b/test/libsolidity/syntaxTests/structs/recursion/struct_definition_indirectly_recursive_dynamic_array2.sol new file mode 100644 index 00000000..f20510ca --- /dev/null +++ b/test/libsolidity/syntaxTests/structs/recursion/struct_definition_indirectly_recursive_dynamic_array2.sol @@ -0,0 +1,11 @@ +contract Test { + struct MyStructName1 { + address addr; + uint256 count; + MyStructName2 x; + } + struct MyStructName2 { + MyStructName1[] x; + } +} +// ---- diff --git a/test/libsolidity/syntaxTests/structs/recursion/struct_definition_indirectly_recursive_dynamic_array3.sol b/test/libsolidity/syntaxTests/structs/recursion/struct_definition_indirectly_recursive_dynamic_array3.sol new file mode 100644 index 00000000..69747e71 --- /dev/null +++ b/test/libsolidity/syntaxTests/structs/recursion/struct_definition_indirectly_recursive_dynamic_array3.sol @@ -0,0 +1,11 @@ +contract Test { + struct MyStructName1 { + address addr; + uint256 count; + MyStructName2[] x; + } + struct MyStructName2 { + MyStructName1[] x; + } +} +// ---- diff --git a/test/libsolidity/syntaxTests/structs/recursion/struct_definition_indirectly_recursive_dynamic_multi_array.sol b/test/libsolidity/syntaxTests/structs/recursion/struct_definition_indirectly_recursive_dynamic_multi_array.sol new file mode 100644 index 00000000..b3507828 --- /dev/null +++ b/test/libsolidity/syntaxTests/structs/recursion/struct_definition_indirectly_recursive_dynamic_multi_array.sol @@ -0,0 +1,21 @@ +contract Test { + struct S1 { + S2[1][] x; + } + struct S2 { + S1 x; + } + struct T1 { + T2[][1] x; + } + struct T2 { + T1 x; + } + struct R1 { + R2[][] x; + } + struct R2 { + R1 x; + } +} +// ---- diff --git a/test/libsolidity/syntaxTests/structs/recursion/struct_definition_indirectly_recursive_fixed_array1.sol b/test/libsolidity/syntaxTests/structs/recursion/struct_definition_indirectly_recursive_fixed_array1.sol new file mode 100644 index 00000000..2c0b90ec --- /dev/null +++ b/test/libsolidity/syntaxTests/structs/recursion/struct_definition_indirectly_recursive_fixed_array1.sol @@ -0,0 +1,12 @@ +contract Test { + struct MyStructName1 { + address addr; + uint256 count; + MyStructName2[1] x; + } + struct MyStructName2 { + MyStructName1 x; + } +} +// ---- +// TypeError: (20-121): Recursive struct definition. diff --git a/test/libsolidity/syntaxTests/structs/recursion/struct_definition_indirectly_recursive_fixed_array2.sol b/test/libsolidity/syntaxTests/structs/recursion/struct_definition_indirectly_recursive_fixed_array2.sol new file mode 100644 index 00000000..3178e569 --- /dev/null +++ b/test/libsolidity/syntaxTests/structs/recursion/struct_definition_indirectly_recursive_fixed_array2.sol @@ -0,0 +1,12 @@ +contract Test { + struct MyStructName1 { + address addr; + uint256 count; + MyStructName2 x; + } + struct MyStructName2 { + MyStructName1[1] x; + } +} +// ---- +// TypeError: (20-118): Recursive struct definition. diff --git a/test/libsolidity/syntaxTests/structs/recursion/struct_definition_indirectly_recursive_fixed_array3.sol b/test/libsolidity/syntaxTests/structs/recursion/struct_definition_indirectly_recursive_fixed_array3.sol new file mode 100644 index 00000000..e34cf9bc --- /dev/null +++ b/test/libsolidity/syntaxTests/structs/recursion/struct_definition_indirectly_recursive_fixed_array3.sol @@ -0,0 +1,12 @@ +contract Test { + struct MyStructName1 { + address addr; + uint256 count; + MyStructName2[1] x; + } + struct MyStructName2 { + MyStructName1[1] x; + } +} +// ---- +// TypeError: (20-121): Recursive struct definition. diff --git a/test/libsolidity/syntaxTests/structs/recursion/struct_definition_indirectly_recursive_fixed_multi_array.sol b/test/libsolidity/syntaxTests/structs/recursion/struct_definition_indirectly_recursive_fixed_multi_array.sol new file mode 100644 index 00000000..ed659b6e --- /dev/null +++ b/test/libsolidity/syntaxTests/structs/recursion/struct_definition_indirectly_recursive_fixed_multi_array.sol @@ -0,0 +1,12 @@ +contract Test { + struct MyStructName1 { + address addr; + uint256 count; + MyStructName2[1][1] x; + } + struct MyStructName2 { + MyStructName1 x; + } +} +// ---- +// TypeError: (20-124): Recursive struct definition. diff --git a/test/libsolidity/syntaxTests/structs/recursion/struct_definition_not_really_recursive_array.sol b/test/libsolidity/syntaxTests/structs/recursion/struct_definition_not_really_recursive_array.sol new file mode 100644 index 00000000..b2053b8a --- /dev/null +++ b/test/libsolidity/syntaxTests/structs/recursion/struct_definition_not_really_recursive_array.sol @@ -0,0 +1,4 @@ +contract Test { + struct S1 { uint a; } + struct S2 { S1[1] x; S1[1] y; } +} diff --git a/test/libsolidity/syntaxTests/tight_packing_literals.sol b/test/libsolidity/syntaxTests/tight_packing_literals.sol index a190adc3..0fc1fc08 100644 --- a/test/libsolidity/syntaxTests/tight_packing_literals.sol +++ b/test/libsolidity/syntaxTests/tight_packing_literals.sol @@ -1,33 +1,8 @@ contract C { - function f() pure public returns (bytes32) { - return keccak256(1); - } - function g() pure public returns (bytes32) { - return sha3(1); - } - function h() pure public returns (bytes32) { - return sha256(1); - } - function j() pure public returns (bytes32) { - return ripemd160(1); - } - function k() pure public returns (bytes) { + function k() pure public returns (bytes memory) { return abi.encodePacked(1); } } // ---- -// Warning: (87-88): The type of "int_const 1" was inferred as uint8. This is probably not desired. Use an explicit type to silence this warning. -// Warning: (77-89): This function only accepts a single "bytes" argument. Please use "abi.encodePacked(...)" or a similar function to encode the data. -// Warning: (77-89): The provided argument of type int_const 1 is not implicitly convertible to expected type bytes memory. -// Warning: (161-168): "sha3" has been deprecated in favour of "keccak256" -// Warning: (166-167): The type of "int_const 1" was inferred as uint8. This is probably not desired. Use an explicit type to silence this warning. -// Warning: (161-168): This function only accepts a single "bytes" argument. Please use "abi.encodePacked(...)" or a similar function to encode the data. -// Warning: (161-168): The provided argument of type int_const 1 is not implicitly convertible to expected type bytes memory. -// Warning: (247-248): The type of "int_const 1" was inferred as uint8. This is probably not desired. Use an explicit type to silence this warning. -// Warning: (240-249): This function only accepts a single "bytes" argument. Please use "abi.encodePacked(...)" or a similar function to encode the data. -// Warning: (240-249): The provided argument of type int_const 1 is not implicitly convertible to expected type bytes memory. -// Warning: (331-332): The type of "int_const 1" was inferred as uint8. This is probably not desired. Use an explicit type to silence this warning. -// Warning: (321-333): This function only accepts a single "bytes" argument. Please use "abi.encodePacked(...)" or a similar function to encode the data. -// Warning: (321-333): The provided argument of type int_const 1 is not implicitly convertible to expected type bytes memory. -// Warning: (420-421): The type of "int_const 1" was inferred as uint8. This is probably not desired. Use an explicit type to silence this warning. +// TypeError: (99-100): Cannot perform packed encoding for a literal. Please convert it to an explicit type first. diff --git a/test/libsolidity/syntaxTests/tight_packing_literals_050.sol b/test/libsolidity/syntaxTests/tight_packing_literals_050.sol deleted file mode 100644 index b7557d2a..00000000 --- a/test/libsolidity/syntaxTests/tight_packing_literals_050.sol +++ /dev/null @@ -1,34 +0,0 @@ -pragma experimental "v0.5.0"; -contract C { - function f() pure public returns (bytes32) { - return keccak256(1); - } - function g() pure public returns (bytes32) { - return sha3(1); - } - function h() pure public returns (bytes32) { - return sha256(1); - } - function j() pure public returns (bytes32) { - return ripemd160(1); - } - function k() pure public returns (bytes) { - return abi.encodePacked(1); - } -} - -// ---- -// TypeError: (117-118): Cannot perform packed encoding for a literal. Please convert it to an explicit type first. -// TypeError: (107-119): This function only accepts a single "bytes" argument. Please use "abi.encodePacked(...)" or a similar function to encode the data. -// TypeError: (107-119): The provided argument of type int_const 1 is not implicitly convertible to expected type bytes memory. -// TypeError: (191-198): "sha3" has been deprecated in favour of "keccak256" -// TypeError: (196-197): Cannot perform packed encoding for a literal. Please convert it to an explicit type first. -// TypeError: (191-198): This function only accepts a single "bytes" argument. Please use "abi.encodePacked(...)" or a similar function to encode the data. -// TypeError: (191-198): The provided argument of type int_const 1 is not implicitly convertible to expected type bytes memory. -// TypeError: (277-278): Cannot perform packed encoding for a literal. Please convert it to an explicit type first. -// TypeError: (270-279): This function only accepts a single "bytes" argument. Please use "abi.encodePacked(...)" or a similar function to encode the data. -// TypeError: (270-279): The provided argument of type int_const 1 is not implicitly convertible to expected type bytes memory. -// TypeError: (361-362): Cannot perform packed encoding for a literal. Please convert it to an explicit type first. -// TypeError: (351-363): This function only accepts a single "bytes" argument. Please use "abi.encodePacked(...)" or a similar function to encode the data. -// TypeError: (351-363): The provided argument of type int_const 1 is not implicitly convertible to expected type bytes memory. -// TypeError: (450-451): Cannot perform packed encoding for a literal. Please convert it to an explicit type first. diff --git a/test/libsolidity/syntaxTests/tight_packing_literals_fine.sol b/test/libsolidity/syntaxTests/tight_packing_literals_fine.sol index 2b9b688a..45fc1f72 100644 --- a/test/libsolidity/syntaxTests/tight_packing_literals_fine.sol +++ b/test/libsolidity/syntaxTests/tight_packing_literals_fine.sol @@ -1,30 +1,9 @@ contract C { - function f() pure public returns (bytes32) { - return keccak256(uint8(1)); - } - function g() pure public returns (bytes32) { - return sha3(uint8(1)); - } - function h() pure public returns (bytes32) { - return sha256(uint8(1)); - } - function j() pure public returns (bytes32) { - return ripemd160(uint8(1)); - } - function k() pure public returns (bytes) { + function k() pure public returns (bytes memory) { return abi.encodePacked(uint8(1)); } - function l() pure public returns (bytes) { + function l() pure public returns (bytes memory) { return abi.encode(1); } } // ---- -// Warning: (77-96): This function only accepts a single "bytes" argument. Please use "abi.encodePacked(...)" or a similar function to encode the data. -// Warning: (77-96): The provided argument of type uint8 is not implicitly convertible to expected type bytes memory. -// Warning: (168-182): "sha3" has been deprecated in favour of "keccak256" -// Warning: (168-182): This function only accepts a single "bytes" argument. Please use "abi.encodePacked(...)" or a similar function to encode the data. -// Warning: (168-182): The provided argument of type uint8 is not implicitly convertible to expected type bytes memory. -// Warning: (254-270): This function only accepts a single "bytes" argument. Please use "abi.encodePacked(...)" or a similar function to encode the data. -// Warning: (254-270): The provided argument of type uint8 is not implicitly convertible to expected type bytes memory. -// Warning: (342-361): This function only accepts a single "bytes" argument. Please use "abi.encodePacked(...)" or a similar function to encode the data. -// Warning: (342-361): The provided argument of type uint8 is not implicitly convertible to expected type bytes memory. diff --git a/test/libsolidity/syntaxTests/tupleAssignments/double_storage_crash.sol b/test/libsolidity/syntaxTests/tupleAssignments/double_storage_crash.sol new file mode 100644 index 00000000..3cff3a9a --- /dev/null +++ b/test/libsolidity/syntaxTests/tupleAssignments/double_storage_crash.sol @@ -0,0 +1,11 @@ +// This used to crash in certain compiler versions. +contract CrashContract { + struct S { uint a; } + S x; + function f() public { + (x, x) = 1(x, x); + } +} +// ---- +// TypeError: (170-177): Type is not callable +// TypeError: (170-177): Type tuple() is not implicitly convertible to expected type tuple(struct CrashContract.S storage ref,struct CrashContract.S storage ref). diff --git a/test/libsolidity/syntaxTests/tupleAssignments/err_fill_assignment.sol b/test/libsolidity/syntaxTests/tupleAssignments/err_fill_assignment.sol new file mode 100644 index 00000000..32b381bb --- /dev/null +++ b/test/libsolidity/syntaxTests/tupleAssignments/err_fill_assignment.sol @@ -0,0 +1,11 @@ +contract C { + function f() public pure returns (uint, uint, bytes32) { + uint a; + bytes32 b; + (a,) = f(); + (,b) = f(); + } +} +// ---- +// TypeError: (103-106): Type tuple(uint256,uint256,bytes32) is not implicitly convertible to expected type tuple(uint256,). +// TypeError: (117-120): Type tuple(uint256,uint256,bytes32) is not implicitly convertible to expected type tuple(,bytes32). diff --git a/test/libsolidity/syntaxTests/tupleAssignments/error_fill.sol b/test/libsolidity/syntaxTests/tupleAssignments/error_fill.sol index 5b7f870b..32b381bb 100644 --- a/test/libsolidity/syntaxTests/tupleAssignments/error_fill.sol +++ b/test/libsolidity/syntaxTests/tupleAssignments/error_fill.sol @@ -1,4 +1,3 @@ -pragma experimental "v0.5.0"; contract C { function f() public pure returns (uint, uint, bytes32) { uint a; @@ -8,5 +7,5 @@ contract C { } } // ---- -// TypeError: (126-136): Different number of components on the left hand side (2) than on the right hand side (3). -// TypeError: (140-150): Different number of components on the left hand side (2) than on the right hand side (3). +// TypeError: (103-106): Type tuple(uint256,uint256,bytes32) is not implicitly convertible to expected type tuple(uint256,). +// TypeError: (117-120): Type tuple(uint256,uint256,bytes32) is not implicitly convertible to expected type tuple(,bytes32). diff --git a/test/libsolidity/syntaxTests/tupleAssignments/large_component_count.sol b/test/libsolidity/syntaxTests/tupleAssignments/large_component_count.sol index bbf21d7e..f14641cb 100644 --- a/test/libsolidity/syntaxTests/tupleAssignments/large_component_count.sol +++ b/test/libsolidity/syntaxTests/tupleAssignments/large_component_count.sol @@ -1,4 +1,3 @@ -pragma experimental "v0.5.0"; contract C { function g() public pure returns ( uint, diff --git a/test/libsolidity/syntaxTests/tupleAssignments/nowarn_explicit_singleton_token_expression.sol b/test/libsolidity/syntaxTests/tupleAssignments/nowarn_explicit_singleton_token_expression.sol deleted file mode 100644 index 3262781b..00000000 --- a/test/libsolidity/syntaxTests/tupleAssignments/nowarn_explicit_singleton_token_expression.sol +++ /dev/null @@ -1,8 +0,0 @@ -contract C { - function f() public pure { - uint a; - (a,) = (uint(1),); - } -} -// ---- -// Warning: (53-70): Different number of components on the left hand side (2) than on the right hand side (1). diff --git a/test/libsolidity/syntaxTests/tupleAssignments/warn_fill_assignment.sol b/test/libsolidity/syntaxTests/tupleAssignments/warn_fill_assignment.sol deleted file mode 100644 index a079a509..00000000 --- a/test/libsolidity/syntaxTests/tupleAssignments/warn_fill_assignment.sol +++ /dev/null @@ -1,11 +0,0 @@ -contract C { - function f() public pure returns (uint, uint, bytes32) { - uint a; - bytes32 b; - (a,) = f(); - (,b) = f(); - } -} -// ---- -// Warning: (96-106): Different number of components on the left hand side (2) than on the right hand side (3). -// Warning: (110-120): Different number of components on the left hand side (2) than on the right hand side (3). diff --git a/test/libsolidity/syntaxTests/tupleAssignments/warn_fill_vardecl.sol b/test/libsolidity/syntaxTests/tupleAssignments/warn_fill_vardecl.sol deleted file mode 100644 index 1d243c7c..00000000 --- a/test/libsolidity/syntaxTests/tupleAssignments/warn_fill_vardecl.sol +++ /dev/null @@ -1,11 +0,0 @@ -contract C { - function f() public pure returns (uint, uint, uint, uint) { - // Can later be replaced by (uint a, uint b,) = f(); - var (a,b,) = f(); - a; b; - } -} -// ---- -// Warning: (136-137): Use of the "var" keyword is deprecated. -// Warning: (138-139): Use of the "var" keyword is deprecated. -// Warning: (131-147): Different number of components on the left hand side (3) than on the right hand side (4). diff --git a/test/libsolidity/syntaxTests/tupleAssignments/warn_multiple_storage_storage_copies_fill_left.sol b/test/libsolidity/syntaxTests/tupleAssignments/warn_multiple_storage_storage_copies_fill_left.sol deleted file mode 100644 index b2979804..00000000 --- a/test/libsolidity/syntaxTests/tupleAssignments/warn_multiple_storage_storage_copies_fill_left.sol +++ /dev/null @@ -1,10 +0,0 @@ -contract C { - struct S { uint a; uint b; } - S x; S y; - function f() public { - (,x, y) = (1, 2, y, x); - } -} -// ---- -// Warning: (79-101): This assignment performs two copies to storage. Since storage copies do not first copy to a temporary location, one of them might be overwritten before the second is executed and thus may have unexpected effects. It is safer to perform the copies separately or assign to storage pointers first. -// Warning: (79-101): Different number of components on the left hand side (3) than on the right hand side (4). diff --git a/test/libsolidity/syntaxTests/tupleAssignments/warn_multiple_storage_storage_copies_fill_right.sol b/test/libsolidity/syntaxTests/tupleAssignments/warn_multiple_storage_storage_copies_fill_right.sol deleted file mode 100644 index aa35d7d4..00000000 --- a/test/libsolidity/syntaxTests/tupleAssignments/warn_multiple_storage_storage_copies_fill_right.sol +++ /dev/null @@ -1,10 +0,0 @@ -contract C { - struct S { uint a; uint b; } - S x; S y; - function f() public { - (x, y, ) = (y, x, 1, 2); - } -} -// ---- -// Warning: (79-102): This assignment performs two copies to storage. Since storage copies do not first copy to a temporary location, one of them might be overwritten before the second is executed and thus may have unexpected effects. It is safer to perform the copies separately or assign to storage pointers first. -// Warning: (79-102): Different number of components on the left hand side (3) than on the right hand side (4). diff --git a/test/libsolidity/syntaxTests/types/address/address_abi_decode.sol b/test/libsolidity/syntaxTests/types/address/address_abi_decode.sol new file mode 100644 index 00000000..7be61ad2 --- /dev/null +++ b/test/libsolidity/syntaxTests/types/address/address_abi_decode.sol @@ -0,0 +1,6 @@ +contract C { + function f(bytes memory b) public pure returns (address payable) { + (address payable c) = abi.decode(b, (address)); + return c; + } +}
\ No newline at end of file diff --git a/test/libsolidity/syntaxTests/types/address/address_constant.sol b/test/libsolidity/syntaxTests/types/address/address_constant.sol new file mode 100644 index 00000000..0b1af991 --- /dev/null +++ b/test/libsolidity/syntaxTests/types/address/address_constant.sol @@ -0,0 +1,7 @@ +contract C { + address constant a = address(0); + address payable constant b = address(0); + function f() public pure returns (address, address) { + return (a,b); + } +} diff --git a/test/libsolidity/syntaxTests/types/address/address_constant_assignment.sol b/test/libsolidity/syntaxTests/types/address/address_constant_assignment.sol new file mode 100644 index 00000000..da17ae33 --- /dev/null +++ b/test/libsolidity/syntaxTests/types/address/address_constant_assignment.sol @@ -0,0 +1,11 @@ +contract C { + address constant a = address(0); + address payable constant b = address(0); + function f() public { + a = address(0); + b = address(0); + } +} +// ---- +// TypeError: (129-130): Cannot assign to a constant variable. +// TypeError: (153-154): Cannot assign to a constant variable. diff --git a/test/libsolidity/syntaxTests/types/address/address_in_struct_fail.sol b/test/libsolidity/syntaxTests/types/address/address_in_struct_fail.sol new file mode 100644 index 00000000..9a5b2abb --- /dev/null +++ b/test/libsolidity/syntaxTests/types/address/address_in_struct_fail.sol @@ -0,0 +1,11 @@ +contract A { + struct S { + address payable a; + } + S s; + function f() public { + s.a = address(this); + } +} +// ---- +// TypeError: (110-123): Type address is not implicitly convertible to expected type address payable. diff --git a/test/libsolidity/syntaxTests/types/address/address_in_struct_fine.sol b/test/libsolidity/syntaxTests/types/address/address_in_struct_fine.sol new file mode 100644 index 00000000..5519f0ef --- /dev/null +++ b/test/libsolidity/syntaxTests/types/address/address_in_struct_fine.sol @@ -0,0 +1,20 @@ +contract A { + struct S { + address a; + } + S s; + function f() public { + s.a = address(this); + } +} +contract B { + struct S { + address payable a; + } + S s; + function f() public { + s.a = address(this); + } + function() external payable { + } +}
\ No newline at end of file diff --git a/test/libsolidity/syntaxTests/types/address/address_members_in_contract.sol b/test/libsolidity/syntaxTests/types/address/address_members_in_contract.sol new file mode 100644 index 00000000..eafc8268 --- /dev/null +++ b/test/libsolidity/syntaxTests/types/address/address_members_in_contract.sol @@ -0,0 +1,6 @@ +contract C { + function f() public returns (C) { return this; } + function g() public returns (uint) { return f().balance(); } +} +// ---- +// TypeError: (114-125): Member "balance" not found or not visible after argument-dependent lookup in contract C. Use "address(...).balance" to access this address member. diff --git a/test/libsolidity/syntaxTests/types/address/address_nonpayable_selfdestruct.sol b/test/libsolidity/syntaxTests/types/address/address_nonpayable_selfdestruct.sol new file mode 100644 index 00000000..cc680ff3 --- /dev/null +++ b/test/libsolidity/syntaxTests/types/address/address_nonpayable_selfdestruct.sol @@ -0,0 +1,7 @@ +contract C { + function f(address a) public { + selfdestruct(a); + } +} +// ---- +// TypeError: (69-70): Invalid type for argument in function call. Invalid implicit conversion from address to address payable requested. diff --git a/test/libsolidity/syntaxTests/types/address/address_payable_external_overload.sol b/test/libsolidity/syntaxTests/types/address/address_payable_external_overload.sol new file mode 100644 index 00000000..875532c4 --- /dev/null +++ b/test/libsolidity/syntaxTests/types/address/address_payable_external_overload.sol @@ -0,0 +1,7 @@ +contract C { + function f(address) external pure {} + function f(address payable) external pure {} + +} +// ---- +// TypeError: (58-102): Function overload clash during conversion to external types for arguments. diff --git a/test/libsolidity/syntaxTests/types/address/address_payable_internal_overload_nonpayable.sol b/test/libsolidity/syntaxTests/types/address/address_payable_internal_overload_nonpayable.sol new file mode 100644 index 00000000..65600544 --- /dev/null +++ b/test/libsolidity/syntaxTests/types/address/address_payable_internal_overload_nonpayable.sol @@ -0,0 +1,10 @@ +contract C { + function f(address payable) internal pure {} + function f(address) internal pure returns (uint) {} + function g() internal pure { + address a = address(0); + uint b = f(a); // TODO: should this be valid? + b; + } +} +// ---- diff --git a/test/libsolidity/syntaxTests/types/address/address_payable_internal_overload_payable.sol b/test/libsolidity/syntaxTests/types/address/address_payable_internal_overload_payable.sol new file mode 100644 index 00000000..84142a31 --- /dev/null +++ b/test/libsolidity/syntaxTests/types/address/address_payable_internal_overload_payable.sol @@ -0,0 +1,10 @@ +contract C { + function f(address payable) internal pure {} + function f(address) internal pure {} + function g() internal pure { + address payable a = address(0); + f(a); + } +} +// ---- +// TypeError: (184-185): No unique declaration found after argument-dependent lookup. diff --git a/test/libsolidity/syntaxTests/types/address/address_payable_memory_array_conversion.sol b/test/libsolidity/syntaxTests/types/address/address_payable_memory_array_conversion.sol new file mode 100644 index 00000000..ec58170b --- /dev/null +++ b/test/libsolidity/syntaxTests/types/address/address_payable_memory_array_conversion.sol @@ -0,0 +1,11 @@ +contract C { + function f() public pure { + address payable[] memory a = new address payable[](4); + address[] memory b = new address[](4); + a = b; + b = a; + } +} +// ---- +// TypeError: (166-167): Type address[] memory is not implicitly convertible to expected type address payable[] memory. +// TypeError: (181-182): Type address payable[] memory is not implicitly convertible to expected type address[] memory. diff --git a/test/libsolidity/syntaxTests/types/address/address_payable_public_overload.sol b/test/libsolidity/syntaxTests/types/address/address_payable_public_overload.sol new file mode 100644 index 00000000..839abc26 --- /dev/null +++ b/test/libsolidity/syntaxTests/types/address/address_payable_public_overload.sol @@ -0,0 +1,7 @@ +contract C { + function f(address) public pure {} + function f(address payable) public pure {} + +} +// ---- +// TypeError: (56-98): Function overload clash during conversion to external types for arguments. diff --git a/test/libsolidity/syntaxTests/types/address/address_payable_selfdestruct.sol b/test/libsolidity/syntaxTests/types/address/address_payable_selfdestruct.sol new file mode 100644 index 00000000..bdf43be7 --- /dev/null +++ b/test/libsolidity/syntaxTests/types/address/address_payable_selfdestruct.sol @@ -0,0 +1,5 @@ +contract C { + function f(address payable a) public { + selfdestruct(a); + } +} diff --git a/test/libsolidity/syntaxTests/types/address/address_payable_storage_array_conversion.sol b/test/libsolidity/syntaxTests/types/address/address_payable_storage_array_conversion.sol new file mode 100644 index 00000000..40f85ccc --- /dev/null +++ b/test/libsolidity/syntaxTests/types/address/address_payable_storage_array_conversion.sol @@ -0,0 +1,11 @@ +contract C { + address payable[] a; + address[] b; + function f() public view { + address payable[] storage c = a; + address[] storage d = b; + d = c; // TODO: this could be allowed in the future + } +} +// ---- +// TypeError: (172-173): Type address payable[] storage pointer is not implicitly convertible to expected type address[] storage pointer. diff --git a/test/libsolidity/syntaxTests/types/address/address_payable_storage_array_conversion_fail.sol b/test/libsolidity/syntaxTests/types/address/address_payable_storage_array_conversion_fail.sol new file mode 100644 index 00000000..3c3eb859 --- /dev/null +++ b/test/libsolidity/syntaxTests/types/address/address_payable_storage_array_conversion_fail.sol @@ -0,0 +1,11 @@ +contract C { + address payable[] a; + address[] b; + function f() public view { + address payable[] storage c = a; + address[] storage d = b; + c = d; + } +} +// ---- +// TypeError: (172-173): Type address[] storage pointer is not implicitly convertible to expected type address payable[] storage pointer. diff --git a/test/libsolidity/syntaxTests/types/address/address_to_contract.sol b/test/libsolidity/syntaxTests/types/address/address_to_contract.sol new file mode 100644 index 00000000..629a3df0 --- /dev/null +++ b/test/libsolidity/syntaxTests/types/address/address_to_contract.sol @@ -0,0 +1,6 @@ +contract C { + function f() public pure returns (C c) { + c = C(address(2)); + } +} +// ---- diff --git a/test/libsolidity/syntaxTests/types/address/address_to_contract_implicitly.sol b/test/libsolidity/syntaxTests/types/address/address_to_contract_implicitly.sol new file mode 100644 index 00000000..c9e5ddf6 --- /dev/null +++ b/test/libsolidity/syntaxTests/types/address/address_to_contract_implicitly.sol @@ -0,0 +1,7 @@ +contract C { + function f() public view { + C c = address(2); + } +} +// ---- +// TypeError: (46-62): Type address payable is not implicitly convertible to expected type contract C. diff --git a/test/libsolidity/syntaxTests/types/address/address_to_contract_payable_fallback.sol b/test/libsolidity/syntaxTests/types/address/address_to_contract_payable_fallback.sol new file mode 100644 index 00000000..6917444b --- /dev/null +++ b/test/libsolidity/syntaxTests/types/address/address_to_contract_payable_fallback.sol @@ -0,0 +1,8 @@ +contract C { + function f() public pure returns (C c) { + c = C(address(2)); + } + function() external payable { + } +} +// ---- diff --git a/test/libsolidity/syntaxTests/types/address/address_to_payable_address.sol b/test/libsolidity/syntaxTests/types/address/address_to_payable_address.sol new file mode 100644 index 00000000..1aab9b51 --- /dev/null +++ b/test/libsolidity/syntaxTests/types/address/address_to_payable_address.sol @@ -0,0 +1,10 @@ +contract C { + function f(address a) public pure { + address b; + address payable c = a; + c = b; + } +} +// ---- +// TypeError: (80-101): Type address is not implicitly convertible to expected type address payable. +// TypeError: (115-116): Type address is not implicitly convertible to expected type address payable. diff --git a/test/libsolidity/syntaxTests/types/address/address_to_payable_address_double.sol b/test/libsolidity/syntaxTests/types/address/address_to_payable_address_double.sol new file mode 100644 index 00000000..1e755033 --- /dev/null +++ b/test/libsolidity/syntaxTests/types/address/address_to_payable_address_double.sol @@ -0,0 +1,7 @@ +contract C { + function f(address a) public pure returns (address payable) { + return address(address(a)); + } +} +// ---- +// TypeError: (94-113): Return argument type address is not implicitly convertible to expected type (type of first return variable) address payable. diff --git a/test/libsolidity/syntaxTests/types/address/address_tuple_fail.sol b/test/libsolidity/syntaxTests/types/address/address_tuple_fail.sol new file mode 100644 index 00000000..17e9e7c1 --- /dev/null +++ b/test/libsolidity/syntaxTests/types/address/address_tuple_fail.sol @@ -0,0 +1,8 @@ +contract C { + function f() public view returns (address payable a, address b) { + (address c, address payable d) = (address(this), address(0)); + (a,b) = (c,d); + } +} +// ---- +// TypeError: (169-174): Type tuple(address,address payable) is not implicitly convertible to expected type tuple(address payable,address). diff --git a/test/libsolidity/syntaxTests/types/address/address_tuple_fine.sol b/test/libsolidity/syntaxTests/types/address/address_tuple_fine.sol new file mode 100644 index 00000000..846de1f4 --- /dev/null +++ b/test/libsolidity/syntaxTests/types/address/address_tuple_fine.sol @@ -0,0 +1,6 @@ +contract C { + function f() public view returns (address payable a, address b) { + (address c, address payable d) = (address(this), address(0)); + (a,b) = (d,c); + } +}
\ No newline at end of file diff --git a/test/libsolidity/syntaxTests/types/address/bytes_long_to_payable_address.sol b/test/libsolidity/syntaxTests/types/address/bytes_long_to_payable_address.sol new file mode 100644 index 00000000..ef87ac55 --- /dev/null +++ b/test/libsolidity/syntaxTests/types/address/bytes_long_to_payable_address.sol @@ -0,0 +1,8 @@ +contract C { + function f(bytes32 x) public pure returns (address payable) { + return address(x); + } +} +// ---- +// TypeError: (94-104): Explicit type conversion not allowed from "bytes32" to "address". +// TypeError: (94-104): Return argument type address is not implicitly convertible to expected type (type of first return variable) address payable. diff --git a/test/libsolidity/syntaxTests/types/address/bytes_short_to_payable_address.sol b/test/libsolidity/syntaxTests/types/address/bytes_short_to_payable_address.sol new file mode 100644 index 00000000..2aa60251 --- /dev/null +++ b/test/libsolidity/syntaxTests/types/address/bytes_short_to_payable_address.sol @@ -0,0 +1,8 @@ +contract C { + function f(bytes10 x) public pure returns (address payable) { + return address(x); + } +} +// ---- +// TypeError: (94-104): Explicit type conversion not allowed from "bytes10" to "address". +// TypeError: (94-104): Return argument type address is not implicitly convertible to expected type (type of first return variable) address payable. diff --git a/test/libsolidity/syntaxTests/types/address/bytes_to_payable_address.sol b/test/libsolidity/syntaxTests/types/address/bytes_to_payable_address.sol new file mode 100644 index 00000000..5b6a6714 --- /dev/null +++ b/test/libsolidity/syntaxTests/types/address/bytes_to_payable_address.sol @@ -0,0 +1,5 @@ +contract C { + function f(bytes20 x) public pure returns (address payable) { + return address(x); + } +} diff --git a/test/libsolidity/syntaxTests/types/address/contract_no_fallback_to_payable_address.sol b/test/libsolidity/syntaxTests/types/address/contract_no_fallback_to_payable_address.sol new file mode 100644 index 00000000..777bce00 --- /dev/null +++ b/test/libsolidity/syntaxTests/types/address/contract_no_fallback_to_payable_address.sol @@ -0,0 +1,8 @@ +contract C { + function f() public view { + address payable a = address(this); + a; + } +} +// ---- +// TypeError: (46-79): Type address is not implicitly convertible to expected type address payable. diff --git a/test/libsolidity/syntaxTests/types/address/contract_non_payable_fallback_to_payable_address.sol b/test/libsolidity/syntaxTests/types/address/contract_non_payable_fallback_to_payable_address.sol new file mode 100644 index 00000000..6518eebb --- /dev/null +++ b/test/libsolidity/syntaxTests/types/address/contract_non_payable_fallback_to_payable_address.sol @@ -0,0 +1,10 @@ +contract C { + function f() public view { + address payable a = address(this); + a; + } + function() external { + } +} +// ---- +// TypeError: (46-79): Type address is not implicitly convertible to expected type address payable. diff --git a/test/libsolidity/syntaxTests/types/address/contract_payable_fallback_to_payable_address.sol b/test/libsolidity/syntaxTests/types/address/contract_payable_fallback_to_payable_address.sol new file mode 100644 index 00000000..359beeb4 --- /dev/null +++ b/test/libsolidity/syntaxTests/types/address/contract_payable_fallback_to_payable_address.sol @@ -0,0 +1,9 @@ +contract C { + function f() public view { + address payable a = address(this); + a; + } + function() external payable { + } +} +// ---- diff --git a/test/libsolidity/syntaxTests/types/address/contract_payable_fallback_to_payable_address_implicitly.sol b/test/libsolidity/syntaxTests/types/address/contract_payable_fallback_to_payable_address_implicitly.sol new file mode 100644 index 00000000..4b20b1c6 --- /dev/null +++ b/test/libsolidity/syntaxTests/types/address/contract_payable_fallback_to_payable_address_implicitly.sol @@ -0,0 +1,10 @@ +contract C { + function f() public view { + address payable a = this; + a; + } + function() external payable { + } +} +// ---- +// TypeError: (46-70): Type contract C is not implicitly convertible to expected type address payable. diff --git a/test/libsolidity/syntaxTests/types/address/contract_to_address.sol b/test/libsolidity/syntaxTests/types/address/contract_to_address.sol new file mode 100644 index 00000000..ec2f8184 --- /dev/null +++ b/test/libsolidity/syntaxTests/types/address/contract_to_address.sol @@ -0,0 +1,7 @@ +contract C { + function f() public view { + address a = address(this); + a; + } +} +// ---- diff --git a/test/libsolidity/syntaxTests/types/address/contract_to_address_implicitly.sol b/test/libsolidity/syntaxTests/types/address/contract_to_address_implicitly.sol new file mode 100644 index 00000000..8be9daac --- /dev/null +++ b/test/libsolidity/syntaxTests/types/address/contract_to_address_implicitly.sol @@ -0,0 +1,8 @@ +contract C { + function f() public view { + address a = this; + a; + } +} +// ---- +// TypeError: (46-62): Type contract C is not implicitly convertible to expected type address. diff --git a/test/libsolidity/syntaxTests/types/address/literal_to_address.sol b/test/libsolidity/syntaxTests/types/address/literal_to_address.sol new file mode 100644 index 00000000..20ee56de --- /dev/null +++ b/test/libsolidity/syntaxTests/types/address/literal_to_address.sol @@ -0,0 +1,9 @@ +contract C { + function f() public pure { + address a = address(0); + a = address(1); + address b = 0x0123456789012345678901234567890123456789; + b = 0x9876543210987654321098765432109876543210; + b = 0x9876_5432_1098_7654_3210_9876_5432_1098_7654_3210; + } +} diff --git a/test/libsolidity/syntaxTests/types/address/literal_to_payable_address.sol b/test/libsolidity/syntaxTests/types/address/literal_to_payable_address.sol new file mode 100644 index 00000000..97f4d85d --- /dev/null +++ b/test/libsolidity/syntaxTests/types/address/literal_to_payable_address.sol @@ -0,0 +1,8 @@ +contract C { + function f() public pure { + address payable a = address(0); + a = address(1); + address payable b = 0x0123456789012345678901234567890123456789; + b = 0x9876543210987654321098765432109876543210; + } +}
\ No newline at end of file diff --git a/test/libsolidity/syntaxTests/types/address/nonpayable_address_to_contract_payable_fallback.sol b/test/libsolidity/syntaxTests/types/address/nonpayable_address_to_contract_payable_fallback.sol new file mode 100644 index 00000000..e13a0897 --- /dev/null +++ b/test/libsolidity/syntaxTests/types/address/nonpayable_address_to_contract_payable_fallback.sol @@ -0,0 +1,10 @@ +contract C { + function f() public pure returns (C c) { + address a = address(2); + c = C(a); + } + function() external payable { + } +} +// ---- +// TypeError: (92-96): Explicit type conversion not allowed from non-payable "address" to "contract C", which has a payable fallback function. diff --git a/test/libsolidity/syntaxTests/types/address/payable_address_to_address.sol b/test/libsolidity/syntaxTests/types/address/payable_address_to_address.sol new file mode 100644 index 00000000..f5dbf937 --- /dev/null +++ b/test/libsolidity/syntaxTests/types/address/payable_address_to_address.sol @@ -0,0 +1,7 @@ +contract C { + function f(address payable a) public pure { + address payable b; + address c = a; + c = b; + } +}
\ No newline at end of file diff --git a/test/libsolidity/syntaxTests/types/address/uint_to_payable_address.sol b/test/libsolidity/syntaxTests/types/address/uint_to_payable_address.sol new file mode 100644 index 00000000..9a33985a --- /dev/null +++ b/test/libsolidity/syntaxTests/types/address/uint_to_payable_address.sol @@ -0,0 +1,5 @@ +contract C { + function f(uint x) public pure returns (address payable) { + return address(x); + } +} diff --git a/test/libsolidity/syntaxTests/types/array_index_too_large.sol b/test/libsolidity/syntaxTests/types/array_index_too_large.sol new file mode 100644 index 00000000..06b5071f --- /dev/null +++ b/test/libsolidity/syntaxTests/types/array_index_too_large.sol @@ -0,0 +1,8 @@ +contract C { + function f() public returns (string memory) { + // this used to cause an internal error + return (["zeppelin"][123456789012345678901234567890123456789012345678901234567890123456789012345678]); + } +} +// ---- +// TypeError: (140-218): Type int_const 1234...(70 digits omitted)...5678 is not implicitly convertible to expected type uint256.
\ No newline at end of file diff --git a/test/libsolidity/syntaxTests/types/bool_ops.sol b/test/libsolidity/syntaxTests/types/bool_ops.sol new file mode 100644 index 00000000..91033906 --- /dev/null +++ b/test/libsolidity/syntaxTests/types/bool_ops.sol @@ -0,0 +1,53 @@ +contract C { + function f(bool a, bool b) public pure { + bool c; + // OK + c = !a; + c = !b; + c = a == b; + c = a != b; + c = a || b; + c = a && b; + + // Not OK + c = a > b; + c = a < b; + c = a >= b; + c = a <= b; + c = a & b; + c = a | b; + c = a ^ b; + c = ~a; + c = ~b; + c = a + b; + c = a - b; + c = -a; + c = -b; + c = a * b; + c = a / b; + c = a ** b; + c = a % b; + c = a << b; + c = a >> b; + } +} +// ---- +// TypeError: (231-236): Operator > not compatible with types bool and bool +// TypeError: (250-255): Operator < not compatible with types bool and bool +// TypeError: (269-275): Operator >= not compatible with types bool and bool +// TypeError: (289-295): Operator <= not compatible with types bool and bool +// TypeError: (309-314): Operator & not compatible with types bool and bool +// TypeError: (328-333): Operator | not compatible with types bool and bool +// TypeError: (347-352): Operator ^ not compatible with types bool and bool +// TypeError: (366-368): Unary operator ~ cannot be applied to type bool +// TypeError: (382-384): Unary operator ~ cannot be applied to type bool +// TypeError: (398-403): Operator + not compatible with types bool and bool +// TypeError: (417-422): Operator - not compatible with types bool and bool +// TypeError: (436-438): Unary operator - cannot be applied to type bool +// TypeError: (452-454): Unary operator - cannot be applied to type bool +// TypeError: (468-473): Operator * not compatible with types bool and bool +// TypeError: (487-492): Operator / not compatible with types bool and bool +// TypeError: (506-512): Operator ** not compatible with types bool and bool +// TypeError: (526-531): Operator % not compatible with types bool and bool +// TypeError: (545-551): Operator << not compatible with types bool and bool +// TypeError: (565-571): Operator >> not compatible with types bool and bool diff --git a/test/libsolidity/syntaxTests/types/bytes1_to_uint256.sol b/test/libsolidity/syntaxTests/types/bytes1_to_uint256.sol new file mode 100644 index 00000000..58828a62 --- /dev/null +++ b/test/libsolidity/syntaxTests/types/bytes1_to_uint256.sol @@ -0,0 +1,7 @@ +contract C { + function f() public pure returns(uint256) { + return uint256(bytes1('')); + } +} +// ---- +// TypeError: (76-95): Explicit type conversion not allowed from "bytes1" to "uint256". diff --git a/test/libsolidity/syntaxTests/types/bytes32_to_uint32.sol b/test/libsolidity/syntaxTests/types/bytes32_to_uint32.sol new file mode 100644 index 00000000..77e813ab --- /dev/null +++ b/test/libsolidity/syntaxTests/types/bytes32_to_uint32.sol @@ -0,0 +1,7 @@ +contract C { + function f() public pure returns(uint32) { + return uint32(bytes32('')); + } +} +// ---- +// TypeError: (75-94): Explicit type conversion not allowed from "bytes32" to "uint32". diff --git a/test/libsolidity/syntaxTests/types/bytes_to_contract.sol b/test/libsolidity/syntaxTests/types/bytes_to_contract.sol index 2a3219ec..820dbf9b 100644 --- a/test/libsolidity/syntaxTests/types/bytes_to_contract.sol +++ b/test/libsolidity/syntaxTests/types/bytes_to_contract.sol @@ -1,7 +1,7 @@ contract C { function f() public pure { - C(bytes20(0x1234)); + C(bytes20(uint160(0x1234))); } } // ---- -// TypeError: (64-82): Explicit type conversion not allowed from "bytes20" to "contract C". +// TypeError: (64-91): Explicit type conversion not allowed from "bytes20" to "contract C". diff --git a/test/libsolidity/syntaxTests/types/bytes_to_uint_same_size.sol b/test/libsolidity/syntaxTests/types/bytes_to_uint_same_size.sol new file mode 100644 index 00000000..2963cfd2 --- /dev/null +++ b/test/libsolidity/syntaxTests/types/bytes_to_uint_same_size.sol @@ -0,0 +1,20 @@ +contract C { + function f() public pure returns (uint256) { + return uint256(bytes32(uint256(0))); + } + function g() public pure returns (uint128) { + return uint128(bytes16(uint128(0))); + } + function h() public pure returns (uint64) { + return uint64(bytes8(uint64(0))); + } + function i() public pure returns (uint32) { + return uint32(bytes4(uint32(0))); + } + function j() public pure returns (uint16) { + return uint16(bytes2(uint16(0))); + } + function k() public pure returns (uint8) { + return uint8(bytes1(uint8(0))); + } +} diff --git a/test/libsolidity/syntaxTests/types/bytesm.sol b/test/libsolidity/syntaxTests/types/bytesm.sol index 550760b9..77ff7524 100644 --- a/test/libsolidity/syntaxTests/types/bytesm.sol +++ b/test/libsolidity/syntaxTests/types/bytesm.sol @@ -1,5 +1,5 @@ contract C { - byte b = byte(1); + byte b = byte(0x01); bytes1 b1 = b; bytes2 b2 = b1; bytes3 b3 = b2; diff --git a/test/libsolidity/syntaxTests/types/contract_to_base.sol b/test/libsolidity/syntaxTests/types/contract_to_base.sol new file mode 100644 index 00000000..b0a24e62 --- /dev/null +++ b/test/libsolidity/syntaxTests/types/contract_to_base.sol @@ -0,0 +1,9 @@ +contract A {} +contract B is A {} +contract C { + function f() public { + A a = new B(); + a; + } +} +// ---- diff --git a/test/libsolidity/syntaxTests/types/contract_to_base_base.sol b/test/libsolidity/syntaxTests/types/contract_to_base_base.sol new file mode 100644 index 00000000..e99e5cdc --- /dev/null +++ b/test/libsolidity/syntaxTests/types/contract_to_base_base.sol @@ -0,0 +1,10 @@ +contract A {} +contract B is A {} +contract C is B {} +contract D { + function f() public { + A a = new C(); + a; + } +} +// ---- diff --git a/test/libsolidity/syntaxTests/types/contract_to_derived.sol b/test/libsolidity/syntaxTests/types/contract_to_derived.sol new file mode 100644 index 00000000..ac8df5d1 --- /dev/null +++ b/test/libsolidity/syntaxTests/types/contract_to_derived.sol @@ -0,0 +1,9 @@ +contract B {} +contract A is B {} +contract C { + function f() public pure { + A a = A(new B()); + } +} +// ---- +// TypeError: (85-95): Explicit type conversion not allowed from "contract B" to "contract A". diff --git a/test/libsolidity/syntaxTests/types/contract_to_unrelated_contract.sol b/test/libsolidity/syntaxTests/types/contract_to_unrelated_contract.sol new file mode 100644 index 00000000..b0a4875f --- /dev/null +++ b/test/libsolidity/syntaxTests/types/contract_to_unrelated_contract.sol @@ -0,0 +1,9 @@ +contract A {} +contract B {} +contract C { + function f() public pure { + B b = B(new A()); + } +} +// ---- +// TypeError: (80-90): Explicit type conversion not allowed from "contract A" to "contract B". diff --git a/test/libsolidity/syntaxTests/types/cyclic_dependency_check_on_consts_exhausted.sol b/test/libsolidity/syntaxTests/types/cyclic_dependency_check_on_consts_exhausted.sol new file mode 100644 index 00000000..c6669746 --- /dev/null +++ b/test/libsolidity/syntaxTests/types/cyclic_dependency_check_on_consts_exhausted.sol @@ -0,0 +1,262 @@ +contract A {} +contract Main { + A constant B = C; + A constant C = D; + A constant D = E; + A constant E = F; + A constant F = G; + A constant G = H; + A constant H = I; + A constant I = J; + A constant J = K; + A constant K = L; + A constant L = M; + A constant M = N; + A constant N = O; + A constant O = P; + A constant P = Q; + A constant Q = R; + A constant R = S; + A constant S = T; + A constant T = U; + A constant U = V; + A constant V = W; + A constant W = X; + A constant X = Y; + A constant Y = Z; + A constant Z = BA; + A constant BA = BB; + A constant BB = BC; + A constant BC = BD; + A constant BD = BE; + A constant BE = BF; + A constant BF = BG; + A constant BG = BH; + A constant BH = BI; + A constant BI = BJ; + A constant BJ = BK; + A constant BK = BL; + A constant BL = BM; + A constant BM = BN; + A constant BN = BO; + A constant BO = BP; + A constant BP = BQ; + A constant BQ = BR; + A constant BR = BS; + A constant BS = BT; + A constant BT = BU; + A constant BU = BV; + A constant BV = BW; + A constant BW = BX; + A constant BX = BY; + A constant BY = BZ; + A constant BZ = CA; + A constant CA = CB; + A constant CB = CC; + A constant CC = CD; + A constant CD = CE; + A constant CE = CF; + A constant CF = CG; + A constant CG = CH; + A constant CH = CI; + A constant CI = CJ; + A constant CJ = CK; + A constant CK = CL; + A constant CL = CM; + A constant CM = CN; + A constant CN = CO; + A constant CO = CP; + A constant CP = CQ; + A constant CQ = CR; + A constant CR = CS; + A constant CS = CT; + A constant CT = CU; + A constant CU = CV; + A constant CV = CW; + A constant CW = CX; + A constant CX = CY; + A constant CY = CZ; + A constant CZ = DA; + A constant DA = DB; + A constant DB = DC; + A constant DC = DD; + A constant DD = DE; + A constant DE = DF; + A constant DF = DG; + A constant DG = DH; + A constant DH = DI; + A constant DI = DJ; + A constant DJ = DK; + A constant DK = DL; + A constant DL = DM; + A constant DM = DN; + A constant DN = DO; + A constant DO = DP; + A constant DP = DQ; + A constant DQ = DR; + A constant DR = DS; + A constant DS = DT; + A constant DT = DU; + A constant DU = DV; + A constant DV = DW; + A constant DW = DX; + A constant DX = DY; + A constant DY = DZ; + A constant DZ = EA; + A constant EA = EB; + A constant EB = EC; + A constant EC = ED; + A constant ED = EE; + A constant EE = EF; + A constant EF = EG; + A constant EG = EH; + A constant EH = EI; + A constant EI = EJ; + A constant EJ = EK; + A constant EK = EL; + A constant EL = EM; + A constant EM = EN; + A constant EN = EO; + A constant EO = EP; + A constant EP = EQ; + A constant EQ = ER; + A constant ER = ES; + A constant ES = ET; + A constant ET = EU; + A constant EU = EV; + A constant EV = EW; + A constant EW = EX; + A constant EX = EY; + A constant EY = EZ; + A constant EZ = FA; + A constant FA = FB; + A constant FB = FC; + A constant FC = FD; + A constant FD = FE; + A constant FE = FF; + A constant FF = FG; + A constant FG = FH; + A constant FH = FI; + A constant FI = FJ; + A constant FJ = FK; + A constant FK = FL; + A constant FL = FM; + A constant FM = FN; + A constant FN = FO; + A constant FO = FP; + A constant FP = FQ; + A constant FQ = FR; + A constant FR = FS; + A constant FS = FT; + A constant FT = FU; + A constant FU = FV; + A constant FV = FW; + A constant FW = FX; + A constant FX = FY; + A constant FY = FZ; + A constant FZ = GA; + A constant GA = GB; + A constant GB = GC; + A constant GC = GD; + A constant GD = GE; + A constant GE = GF; + A constant GF = GG; + A constant GG = GH; + A constant GH = GI; + A constant GI = GJ; + A constant GJ = GK; + A constant GK = GL; + A constant GL = GM; + A constant GM = GN; + A constant GN = GO; + A constant GO = GP; + A constant GP = GQ; + A constant GQ = GR; + A constant GR = GS; + A constant GS = GT; + A constant GT = GU; + A constant GU = GV; + A constant GV = GW; + A constant GW = GX; + A constant GX = GY; + A constant GY = GZ; + A constant GZ = HA; + A constant HA = HB; + A constant HB = HC; + A constant HC = HD; + A constant HD = HE; + A constant HE = HF; + A constant HF = HG; + A constant HG = HH; + A constant HH = HI; + A constant HI = HJ; + A constant HJ = HK; + A constant HK = HL; + A constant HL = HM; + A constant HM = HN; + A constant HN = HO; + A constant HO = HP; + A constant HP = HQ; + A constant HQ = HR; + A constant HR = HS; + A constant HS = HT; + A constant HT = HU; + A constant HU = HV; + A constant HV = HW; + A constant HW = HX; + A constant HX = HY; + A constant HY = HZ; + A constant HZ = IA; + A constant IA = IB; + A constant IB = IC; + A constant IC = ID; + A constant ID = IE; + A constant IE = IF; + A constant IF = IG; + A constant IG = IH; + A constant IH = II; + A constant II = IJ; + A constant IJ = IK; + A constant IK = IL; + A constant IL = IM; + A constant IM = IN; + A constant IN = IO; + A constant IO = IP; + A constant IP = IQ; + A constant IQ = IR; + A constant IR = IS; + A constant IS = IT; + A constant IT = IU; + A constant IU = IV; + A constant IV = IW; + A constant IW = IX; + A constant IX = IY; + A constant IY = IZ; + A constant IZ = JA; + A constant JA = JB; + A constant JB = JC; + A constant JC = JD; + A constant JD = JE; + A constant JE = JF; + A constant JF = JG; + A constant JG = JH; + A constant JH = JI; + A constant JI = JJ; + A constant JJ = JK; + A constant JK = JL; + A constant JL = JM; + A constant JM = JN; + A constant JN = JO; + A constant JO = JP; + A constant JP = JQ; + A constant JQ = JR; + A constant JR = JS; + A constant JS = JT; + A constant JT = JU; + A constant JU = JV; + A constant JV = JW; + A constant JW = JX; + A constant JX = A(0x00); +} +// ---- +// DeclarationError: (6105-6123): Variable definition exhausting cyclic dependency validator. diff --git a/test/libsolidity/syntaxTests/types/cyclic_dependency_check_on_consts_good.sol b/test/libsolidity/syntaxTests/types/cyclic_dependency_check_on_consts_good.sol new file mode 100644 index 00000000..7f8c6189 --- /dev/null +++ b/test/libsolidity/syntaxTests/types/cyclic_dependency_check_on_consts_good.sol @@ -0,0 +1,135 @@ +contract A {} +contract Main { + A constant B = C; + A constant C = D; + A constant D = E; + A constant E = F; + A constant F = G; + A constant G = H; + A constant H = I; + A constant I = J; + A constant J = K; + A constant K = L; + A constant L = M; + A constant M = N; + A constant N = O; + A constant O = P; + A constant P = Q; + A constant Q = R; + A constant R = S; + A constant S = T; + A constant T = U; + A constant U = V; + A constant V = W; + A constant W = X; + A constant X = Y; + A constant Y = Z; + A constant Z = BA; + A constant BA = BB; + A constant BB = BC; + A constant BC = BD; + A constant BD = BE; + A constant BE = BF; + A constant BF = BG; + A constant BG = BH; + A constant BH = BI; + A constant BI = BJ; + A constant BJ = BK; + A constant BK = BL; + A constant BL = BM; + A constant BM = BN; + A constant BN = BO; + A constant BO = BP; + A constant BP = BQ; + A constant BQ = BR; + A constant BR = BS; + A constant BS = BT; + A constant BT = BU; + A constant BU = BV; + A constant BV = BW; + A constant BW = BX; + A constant BX = BY; + A constant BY = BZ; + A constant BZ = CA; + A constant CA = CB; + A constant CB = CC; + A constant CC = CD; + A constant CD = CE; + A constant CE = CF; + A constant CF = CG; + A constant CG = CH; + A constant CH = CI; + A constant CI = CJ; + A constant CJ = CK; + A constant CK = CL; + A constant CL = CM; + A constant CM = CN; + A constant CN = CO; + A constant CO = CP; + A constant CP = CQ; + A constant CQ = CR; + A constant CR = CS; + A constant CS = CT; + A constant CT = CU; + A constant CU = CV; + A constant CV = CW; + A constant CW = CX; + A constant CX = CY; + A constant CY = CZ; + A constant CZ = DA; + A constant DA = DB; + A constant DB = DC; + A constant DC = DD; + A constant DD = DE; + A constant DE = DF; + A constant DF = DG; + A constant DG = DH; + A constant DH = DI; + A constant DI = DJ; + A constant DJ = DK; + A constant DK = DL; + A constant DL = DM; + A constant DM = DN; + A constant DN = DO; + A constant DO = DP; + A constant DP = DQ; + A constant DQ = DR; + A constant DR = DS; + A constant DS = DT; + A constant DT = DU; + A constant DU = DV; + A constant DV = DW; + A constant DW = DX; + A constant DX = DY; + A constant DY = DZ; + A constant DZ = EA; + A constant EA = EB; + A constant EB = EC; + A constant EC = ED; + A constant ED = EE; + A constant EE = EF; + A constant EF = EG; + A constant EG = EH; + A constant EH = EI; + A constant EI = EJ; + A constant EJ = EK; + A constant EK = EL; + A constant EL = EM; + A constant EM = EN; + A constant EN = EO; + A constant EO = EP; + A constant EP = EQ; + A constant EQ = ER; + A constant ER = ES; + A constant ES = ET; + A constant ET = EU; + A constant EU = EV; + A constant EV = EW; + A constant EW = EX; + A constant EX = EY; + A constant EY = EZ; + A constant EZ = FA; + A constant FA = FB; + A constant FB = FC; + A constant FC = A(0x00); +} diff --git a/test/libsolidity/syntaxTests/types/cyclic_dependency_check_on_struct_exhausted.sol b/test/libsolidity/syntaxTests/types/cyclic_dependency_check_on_struct_exhausted.sol new file mode 100644 index 00000000..db0ff4af --- /dev/null +++ b/test/libsolidity/syntaxTests/types/cyclic_dependency_check_on_struct_exhausted.sol @@ -0,0 +1,260 @@ +contract Main { + struct B { C m; } + struct C { D m; } + struct D { E m; } + struct E { F m; } + struct F { G m; } + struct G { H m; } + struct H { I m; } + struct I { J m; } + struct J { K m; } + struct K { L m; } + struct L { M m; } + struct M { N m; } + struct N { O m; } + struct O { P m; } + struct P { Q m; } + struct Q { R m; } + struct R { S m; } + struct S { T m; } + struct T { U m; } + struct U { V m; } + struct V { W m; } + struct W { X m; } + struct X { Y m; } + struct Y { Z m; } + struct Z { BA m; } + struct BA { BB m; } + struct BB { BC m; } + struct BC { BD m; } + struct BD { BE m; } + struct BE { BF m; } + struct BF { BG m; } + struct BG { BH m; } + struct BH { BI m; } + struct BI { BJ m; } + struct BJ { BK m; } + struct BK { BL m; } + struct BL { BM m; } + struct BM { BN m; } + struct BN { BO m; } + struct BO { BP m; } + struct BP { BQ m; } + struct BQ { BR m; } + struct BR { BS m; } + struct BS { BT m; } + struct BT { BU m; } + struct BU { BV m; } + struct BV { BW m; } + struct BW { BX m; } + struct BX { BY m; } + struct BY { BZ m; } + struct BZ { CA m; } + struct CA { CB m; } + struct CB { CC m; } + struct CC { CD m; } + struct CD { CE m; } + struct CE { CF m; } + struct CF { CG m; } + struct CG { CH m; } + struct CH { CI m; } + struct CI { CJ m; } + struct CJ { CK m; } + struct CK { CL m; } + struct CL { CM m; } + struct CM { CN m; } + struct CN { CO m; } + struct CO { CP m; } + struct CP { CQ m; } + struct CQ { CR m; } + struct CR { CS m; } + struct CS { CT m; } + struct CT { CU m; } + struct CU { CV m; } + struct CV { CW m; } + struct CW { CX m; } + struct CX { CY m; } + struct CY { CZ m; } + struct CZ { DA m; } + struct DA { DB m; } + struct DB { DC m; } + struct DC { DD m; } + struct DD { DE m; } + struct DE { DF m; } + struct DF { DG m; } + struct DG { DH m; } + struct DH { DI m; } + struct DI { DJ m; } + struct DJ { DK m; } + struct DK { DL m; } + struct DL { DM m; } + struct DM { DN m; } + struct DN { DO m; } + struct DO { DP m; } + struct DP { DQ m; } + struct DQ { DR m; } + struct DR { DS m; } + struct DS { DT m; } + struct DT { DU m; } + struct DU { DV m; } + struct DV { DW m; } + struct DW { DX m; } + struct DX { DY m; } + struct DY { DZ m; } + struct DZ { EA m; } + struct EA { EB m; } + struct EB { EC m; } + struct EC { ED m; } + struct ED { EE m; } + struct EE { EF m; } + struct EF { EG m; } + struct EG { EH m; } + struct EH { EI m; } + struct EI { EJ m; } + struct EJ { EK m; } + struct EK { EL m; } + struct EL { EM m; } + struct EM { EN m; } + struct EN { EO m; } + struct EO { EP m; } + struct EP { EQ m; } + struct EQ { ER m; } + struct ER { ES m; } + struct ES { ET m; } + struct ET { EU m; } + struct EU { EV m; } + struct EV { EW m; } + struct EW { EX m; } + struct EX { EY m; } + struct EY { EZ m; } + struct EZ { FA m; } + struct FA { FB m; } + struct FB { FC m; } + struct FC { FD m; } + struct FD { FE m; } + struct FE { FF m; } + struct FF { FG m; } + struct FG { FH m; } + struct FH { FI m; } + struct FI { FJ m; } + struct FJ { FK m; } + struct FK { FL m; } + struct FL { FM m; } + struct FM { FN m; } + struct FN { FO m; } + struct FO { FP m; } + struct FP { FQ m; } + struct FQ { FR m; } + struct FR { FS m; } + struct FS { FT m; } + struct FT { FU m; } + struct FU { FV m; } + struct FV { FW m; } + struct FW { FX m; } + struct FX { FY m; } + struct FY { FZ m; } + struct FZ { GA m; } + struct GA { GB m; } + struct GB { GC m; } + struct GC { GD m; } + struct GD { GE m; } + struct GE { GF m; } + struct GF { GG m; } + struct GG { GH m; } + struct GH { GI m; } + struct GI { GJ m; } + struct GJ { GK m; } + struct GK { GL m; } + struct GL { GM m; } + struct GM { GN m; } + struct GN { GO m; } + struct GO { GP m; } + struct GP { GQ m; } + struct GQ { GR m; } + struct GR { GS m; } + struct GS { GT m; } + struct GT { GU m; } + struct GU { GV m; } + struct GV { GW m; } + struct GW { GX m; } + struct GX { GY m; } + struct GY { GZ m; } + struct GZ { HA m; } + struct HA { HB m; } + struct HB { HC m; } + struct HC { HD m; } + struct HD { HE m; } + struct HE { HF m; } + struct HF { HG m; } + struct HG { HH m; } + struct HH { HI m; } + struct HI { HJ m; } + struct HJ { HK m; } + struct HK { HL m; } + struct HL { HM m; } + struct HM { HN m; } + struct HN { HO m; } + struct HO { HP m; } + struct HP { HQ m; } + struct HQ { HR m; } + struct HR { HS m; } + struct HS { HT m; } + struct HT { HU m; } + struct HU { HV m; } + struct HV { HW m; } + struct HW { HX m; } + struct HX { HY m; } + struct HY { HZ m; } + struct HZ { IA m; } + struct IA { IB m; } + struct IB { IC m; } + struct IC { ID m; } + struct ID { IE m; } + struct IE { IF m; } + struct IF { IG m; } + struct IG { IH m; } + struct IH { II m; } + struct II { IJ m; } + struct IJ { IK m; } + struct IK { IL m; } + struct IL { IM m; } + struct IM { IN m; } + struct IN { IO m; } + struct IO { IP m; } + struct IP { IQ m; } + struct IQ { IR m; } + struct IR { IS m; } + struct IS { IT m; } + struct IT { IU m; } + struct IU { IV m; } + struct IV { IW m; } + struct IW { IX m; } + struct IX { IY m; } + struct IY { IZ m; } + struct IZ { JA m; } + struct JA { JB m; } + struct JB { JC m; } + struct JC { JD m; } + struct JD { JE m; } + struct JE { JF m; } + struct JF { JG m; } + struct JG { JH m; } + struct JH { JI m; } + struct JI { JJ m; } + struct JJ { JK m; } + struct JK { JL m; } + struct JL { JM m; } + struct JM { JN m; } + struct JN { JO m; } + struct JO { JP m; } + struct JP { JQ m; } + struct JQ { JR m; } + struct JR { JS m; } + struct JS { JT m; } + struct JT { JU m; } + struct JU { JV m; } + struct JV { JW m; } + struct JW { int i; } +} +// ---- +// DeclarationError: (6091-6111): Struct definition exhausting cyclic dependency validator. diff --git a/test/libsolidity/syntaxTests/types/cyclic_dependency_check_on_struct_good.sol b/test/libsolidity/syntaxTests/types/cyclic_dependency_check_on_struct_good.sol new file mode 100644 index 00000000..0419bea6 --- /dev/null +++ b/test/libsolidity/syntaxTests/types/cyclic_dependency_check_on_struct_good.sol @@ -0,0 +1,134 @@ +contract Main { + struct B { C m; } + struct C { D m; } + struct D { E m; } + struct E { F m; } + struct F { G m; } + struct G { H m; } + struct H { I m; } + struct I { J m; } + struct J { K m; } + struct K { L m; } + struct L { M m; } + struct M { N m; } + struct N { O m; } + struct O { P m; } + struct P { Q m; } + struct Q { R m; } + struct R { S m; } + struct S { T m; } + struct T { U m; } + struct U { V m; } + struct V { W m; } + struct W { X m; } + struct X { Y m; } + struct Y { Z m; } + struct Z { BA m; } + struct BA { BB m; } + struct BB { BC m; } + struct BC { BD m; } + struct BD { BE m; } + struct BE { BF m; } + struct BF { BG m; } + struct BG { BH m; } + struct BH { BI m; } + struct BI { BJ m; } + struct BJ { BK m; } + struct BK { BL m; } + struct BL { BM m; } + struct BM { BN m; } + struct BN { BO m; } + struct BO { BP m; } + struct BP { BQ m; } + struct BQ { BR m; } + struct BR { BS m; } + struct BS { BT m; } + struct BT { BU m; } + struct BU { BV m; } + struct BV { BW m; } + struct BW { BX m; } + struct BX { BY m; } + struct BY { BZ m; } + struct BZ { CA m; } + struct CA { CB m; } + struct CB { CC m; } + struct CC { CD m; } + struct CD { CE m; } + struct CE { CF m; } + struct CF { CG m; } + struct CG { CH m; } + struct CH { CI m; } + struct CI { CJ m; } + struct CJ { CK m; } + struct CK { CL m; } + struct CL { CM m; } + struct CM { CN m; } + struct CN { CO m; } + struct CO { CP m; } + struct CP { CQ m; } + struct CQ { CR m; } + struct CR { CS m; } + struct CS { CT m; } + struct CT { CU m; } + struct CU { CV m; } + struct CV { CW m; } + struct CW { CX m; } + struct CX { CY m; } + struct CY { CZ m; } + struct CZ { DA m; } + struct DA { DB m; } + struct DB { DC m; } + struct DC { DD m; } + struct DD { DE m; } + struct DE { DF m; } + struct DF { DG m; } + struct DG { DH m; } + struct DH { DI m; } + struct DI { DJ m; } + struct DJ { DK m; } + struct DK { DL m; } + struct DL { DM m; } + struct DM { DN m; } + struct DN { DO m; } + struct DO { DP m; } + struct DP { DQ m; } + struct DQ { DR m; } + struct DR { DS m; } + struct DS { DT m; } + struct DT { DU m; } + struct DU { DV m; } + struct DV { DW m; } + struct DW { DX m; } + struct DX { DY m; } + struct DY { DZ m; } + struct DZ { EA m; } + struct EA { EB m; } + struct EB { EC m; } + struct EC { ED m; } + struct ED { EE m; } + struct EE { EF m; } + struct EF { EG m; } + struct EG { EH m; } + struct EH { EI m; } + struct EI { EJ m; } + struct EJ { EK m; } + struct EK { EL m; } + struct EL { EM m; } + struct EM { EN m; } + struct EN { EO m; } + struct EO { EP m; } + struct EP { EQ m; } + struct EQ { ER m; } + struct ER { ES m; } + struct ES { ET m; } + struct ET { EU m; } + struct EU { EV m; } + struct EV { EW m; } + struct EW { EX m; } + struct EX { EY m; } + struct EY { EZ m; } + struct EZ { FA m; } + struct FA { FB m; } + struct FB { FC m; } + struct FC { int i; } +} diff --git a/test/libsolidity/syntaxTests/types/decimal_literal_to_bytesXX_explicit.sol b/test/libsolidity/syntaxTests/types/decimal_literal_to_bytesXX_explicit.sol new file mode 100644 index 00000000..ff285a07 --- /dev/null +++ b/test/libsolidity/syntaxTests/types/decimal_literal_to_bytesXX_explicit.sol @@ -0,0 +1,23 @@ +contract C { + function f() public pure { + bytes1 b1 = bytes1(1); + bytes2 b2 = bytes2(1); + bytes2 b3 = bytes2(256); + bytes3 b4 = bytes3(1); + bytes3 b5 = bytes3(65536); + bytes4 b6 = bytes4(1); + bytes4 b7 = bytes4(16777216); + bytes16 b8 = bytes16(1); + bytes32 b9 = bytes32(1); + } +} +// ---- +// TypeError: (60-69): Explicit type conversion not allowed from "int_const 1" to "bytes1". +// TypeError: (88-97): Explicit type conversion not allowed from "int_const 1" to "bytes2". +// TypeError: (116-127): Explicit type conversion not allowed from "int_const 256" to "bytes2". +// TypeError: (146-155): Explicit type conversion not allowed from "int_const 1" to "bytes3". +// TypeError: (174-187): Explicit type conversion not allowed from "int_const 65536" to "bytes3". +// TypeError: (206-215): Explicit type conversion not allowed from "int_const 1" to "bytes4". +// TypeError: (234-250): Explicit type conversion not allowed from "int_const 16777216" to "bytes4". +// TypeError: (270-280): Explicit type conversion not allowed from "int_const 1" to "bytes16". +// TypeError: (300-310): Explicit type conversion not allowed from "int_const 1" to "bytes32". diff --git a/test/libsolidity/syntaxTests/types/decimal_literal_to_bytesXX_implicit.sol b/test/libsolidity/syntaxTests/types/decimal_literal_to_bytesXX_implicit.sol new file mode 100644 index 00000000..e472c43b --- /dev/null +++ b/test/libsolidity/syntaxTests/types/decimal_literal_to_bytesXX_implicit.sol @@ -0,0 +1,23 @@ +contract C { + function f() public pure { + bytes1 b1 = 1; + bytes2 b2 = 1; + bytes2 b3 = 256; + bytes3 b4 = 1; + bytes3 b5 = 65536; + bytes4 b6 = 1; + bytes4 b7 = 16777216; + bytes16 b8 = 1; + bytes32 b9 = 1; + } +} +// ---- +// TypeError: (48-61): Type int_const 1 is not implicitly convertible to expected type bytes1. +// TypeError: (68-81): Type int_const 1 is not implicitly convertible to expected type bytes2. +// TypeError: (88-103): Type int_const 256 is not implicitly convertible to expected type bytes2. +// TypeError: (110-123): Type int_const 1 is not implicitly convertible to expected type bytes3. +// TypeError: (130-147): Type int_const 65536 is not implicitly convertible to expected type bytes3. +// TypeError: (154-167): Type int_const 1 is not implicitly convertible to expected type bytes4. +// TypeError: (174-194): Type int_const 16777216 is not implicitly convertible to expected type bytes4. +// TypeError: (201-215): Type int_const 1 is not implicitly convertible to expected type bytes16. +// TypeError: (222-236): Type int_const 1 is not implicitly convertible to expected type bytes32. diff --git a/test/libsolidity/syntaxTests/types/empty_tuple_event.sol b/test/libsolidity/syntaxTests/types/empty_tuple_event.sol index 3e40b155..898ee8ba 100644 --- a/test/libsolidity/syntaxTests/types/empty_tuple_event.sol +++ b/test/libsolidity/syntaxTests/types/empty_tuple_event.sol @@ -1,10 +1,8 @@ -pragma solidity ^0.4.3; contract C { event SomeEvent(); function a() public { - (SomeEvent(), 7); + (emit SomeEvent(), 7); } } // ---- -// Warning: (95-106): Invoking events without "emit" prefix is deprecated. -// Warning: (95-106): Tuple component cannot be empty. +// ParserError: (71-75): Expected primary expression. diff --git a/test/libsolidity/syntaxTests/types/empty_tuple_event_050.sol b/test/libsolidity/syntaxTests/types/empty_tuple_event_050.sol deleted file mode 100644 index aec5ff2a..00000000 --- a/test/libsolidity/syntaxTests/types/empty_tuple_event_050.sol +++ /dev/null @@ -1,10 +0,0 @@ -pragma experimental "v0.5.0"; -contract C { - event SomeEvent(); - function a() public { - (SomeEvent(), 7); - } -} -// ---- -// TypeError: (101-112): Event invocations have to be prefixed by "emit". -// TypeError: (101-112): Tuple component cannot be empty. diff --git a/test/libsolidity/syntaxTests/types/empty_tuple_function.sol b/test/libsolidity/syntaxTests/types/empty_tuple_function.sol index 05b54442..a898f84a 100644 --- a/test/libsolidity/syntaxTests/types/empty_tuple_function.sol +++ b/test/libsolidity/syntaxTests/types/empty_tuple_function.sol @@ -1,4 +1,3 @@ -pragma solidity ^0.4.3; contract C { function f() private pure {} function a() public pure { @@ -8,5 +7,5 @@ contract C { } } // ---- -// Warning: (162-165): Tuple component cannot be empty. -// Warning: (181-184): Tuple component cannot be empty. +// TypeError: (138-141): Tuple component cannot be empty. +// TypeError: (157-160): Tuple component cannot be empty. diff --git a/test/libsolidity/syntaxTests/types/empty_tuple_function_050.sol b/test/libsolidity/syntaxTests/types/empty_tuple_function_050.sol deleted file mode 100644 index c4b9e03f..00000000 --- a/test/libsolidity/syntaxTests/types/empty_tuple_function_050.sol +++ /dev/null @@ -1,11 +0,0 @@ -pragma experimental "v0.5.0"; -contract C { - function f() private pure {} - function a() public pure { - bool x = true; - bool y = true; - (x) ? (f(), y = false) : (f(), y = false); - } -} -// ---- -// TypeError: (168-171): Tuple component cannot be empty. diff --git a/test/libsolidity/syntaxTests/types/empty_tuple_lvalue.sol b/test/libsolidity/syntaxTests/types/empty_tuple_lvalue.sol index cba30c1b..63b039cd 100644 --- a/test/libsolidity/syntaxTests/types/empty_tuple_lvalue.sol +++ b/test/libsolidity/syntaxTests/types/empty_tuple_lvalue.sol @@ -1,4 +1,3 @@ -pragma solidity ^0.4.3; contract C { function f() private pure {} function a() public { @@ -8,6 +7,6 @@ contract C { } } // ---- -// Warning: (146-149): Tuple component cannot be empty. -// Warning: (151-154): Tuple component cannot be empty. -// TypeError: (145-155): Type tuple(tuple(),tuple()) is not implicitly convertible to expected type tuple(uint256,uint256). +// TypeError: (122-125): Tuple component cannot be empty. +// TypeError: (127-130): Tuple component cannot be empty. +// TypeError: (121-131): Type tuple(tuple(),tuple()) is not implicitly convertible to expected type tuple(uint256,uint256). diff --git a/test/libsolidity/syntaxTests/types/empty_tuple_lvalue_050.sol b/test/libsolidity/syntaxTests/types/empty_tuple_lvalue_050.sol deleted file mode 100644 index b0691778..00000000 --- a/test/libsolidity/syntaxTests/types/empty_tuple_lvalue_050.sol +++ /dev/null @@ -1,11 +0,0 @@ -pragma experimental "v0.5.0"; -contract C { - function f() private pure {} - function a() public { - uint x; - uint y; - (x, y) = (f(), f()); - } -} -// ---- -// TypeError: (152-155): Tuple component cannot be empty. diff --git a/test/libsolidity/syntaxTests/types/empty_tuple_lvalue_array.sol b/test/libsolidity/syntaxTests/types/empty_tuple_lvalue_array.sol index f8b2ae7e..9bc21561 100644 --- a/test/libsolidity/syntaxTests/types/empty_tuple_lvalue_array.sol +++ b/test/libsolidity/syntaxTests/types/empty_tuple_lvalue_array.sol @@ -1,4 +1,3 @@ -pragma solidity ^0.4.3; contract C { function f() private pure {} function a() public { @@ -8,4 +7,4 @@ contract C { } } // ---- -// TypeError: (146-149): Array component cannot be empty. +// TypeError: (122-125): Array component cannot be empty. diff --git a/test/libsolidity/syntaxTests/types/function_call_fail.sol b/test/libsolidity/syntaxTests/types/function_call_fail.sol new file mode 100644 index 00000000..ef52ab44 --- /dev/null +++ b/test/libsolidity/syntaxTests/types/function_call_fail.sol @@ -0,0 +1,9 @@ +contract C { + function f(uint y) public pure { + (4(y)) = 2; + } +} +// ---- +// TypeError: (59-63): Type is not callable +// TypeError: (59-63): Expression has to be an lvalue. +// TypeError: (67-68): Type int_const 2 is not implicitly convertible to expected type tuple(). diff --git a/test/libsolidity/syntaxTests/types/function_call_fail2.sol b/test/libsolidity/syntaxTests/types/function_call_fail2.sol new file mode 100644 index 00000000..389ffce9 --- /dev/null +++ b/test/libsolidity/syntaxTests/types/function_call_fail2.sol @@ -0,0 +1,7 @@ +contract C { + function f(uint y) public pure returns (uint) { + (f(y)) = 2; + } +} +// ---- +// TypeError: (74-78): Expression has to be an lvalue. diff --git a/test/libsolidity/syntaxTests/types/function_types/function_parameter_return_types_fail.sol b/test/libsolidity/syntaxTests/types/function_types/function_parameter_return_types_fail.sol new file mode 100644 index 00000000..0d4fded1 --- /dev/null +++ b/test/libsolidity/syntaxTests/types/function_types/function_parameter_return_types_fail.sol @@ -0,0 +1,42 @@ +contract Test +{ + function uint256_to_uint256(uint256 x) internal pure returns (uint256) { return x; } + function uint256_to_string(uint256 x) internal pure returns (string memory) { return x == 0 ? "a" : "b"; } + function uint256_to_string_storage(uint256) internal pure returns (string storage); + function string_to_uint256(string memory x) internal pure returns (uint256) { return bytes(x).length; } + function string_to_string(string memory x) internal pure returns (string memory) { return x; } + + function uint256_uint256_to_uint256(uint256 x, uint256 y) internal pure returns (uint256) { return x + y; } + function uint256_uint256_to_string(uint256 x, uint256 y) internal pure returns (string memory) { return x == y ? "a" : "b"; } + function string_uint256_to_string(string memory x, uint256 y) internal pure returns (string memory) { return y == 0 ? "a" : x; } + function string_string_to_string(string memory x, string memory y) internal pure returns (string memory) { return bytes(x).length == 0 ? y : x; } + function uint256_string_to_string(uint256 x, string memory y) internal pure returns (string memory) { return x == 0 ? "a" : y; } + + function tests() internal pure + { + function (uint256) internal pure returns (uint256) var_uint256_to_uint256 = uint256_to_string; + function (uint256) internal pure returns (string memory) var_uint256_to_string = uint256_to_string_storage; + function (string memory) internal pure returns (uint256) var_string_to_uint256 = uint256_to_string; + function (string memory) internal pure returns (string memory) var_string_to_string = var_uint256_to_string; + + function (uint256, uint256) internal pure returns (uint256) var_uint256_uint256_to_uint256 = uint256_to_uint256; + function (string memory, uint256) internal pure returns (string memory) var_string_uint256_to_string = string_to_string; + function (string memory, string memory) internal pure returns (string memory) var_string_string_to_string = string_to_string; + + var_uint256_to_uint256(1); + var_uint256_to_string(2); + var_string_to_uint256("a"); + var_string_to_string("b"); + var_uint256_uint256_to_uint256(3, 4); + var_string_uint256_to_string("c", 7); + var_string_string_to_string("d", "e"); + } +} +// ---- +// TypeError: (1218-1311): Type function (uint256) pure returns (string memory) is not implicitly convertible to expected type function (uint256) pure returns (uint256). +// TypeError: (1319-1425): Type function (uint256) pure returns (string storage pointer) is not implicitly convertible to expected type function (uint256) pure returns (string memory). +// TypeError: (1433-1531): Type function (uint256) pure returns (string memory) is not implicitly convertible to expected type function (string memory) pure returns (uint256). +// TypeError: (1539-1646): Type function (uint256) pure returns (string memory) is not implicitly convertible to expected type function (string memory) pure returns (string memory). +// TypeError: (1655-1766): Type function (uint256) pure returns (uint256) is not implicitly convertible to expected type function (uint256,uint256) pure returns (uint256). +// TypeError: (1774-1893): Type function (string memory) pure returns (string memory) is not implicitly convertible to expected type function (string memory,uint256) pure returns (string memory). +// TypeError: (1901-2025): Type function (string memory) pure returns (string memory) is not implicitly convertible to expected type function (string memory,string memory) pure returns (string memory). diff --git a/test/libsolidity/syntaxTests/types/function_types/function_parameter_return_types_success.sol b/test/libsolidity/syntaxTests/types/function_types/function_parameter_return_types_success.sol new file mode 100644 index 00000000..f750632e --- /dev/null +++ b/test/libsolidity/syntaxTests/types/function_types/function_parameter_return_types_success.sol @@ -0,0 +1,39 @@ +contract Test +{ + function uint256_to_uint256(uint256 x) internal pure returns (uint256) { return x; } + function uint256_to_string(uint256 x) internal pure returns (string memory) { return x == 0 ? "a" : "b"; } + function string_to_uint256(string memory x) internal pure returns (uint256) { return bytes(x).length; } + function string_to_string(string memory x) internal pure returns (string memory) { return x; } + + function uint256_uint256_to_uint256(uint256 x, uint256 y) internal pure returns (uint256) { return x + y; } + function uint256_uint256_to_string(uint256 x, uint256 y) internal pure returns (string memory) { return x == y ? "a" : "b"; } + function string_uint256_to_string(string memory x, uint256 y) internal pure returns (string memory) { return y == 0 ? "a" : x; } + function string_string_to_string(string memory x, string memory y) internal pure returns (string memory) { return bytes(x).length == 0 ? y : x; } + function uint256_string_to_string(uint256 x, string memory y) internal pure returns (string memory) { return x == 0 ? "a" : y; } + + function tests() internal pure + { + function (uint256) internal pure returns (uint256) var_uint256_to_uint256 = uint256_to_uint256; + function (uint256) internal pure returns (string memory) var_uint256_to_string = uint256_to_string; + function (string memory) internal pure returns (uint256) var_string_to_uint256 = string_to_uint256; + function (string memory) internal pure returns (string memory) var_string_to_string = string_to_string; + + function (uint256, uint256) internal pure returns (uint256) var_uint256_uint256_to_uint256 = uint256_uint256_to_uint256; + function (uint256, uint256) internal pure returns (string memory) var_uint256_uint256_to_string = uint256_uint256_to_string; + function (string memory, uint256) internal pure returns (string memory) var_string_uint256_to_string = string_uint256_to_string; + function (string memory, string memory) internal pure returns (string memory) var_string_string_to_string = string_string_to_string; + function (uint256, string memory) internal pure returns (string memory) var_uint256_string_to_string = uint256_string_to_string; + + // Avoid unused variable warnings: + var_uint256_to_uint256(1); + var_uint256_to_string(2); + var_string_to_uint256("a"); + var_string_to_string("b"); + var_uint256_uint256_to_uint256(3, 4); + var_uint256_uint256_to_string(5, 6); + var_string_uint256_to_string("c", 7); + var_string_string_to_string("d", "e"); + var_uint256_string_to_string(8, "f"); + } +} +// ---- diff --git a/test/libsolidity/syntaxTests/types/function_types/function_state_mutability_fail.sol b/test/libsolidity/syntaxTests/types/function_types/function_state_mutability_fail.sol new file mode 100644 index 00000000..818d7840 --- /dev/null +++ b/test/libsolidity/syntaxTests/types/function_types/function_state_mutability_fail.sol @@ -0,0 +1,51 @@ +contract Test +{ + function internalPureFunc(uint256 x) internal pure returns (uint256) { return x; } + function internalViewFunc(uint256 x) internal view returns (uint256) { return x; } + function internalMutableFunc(uint256 x) internal returns (uint256) { return x; } + + function externalPureFunc(uint256 x) external pure returns (uint256) { return x; } + function externalViewFunc(uint256 x) external view returns (uint256) { return x; } + function externalPayableFunc(uint256 x) external payable returns (uint256) { return x; } + function externalMutableFunc(uint256 x) external returns (uint256) { return x; } + + function funcTakesInternalPure(function(uint256) internal pure returns(uint256) a) internal returns (uint256) { return a(4); } + function funcTakesInternalView(function(uint256) internal view returns(uint256) a) internal returns (uint256) { return a(4); } + function funcTakesInternalMutable(function(uint256) internal returns(uint256) a) internal returns (uint256) { return a(4); } + + function funcTakesExternalPure(function(uint256) external pure returns(uint256) a) internal returns (uint256) { return a(4); } + function funcTakesExternalView(function(uint256) external view returns(uint256) a) internal returns (uint256) { return a(4); } + function funcTakesExternalPayable(function(uint256) external payable returns(uint256) a) internal returns (uint256) { return a(4); } + function funcTakesExternalMutable(function(uint256) external returns(uint256) a) internal returns (uint256) { return a(4); } + + function tests() internal + { + funcTakesInternalPure(internalViewFunc); // view -> pure should fail + funcTakesInternalPure(internalMutableFunc); // mutable -> pure should fail + + funcTakesInternalView(internalMutableFunc); // mutable -> view should fail + + funcTakesExternalPure(this.externalViewFunc); // view -> pure should fail + funcTakesExternalPure(this.externalPayableFunc); // payable -> pure should fail + funcTakesExternalPure(this.externalMutableFunc); // mutable -> pure should fail + + funcTakesExternalView(this.externalPayableFunc); // payable -> view should fail + funcTakesExternalView(this.externalMutableFunc); // mutable -> view should fail + + funcTakesExternalPayable(this.externalPureFunc); // pure -> payable should fail + funcTakesExternalPayable(this.externalViewFunc); // view -> payable should fail + funcTakesExternalPayable(this.externalMutableFunc); // mutable -> payable should fail + } +} +// ---- +// TypeError: (1580-1596): Invalid type for argument in function call. Invalid implicit conversion from function (uint256) view returns (uint256) to function (uint256) pure returns (uint256) requested. +// TypeError: (1653-1672): Invalid type for argument in function call. Invalid implicit conversion from function (uint256) returns (uint256) to function (uint256) pure returns (uint256) requested. +// TypeError: (1733-1752): Invalid type for argument in function call. Invalid implicit conversion from function (uint256) returns (uint256) to function (uint256) view returns (uint256) requested. +// TypeError: (1813-1834): Invalid type for argument in function call. Invalid implicit conversion from function (uint256) view external returns (uint256) to function (uint256) pure external returns (uint256) requested. +// TypeError: (1891-1915): Invalid type for argument in function call. Invalid implicit conversion from function (uint256) payable external returns (uint256) to function (uint256) pure external returns (uint256) requested. +// TypeError: (1975-1999): Invalid type for argument in function call. Invalid implicit conversion from function (uint256) external returns (uint256) to function (uint256) pure external returns (uint256) requested. +// TypeError: (2060-2084): Invalid type for argument in function call. Invalid implicit conversion from function (uint256) payable external returns (uint256) to function (uint256) view external returns (uint256) requested. +// TypeError: (2144-2168): Invalid type for argument in function call. Invalid implicit conversion from function (uint256) external returns (uint256) to function (uint256) view external returns (uint256) requested. +// TypeError: (2232-2253): Invalid type for argument in function call. Invalid implicit conversion from function (uint256) pure external returns (uint256) to function (uint256) payable external returns (uint256) requested. +// TypeError: (2316-2337): Invalid type for argument in function call. Invalid implicit conversion from function (uint256) view external returns (uint256) to function (uint256) payable external returns (uint256) requested. +// TypeError: (2400-2424): Invalid type for argument in function call. Invalid implicit conversion from function (uint256) external returns (uint256) to function (uint256) payable external returns (uint256) requested. diff --git a/test/libsolidity/syntaxTests/types/function_types/function_state_mutability_success.sol b/test/libsolidity/syntaxTests/types/function_types/function_state_mutability_success.sol new file mode 100644 index 00000000..4ee515fc --- /dev/null +++ b/test/libsolidity/syntaxTests/types/function_types/function_state_mutability_success.sol @@ -0,0 +1,46 @@ +contract Test +{ + uint y; + function internalPureFunc(uint256 x) internal pure returns (uint256) { return x; } + function internalViewFunc(uint256 x) internal view returns (uint256) { return x + y; } + function internalMutableFunc(uint256 x) internal returns (uint256) { y = x; return x; } + + function externalPureFunc(uint256 x) external pure returns (uint256) { return x; } + function externalViewFunc(uint256 x) external view returns (uint256) { return x + y; } + function externalPayableFunc(uint256 x) external payable returns (uint256) { return x + y; } + function externalMutableFunc(uint256 x) external returns (uint256) { y = x; return x; } + + function funcTakesInternalPure(function(uint256) internal pure returns(uint256) a) internal pure returns (uint256) { return a(4); } + function funcTakesInternalView(function(uint256) internal view returns(uint256) a) internal view returns (uint256) { return a(4); } + function funcTakesInternalMutable(function(uint256) internal returns(uint256) a) internal returns (uint256) { return a(4); } + + function funcTakesExternalPure(function(uint256) external pure returns(uint256) a) internal pure returns (uint256) { return a(4); } + function funcTakesExternalView(function(uint256) external view returns(uint256) a) internal view returns (uint256) { return a(4); } + function funcTakesExternalPayable(function(uint256) external payable returns(uint256) a) internal returns (uint256) { return a(4); } + function funcTakesExternalMutable(function(uint256) external returns(uint256) a) internal returns (uint256) { return a(4); } + + function tests() internal + { + funcTakesInternalPure(internalPureFunc); + + funcTakesInternalView(internalPureFunc); + funcTakesInternalView(internalViewFunc); + + funcTakesInternalMutable(internalPureFunc); + funcTakesInternalMutable(internalViewFunc); + funcTakesInternalMutable(internalMutableFunc); + + funcTakesExternalPure(this.externalPureFunc); + + funcTakesExternalView(this.externalPureFunc); + funcTakesExternalView(this.externalViewFunc); + + funcTakesExternalPayable(this.externalPayableFunc); + + funcTakesExternalMutable(this.externalPureFunc); + funcTakesExternalMutable(this.externalViewFunc); + funcTakesExternalMutable(this.externalPayableFunc); + funcTakesExternalMutable(this.externalMutableFunc); + } +} +// ---- diff --git a/test/libsolidity/syntaxTests/types/hex_literal_to_bytesXX_different_size_explicit.sol b/test/libsolidity/syntaxTests/types/hex_literal_to_bytesXX_different_size_explicit.sol new file mode 100644 index 00000000..e1e9850d --- /dev/null +++ b/test/libsolidity/syntaxTests/types/hex_literal_to_bytesXX_different_size_explicit.sol @@ -0,0 +1,31 @@ +contract C { + function f() public pure { + bytes1 b1 = bytes1(0x1); + bytes1 b2 = bytes1(0x100); + bytes2 b3 = bytes2(0xFF); + bytes2 b4 = bytes2(0x100); + bytes2 b5 = bytes2(0x10000); + bytes3 b6 = bytes3(0xFFFF); + bytes3 b7 = bytes3(0x10000); + bytes3 b8 = bytes3(0x1000000); + bytes4 b9 = bytes4(0xFFFFFF); + bytes4 b10 = bytes4(0x1000000); + bytes4 b11 = bytes4(0x100000000); + bytes16 b12 = bytes16(0x1); + bytes32 b13 = bytes32(0x1); + } +} +// ---- +// TypeError: (60-71): Explicit type conversion not allowed from "int_const 1" to "bytes1". +// TypeError: (90-103): Explicit type conversion not allowed from "int_const 256" to "bytes1". +// TypeError: (122-134): Explicit type conversion not allowed from "int_const 255" to "bytes2". +// TypeError: (153-166): Explicit type conversion not allowed from "int_const 256" to "bytes2". +// TypeError: (185-200): Explicit type conversion not allowed from "int_const 65536" to "bytes2". +// TypeError: (219-233): Explicit type conversion not allowed from "int_const 65535" to "bytes3". +// TypeError: (252-267): Explicit type conversion not allowed from "int_const 65536" to "bytes3". +// TypeError: (286-303): Explicit type conversion not allowed from "int_const 16777216" to "bytes3". +// TypeError: (322-338): Explicit type conversion not allowed from "int_const 16777215" to "bytes4". +// TypeError: (358-375): Explicit type conversion not allowed from "int_const 16777216" to "bytes4". +// TypeError: (395-414): Explicit type conversion not allowed from "int_const 4294967296" to "bytes4". +// TypeError: (435-447): Explicit type conversion not allowed from "int_const 1" to "bytes16". +// TypeError: (468-480): Explicit type conversion not allowed from "int_const 1" to "bytes32". diff --git a/test/libsolidity/syntaxTests/types/hex_literal_to_bytesXX_different_size_implicit.sol b/test/libsolidity/syntaxTests/types/hex_literal_to_bytesXX_different_size_implicit.sol new file mode 100644 index 00000000..44ed9318 --- /dev/null +++ b/test/libsolidity/syntaxTests/types/hex_literal_to_bytesXX_different_size_implicit.sol @@ -0,0 +1,31 @@ +contract C { + function f() public pure { + bytes1 b1 = 0x1; + bytes1 b2 = 0x100; + bytes2 b3 = 0xFF; + bytes2 b4 = 0x100; + bytes2 b5 = 0x10000; + bytes3 b6 = 0xFFFF; + bytes3 b7 = 0x10000; + bytes3 b8 = 0x1000000; + bytes4 b9 = 0xFFFFFF; + bytes4 b10 = 0x1000000; + bytes4 b11 = 0x100000000; + bytes16 b12 = 0x1; + bytes32 b13 = 0x1; + } +} +// ---- +// TypeError: (48-63): Type int_const 1 is not implicitly convertible to expected type bytes1. +// TypeError: (70-87): Type int_const 256 is not implicitly convertible to expected type bytes1. +// TypeError: (94-110): Type int_const 255 is not implicitly convertible to expected type bytes2. +// TypeError: (117-134): Type int_const 256 is not implicitly convertible to expected type bytes2. +// TypeError: (141-160): Type int_const 65536 is not implicitly convertible to expected type bytes2. +// TypeError: (167-185): Type int_const 65535 is not implicitly convertible to expected type bytes3. +// TypeError: (192-211): Type int_const 65536 is not implicitly convertible to expected type bytes3. +// TypeError: (218-239): Type int_const 16777216 is not implicitly convertible to expected type bytes3. +// TypeError: (246-266): Type int_const 16777215 is not implicitly convertible to expected type bytes4. +// TypeError: (273-295): Type int_const 16777216 is not implicitly convertible to expected type bytes4. +// TypeError: (302-326): Type int_const 4294967296 is not implicitly convertible to expected type bytes4. +// TypeError: (333-350): Type int_const 1 is not implicitly convertible to expected type bytes16. +// TypeError: (357-374): Type int_const 1 is not implicitly convertible to expected type bytes32. diff --git a/test/libsolidity/syntaxTests/types/hex_literal_to_bytesXX_same_size_explicit.sol b/test/libsolidity/syntaxTests/types/hex_literal_to_bytesXX_same_size_explicit.sol new file mode 100644 index 00000000..4e18640c --- /dev/null +++ b/test/libsolidity/syntaxTests/types/hex_literal_to_bytesXX_same_size_explicit.sol @@ -0,0 +1,13 @@ +contract C { + function f() public pure { + bytes1 b1 = bytes1(0x01); + bytes1 b2 = bytes1(0xFF); + bytes2 b3 = bytes2(0x0100); + bytes2 b4 = bytes2(0xFFFF); + bytes3 b5 = bytes3(0x010000); + bytes3 b6 = bytes3(0xFFFFFF); + bytes4 b7 = bytes4(0x01000000); + bytes4 b8 = bytes4(0xFFFFFFFF); + b1; b2; b3; b4; b5; b6; b7; b8; + } +} diff --git a/test/libsolidity/syntaxTests/types/hex_literal_to_bytesXX_same_size_implicit.sol b/test/libsolidity/syntaxTests/types/hex_literal_to_bytesXX_same_size_implicit.sol new file mode 100644 index 00000000..daf0bf56 --- /dev/null +++ b/test/libsolidity/syntaxTests/types/hex_literal_to_bytesXX_same_size_implicit.sol @@ -0,0 +1,13 @@ +contract C { + function f() public pure { + bytes1 b1 = 0x01; + bytes1 b2 = 0xFF; + bytes2 b3 = 0x0100; + bytes2 b4 = 0xFFFF; + bytes3 b5 = 0x010000; + bytes3 b6 = 0xFFFFFF; + bytes4 b7 = 0x01000000; + bytes4 b8 = 0xFFFFFFFF; + b1; b2; b3; b4; b5; b6; b7; b8; + } +} diff --git a/test/libsolidity/syntaxTests/types/index_access_for_bytes.sol b/test/libsolidity/syntaxTests/types/index_access_for_bytes.sol new file mode 100644 index 00000000..f31b4cc0 --- /dev/null +++ b/test/libsolidity/syntaxTests/types/index_access_for_bytes.sol @@ -0,0 +1,6 @@ +contract C { + bytes20 x; + function f(bytes16 b) public view { + b[uint8(x[2])]; + } +} diff --git a/test/libsolidity/syntaxTests/types/mapping/argument_external.sol b/test/libsolidity/syntaxTests/types/mapping/argument_external.sol new file mode 100644 index 00000000..2b5e6b05 --- /dev/null +++ b/test/libsolidity/syntaxTests/types/mapping/argument_external.sol @@ -0,0 +1,6 @@ +contract C { + function f(mapping(uint => uint) storage) external pure { + } +} +// ---- +// TypeError: (28-57): Data location must be "calldata" for parameter in external function, but "storage" was given. diff --git a/test/libsolidity/syntaxTests/types/mapping/argument_internal.sol b/test/libsolidity/syntaxTests/types/mapping/argument_internal.sol new file mode 100644 index 00000000..3c021515 --- /dev/null +++ b/test/libsolidity/syntaxTests/types/mapping/argument_internal.sol @@ -0,0 +1,5 @@ +contract C { + function f(mapping(uint => uint) storage) internal pure { + } +} +// ---- diff --git a/test/libsolidity/syntaxTests/types/mapping/argument_private.sol b/test/libsolidity/syntaxTests/types/mapping/argument_private.sol new file mode 100644 index 00000000..63733d71 --- /dev/null +++ b/test/libsolidity/syntaxTests/types/mapping/argument_private.sol @@ -0,0 +1,5 @@ +contract C { + function f(mapping(uint => uint) storage) private pure { + } +} +// ---- diff --git a/test/libsolidity/syntaxTests/types/mapping/argument_public.sol b/test/libsolidity/syntaxTests/types/mapping/argument_public.sol new file mode 100644 index 00000000..32f11fe9 --- /dev/null +++ b/test/libsolidity/syntaxTests/types/mapping/argument_public.sol @@ -0,0 +1,6 @@ +contract C { + function f(mapping(uint => uint) storage) public pure { + } +} +// ---- +// TypeError: (28-57): Data location must be "memory" for parameter in function, but "storage" was given. diff --git a/test/libsolidity/syntaxTests/types/mapping/array_argument_external.sol b/test/libsolidity/syntaxTests/types/mapping/array_argument_external.sol new file mode 100644 index 00000000..0863653c --- /dev/null +++ b/test/libsolidity/syntaxTests/types/mapping/array_argument_external.sol @@ -0,0 +1,6 @@ +contract C { + function f(mapping(uint => uint)[] storage) external pure { + } +} +// ---- +// TypeError: (28-59): Data location must be "calldata" for parameter in external function, but "storage" was given. diff --git a/test/libsolidity/syntaxTests/types/mapping/array_argument_internal.sol b/test/libsolidity/syntaxTests/types/mapping/array_argument_internal.sol new file mode 100644 index 00000000..352d0982 --- /dev/null +++ b/test/libsolidity/syntaxTests/types/mapping/array_argument_internal.sol @@ -0,0 +1,5 @@ +contract C { + function f(mapping(uint => uint)[] storage) internal pure { + } +} +// ---- diff --git a/test/libsolidity/syntaxTests/types/mapping/array_argument_private.sol b/test/libsolidity/syntaxTests/types/mapping/array_argument_private.sol new file mode 100644 index 00000000..332dbe6c --- /dev/null +++ b/test/libsolidity/syntaxTests/types/mapping/array_argument_private.sol @@ -0,0 +1,5 @@ +contract C { + function f(mapping(uint => uint)[] storage) private pure { + } +} +// ---- diff --git a/test/libsolidity/syntaxTests/types/mapping/array_argument_public.sol b/test/libsolidity/syntaxTests/types/mapping/array_argument_public.sol new file mode 100644 index 00000000..99c83d8a --- /dev/null +++ b/test/libsolidity/syntaxTests/types/mapping/array_argument_public.sol @@ -0,0 +1,6 @@ +contract C { + function f(mapping(uint => uint)[] storage) public pure { + } +} +// ---- +// TypeError: (28-59): Data location must be "memory" for parameter in function, but "storage" was given. diff --git a/test/libsolidity/syntaxTests/types/mapping/assignment_local.sol b/test/libsolidity/syntaxTests/types/mapping/assignment_local.sol new file mode 100644 index 00000000..a329c91e --- /dev/null +++ b/test/libsolidity/syntaxTests/types/mapping/assignment_local.sol @@ -0,0 +1,11 @@ +contract test { + mapping(uint=>uint) map; + function fun() public view { + mapping(uint=>uint) storage a = map; + mapping(uint=>uint) storage b = map; + b = a; + (b) = a; + (b, b) = (a, a); + } +} +// ---- diff --git a/test/libsolidity/syntaxTests/types/mapping/assignment_state_variable.sol b/test/libsolidity/syntaxTests/types/mapping/assignment_state_variable.sol new file mode 100644 index 00000000..1323afe6 --- /dev/null +++ b/test/libsolidity/syntaxTests/types/mapping/assignment_state_variable.sol @@ -0,0 +1,14 @@ +contract test { + mapping(uint=>uint) map; + function fun() public { + mapping(uint=>uint) storage a = map; + map = a; + (map) = a; + (map, map) = (a, a); + } +} +// ---- +// TypeError: (126-129): Mappings cannot be assigned to. +// TypeError: (144-147): Mappings cannot be assigned to. +// TypeError: (163-166): Mappings cannot be assigned to. +// TypeError: (168-171): Mappings cannot be assigned to. diff --git a/test/libsolidity/syntaxTests/types/mapping/assignment_struct.sol b/test/libsolidity/syntaxTests/types/mapping/assignment_struct.sol new file mode 100644 index 00000000..b89241ed --- /dev/null +++ b/test/libsolidity/syntaxTests/types/mapping/assignment_struct.sol @@ -0,0 +1,17 @@ +contract test { + struct str { + mapping(uint=>uint) map; + } + str data; + function fun() public { + mapping(uint=>uint) storage a = data.map; + data.map = a; + (data.map) = a; + (data.map, data.map) = (a, a); + } +} +// ---- +// TypeError: (172-180): Mappings cannot be assigned to. +// TypeError: (195-203): Mappings cannot be assigned to. +// TypeError: (219-227): Mappings cannot be assigned to. +// TypeError: (229-237): Mappings cannot be assigned to. diff --git a/test/libsolidity/syntaxTests/types/mapping/function_type_argument_external.sol b/test/libsolidity/syntaxTests/types/mapping/function_type_argument_external.sol new file mode 100644 index 00000000..34f95701 --- /dev/null +++ b/test/libsolidity/syntaxTests/types/mapping/function_type_argument_external.sol @@ -0,0 +1,7 @@ +contract C { + function f(function(mapping(uint=>uint) storage) external) public pure { + } +} +// ---- +// TypeError: (37-64): Data location must be "memory" for parameter in function, but "storage" was given. +// TypeError: (37-64): Internal type cannot be used for external function type. diff --git a/test/libsolidity/syntaxTests/types/mapping/function_type_argument_internal.sol b/test/libsolidity/syntaxTests/types/mapping/function_type_argument_internal.sol new file mode 100644 index 00000000..01e2322e --- /dev/null +++ b/test/libsolidity/syntaxTests/types/mapping/function_type_argument_internal.sol @@ -0,0 +1,4 @@ +contract C { + function f(function(mapping(uint=>uint) storage) internal) internal pure { + } +} diff --git a/test/libsolidity/syntaxTests/types/mapping/function_type_return_external.sol b/test/libsolidity/syntaxTests/types/mapping/function_type_return_external.sol new file mode 100644 index 00000000..aed9b387 --- /dev/null +++ b/test/libsolidity/syntaxTests/types/mapping/function_type_return_external.sol @@ -0,0 +1,7 @@ +contract C { + function f(function() external returns (mapping(uint=>uint) storage)) public pure { + } +} +// ---- +// TypeError: (57-84): Data location must be "memory" for return parameter in function, but "storage" was given. +// TypeError: (57-84): Internal type cannot be used for external function type. diff --git a/test/libsolidity/syntaxTests/types/mapping/function_type_return_internal.sol b/test/libsolidity/syntaxTests/types/mapping/function_type_return_internal.sol new file mode 100644 index 00000000..bd298e5d --- /dev/null +++ b/test/libsolidity/syntaxTests/types/mapping/function_type_return_internal.sol @@ -0,0 +1,4 @@ +contract C { + function f(function() internal returns (mapping(uint=>uint) storage)) internal pure { + } +} diff --git a/test/libsolidity/syntaxTests/types/mapping/library_argument_external.sol b/test/libsolidity/syntaxTests/types/mapping/library_argument_external.sol new file mode 100644 index 00000000..1098008d --- /dev/null +++ b/test/libsolidity/syntaxTests/types/mapping/library_argument_external.sol @@ -0,0 +1,6 @@ +library L { + function f(mapping(uint => uint) storage) external pure { + } +} +// ---- +// TypeError: (27-56): Type is required to live outside storage. diff --git a/test/libsolidity/syntaxTests/types/mapping/library_argument_internal.sol b/test/libsolidity/syntaxTests/types/mapping/library_argument_internal.sol new file mode 100644 index 00000000..1228b6b6 --- /dev/null +++ b/test/libsolidity/syntaxTests/types/mapping/library_argument_internal.sol @@ -0,0 +1,4 @@ +library L { + function f(mapping(uint => uint) storage) internal pure { + } +} diff --git a/test/libsolidity/syntaxTests/types/mapping/library_argument_private.sol b/test/libsolidity/syntaxTests/types/mapping/library_argument_private.sol new file mode 100644 index 00000000..5eaff16b --- /dev/null +++ b/test/libsolidity/syntaxTests/types/mapping/library_argument_private.sol @@ -0,0 +1,4 @@ +library L { + function f(mapping(uint => uint) storage) private pure { + } +} diff --git a/test/libsolidity/syntaxTests/types/mapping/library_argument_public.sol b/test/libsolidity/syntaxTests/types/mapping/library_argument_public.sol new file mode 100644 index 00000000..dedd4f68 --- /dev/null +++ b/test/libsolidity/syntaxTests/types/mapping/library_argument_public.sol @@ -0,0 +1,6 @@ +library L { + function f(mapping(uint => uint) storage) public pure { + } +} +// ---- +// TypeError: (27-56): Type is required to live outside storage. diff --git a/test/libsolidity/syntaxTests/types/mapping/library_array_argument_external.sol b/test/libsolidity/syntaxTests/types/mapping/library_array_argument_external.sol new file mode 100644 index 00000000..da5a911b --- /dev/null +++ b/test/libsolidity/syntaxTests/types/mapping/library_array_argument_external.sol @@ -0,0 +1,6 @@ +library L { + function f(mapping(uint => uint)[] storage) external pure { + } +} +// ---- +// TypeError: (27-58): Type is required to live outside storage. diff --git a/test/libsolidity/syntaxTests/types/mapping/library_array_argument_internal.sol b/test/libsolidity/syntaxTests/types/mapping/library_array_argument_internal.sol new file mode 100644 index 00000000..55c1cea0 --- /dev/null +++ b/test/libsolidity/syntaxTests/types/mapping/library_array_argument_internal.sol @@ -0,0 +1,4 @@ +library L { + function f(mapping(uint => uint)[] storage) internal pure { + } +} diff --git a/test/libsolidity/syntaxTests/types/mapping/library_array_argument_private.sol b/test/libsolidity/syntaxTests/types/mapping/library_array_argument_private.sol new file mode 100644 index 00000000..d37d6504 --- /dev/null +++ b/test/libsolidity/syntaxTests/types/mapping/library_array_argument_private.sol @@ -0,0 +1,4 @@ +library L { + function f(mapping(uint => uint)[] storage) private pure { + } +} diff --git a/test/libsolidity/syntaxTests/types/mapping/library_array_argument_public.sol b/test/libsolidity/syntaxTests/types/mapping/library_array_argument_public.sol new file mode 100644 index 00000000..adb62203 --- /dev/null +++ b/test/libsolidity/syntaxTests/types/mapping/library_array_argument_public.sol @@ -0,0 +1,6 @@ +library L { + function f(mapping(uint => uint)[] storage) public pure { + } +} +// ---- +// TypeError: (27-58): Type is required to live outside storage. diff --git a/test/libsolidity/syntaxTests/types/mapping/library_return_external.sol b/test/libsolidity/syntaxTests/types/mapping/library_return_external.sol new file mode 100644 index 00000000..1e756819 --- /dev/null +++ b/test/libsolidity/syntaxTests/types/mapping/library_return_external.sol @@ -0,0 +1,10 @@ +library L +{ + function f(mapping(uint => uint) storage a, mapping(uint => uint) storage b, bool c) external pure returns(mapping(uint => uint) storage) { + return c ? a : b; + } +} +// ---- +// TypeError: (27-58): Type is required to live outside storage. +// TypeError: (60-91): Type is required to live outside storage. +// TypeError: (123-152): Type is required to live outside storage. diff --git a/test/libsolidity/syntaxTests/types/mapping/library_return_internal.sol b/test/libsolidity/syntaxTests/types/mapping/library_return_internal.sol new file mode 100644 index 00000000..d0fca6bf --- /dev/null +++ b/test/libsolidity/syntaxTests/types/mapping/library_return_internal.sol @@ -0,0 +1,6 @@ +library L +{ + function f(mapping(uint => uint) storage a, mapping(uint => uint) storage b, bool c) internal pure returns(mapping(uint => uint) storage) { + return c ? a : b; + } +} diff --git a/test/libsolidity/syntaxTests/types/mapping/library_return_private.sol b/test/libsolidity/syntaxTests/types/mapping/library_return_private.sol new file mode 100644 index 00000000..13c2000f --- /dev/null +++ b/test/libsolidity/syntaxTests/types/mapping/library_return_private.sol @@ -0,0 +1,6 @@ +library L +{ + function f(mapping(uint => uint) storage a, mapping(uint => uint) storage b, bool c) private pure returns(mapping(uint => uint) storage) { + return c ? a : b; + } +} diff --git a/test/libsolidity/syntaxTests/types/mapping/library_return_public.sol b/test/libsolidity/syntaxTests/types/mapping/library_return_public.sol new file mode 100644 index 00000000..357751a0 --- /dev/null +++ b/test/libsolidity/syntaxTests/types/mapping/library_return_public.sol @@ -0,0 +1,10 @@ +library L +{ + function f(mapping(uint => uint) storage a, mapping(uint => uint) storage b, bool c) public pure returns(mapping(uint => uint) storage) { + return c ? a : b; + } +} +// ---- +// TypeError: (27-58): Type is required to live outside storage. +// TypeError: (60-91): Type is required to live outside storage. +// TypeError: (121-150): Type is required to live outside storage. diff --git a/test/libsolidity/syntaxTests/types/mapping/mapping_array_data_location_function_param_external.sol b/test/libsolidity/syntaxTests/types/mapping/mapping_array_data_location_function_param_external.sol new file mode 100644 index 00000000..0c29ebd8 --- /dev/null +++ b/test/libsolidity/syntaxTests/types/mapping/mapping_array_data_location_function_param_external.sol @@ -0,0 +1,6 @@ +contract c { + function f1(mapping(uint => uint)[] calldata) pure external {} +} +// ---- +// TypeError: (29-61): Type is required to live outside storage. +// TypeError: (29-61): Internal or recursive type is not allowed for public or external functions. diff --git a/test/libsolidity/syntaxTests/types/mapping/mapping_array_return_external.sol b/test/libsolidity/syntaxTests/types/mapping/mapping_array_return_external.sol new file mode 100644 index 00000000..fe021bd0 --- /dev/null +++ b/test/libsolidity/syntaxTests/types/mapping/mapping_array_return_external.sol @@ -0,0 +1,6 @@ +contract C { + function f() external pure returns (mapping(uint=>uint)[] storage m) { + } +} +// ---- +// TypeError: (53-84): Data location must be "memory" for return parameter in function, but "storage" was given. diff --git a/test/libsolidity/syntaxTests/types/mapping/mapping_array_return_internal.sol b/test/libsolidity/syntaxTests/types/mapping/mapping_array_return_internal.sol new file mode 100644 index 00000000..8837c745 --- /dev/null +++ b/test/libsolidity/syntaxTests/types/mapping/mapping_array_return_internal.sol @@ -0,0 +1,16 @@ +contract C { + mapping(uint=>uint)[] m; + function f() internal view returns (mapping(uint=>uint)[] storage) { + return m; + } + function g() private view returns (mapping(uint=>uint)[] storage) { + return m; + } + function h() internal view returns (mapping(uint=>uint)[] storage r) { + r = m; + } + function i() private view returns (mapping(uint=>uint)[] storage r) { + (r,r) = (m,m); + } +} +// ---- diff --git a/test/libsolidity/syntaxTests/types/mapping/mapping_array_return_public.sol b/test/libsolidity/syntaxTests/types/mapping/mapping_array_return_public.sol new file mode 100644 index 00000000..1eb9d03b --- /dev/null +++ b/test/libsolidity/syntaxTests/types/mapping/mapping_array_return_public.sol @@ -0,0 +1,6 @@ +contract C { + function f() public pure returns (mapping(uint=>uint)[] storage m) { + } +} +// ---- +// TypeError: (51-82): Data location must be "memory" for return parameter in function, but "storage" was given. diff --git a/test/libsolidity/syntaxTests/types/mapping/mapping_data_location_calldata.sol b/test/libsolidity/syntaxTests/types/mapping/mapping_data_location_calldata.sol new file mode 100644 index 00000000..deff7c14 --- /dev/null +++ b/test/libsolidity/syntaxTests/types/mapping/mapping_data_location_calldata.sol @@ -0,0 +1,9 @@ +contract c { + mapping(uint => uint) y; + function f() view public { + mapping(uint => uint) calldata x = y; + x; + } +} +// ---- +// TypeError: (81-113): Data location must be "storage" for variable, but "calldata" was given. diff --git a/test/libsolidity/syntaxTests/types/mapping/mapping_data_location_default.sol b/test/libsolidity/syntaxTests/types/mapping/mapping_data_location_default.sol new file mode 100644 index 00000000..e5253f00 --- /dev/null +++ b/test/libsolidity/syntaxTests/types/mapping/mapping_data_location_default.sol @@ -0,0 +1,9 @@ +contract c { + mapping(uint => uint) y; + function f() view public { + mapping(uint => uint) x = y; + x; + } +} +// ---- +// TypeError: (81-104): Data location must be "storage" for variable, but none was given. diff --git a/test/libsolidity/syntaxTests/types/mapping/mapping_data_location_function_param_external.sol b/test/libsolidity/syntaxTests/types/mapping/mapping_data_location_function_param_external.sol new file mode 100644 index 00000000..c050f8e9 --- /dev/null +++ b/test/libsolidity/syntaxTests/types/mapping/mapping_data_location_function_param_external.sol @@ -0,0 +1,6 @@ +contract c { + function f1(mapping(uint => uint) calldata) pure external returns (mapping(uint => uint) memory) {} +} +// ---- +// TypeError: (29-59): Type is required to live outside storage. +// TypeError: (29-59): Internal or recursive type is not allowed for public or external functions. diff --git a/test/libsolidity/syntaxTests/types/mapping/mapping_data_location_function_param_internal.sol b/test/libsolidity/syntaxTests/types/mapping/mapping_data_location_function_param_internal.sol new file mode 100644 index 00000000..17f2f712 --- /dev/null +++ b/test/libsolidity/syntaxTests/types/mapping/mapping_data_location_function_param_internal.sol @@ -0,0 +1,4 @@ +contract c { + function f4(mapping(uint => uint) memory) pure internal {} +} +// ---- diff --git a/test/libsolidity/syntaxTests/types/mapping/mapping_data_location_function_param_public.sol b/test/libsolidity/syntaxTests/types/mapping/mapping_data_location_function_param_public.sol new file mode 100644 index 00000000..b63868b8 --- /dev/null +++ b/test/libsolidity/syntaxTests/types/mapping/mapping_data_location_function_param_public.sol @@ -0,0 +1,6 @@ +contract c { + function f3(mapping(uint => uint) memory) view public {} +} +// ---- +// TypeError: (29-57): Type is required to live outside storage. +// TypeError: (29-57): Internal or recursive type is not allowed for public or external functions. diff --git a/test/libsolidity/syntaxTests/types/mapping/mapping_data_location_memory.sol b/test/libsolidity/syntaxTests/types/mapping/mapping_data_location_memory.sol new file mode 100644 index 00000000..600ae669 --- /dev/null +++ b/test/libsolidity/syntaxTests/types/mapping/mapping_data_location_memory.sol @@ -0,0 +1,9 @@ +contract c { + mapping(uint => uint) y; + function f() view public { + mapping(uint => uint) memory x = y; + x; + } +} +// ---- +// TypeError: (81-111): Data location must be "storage" for variable, but "memory" was given. diff --git a/test/libsolidity/syntaxTests/types/mapping/mapping_dynamic_key.sol b/test/libsolidity/syntaxTests/types/mapping/mapping_dynamic_key.sol new file mode 100644 index 00000000..825ee09a --- /dev/null +++ b/test/libsolidity/syntaxTests/types/mapping/mapping_dynamic_key.sol @@ -0,0 +1,3 @@ +contract c { + mapping(string => uint) data; +} diff --git a/test/libsolidity/syntaxTests/types/mapping/mapping_dynamic_key_public.sol b/test/libsolidity/syntaxTests/types/mapping/mapping_dynamic_key_public.sol new file mode 100644 index 00000000..9fb575af --- /dev/null +++ b/test/libsolidity/syntaxTests/types/mapping/mapping_dynamic_key_public.sol @@ -0,0 +1,5 @@ +contract c { + mapping(string => uint) public data; +} +// ---- +// TypeError: (14-49): Dynamically-sized keys for public mappings are not supported. diff --git a/test/libsolidity/syntaxTests/types/mapping/mapping_return_external.sol b/test/libsolidity/syntaxTests/types/mapping/mapping_return_external.sol new file mode 100644 index 00000000..17e646ce --- /dev/null +++ b/test/libsolidity/syntaxTests/types/mapping/mapping_return_external.sol @@ -0,0 +1,6 @@ +contract C { + function f() external pure returns (mapping(uint=>uint) storage m) { + } +} +// ---- +// TypeError: (53-82): Data location must be "memory" for return parameter in function, but "storage" was given. diff --git a/test/libsolidity/syntaxTests/types/mapping/mapping_return_internal.sol b/test/libsolidity/syntaxTests/types/mapping/mapping_return_internal.sol new file mode 100644 index 00000000..4912836e --- /dev/null +++ b/test/libsolidity/syntaxTests/types/mapping/mapping_return_internal.sol @@ -0,0 +1,16 @@ +contract C { + mapping(uint=>uint) m; + function f() internal view returns (mapping(uint=>uint) storage) { + return m; + } + function g() private view returns (mapping(uint=>uint) storage) { + return m; + } + function h() internal view returns (mapping(uint=>uint) storage r) { + r = m; + } + function i() private view returns (mapping(uint=>uint) storage r) { + (r,r) = (m,m); + } +} +// ---- diff --git a/test/libsolidity/syntaxTests/types/mapping/mapping_return_public.sol b/test/libsolidity/syntaxTests/types/mapping/mapping_return_public.sol new file mode 100644 index 00000000..cf5ec4ff --- /dev/null +++ b/test/libsolidity/syntaxTests/types/mapping/mapping_return_public.sol @@ -0,0 +1,6 @@ +contract C { + function f() public pure returns (mapping(uint=>uint) storage m) { + } +} +// ---- +// TypeError: (51-80): Data location must be "memory" for return parameter in function, but "storage" was given. diff --git a/test/libsolidity/syntaxTests/types/mapping/mapping_return_public_memory.sol b/test/libsolidity/syntaxTests/types/mapping/mapping_return_public_memory.sol new file mode 100644 index 00000000..35c3abc9 --- /dev/null +++ b/test/libsolidity/syntaxTests/types/mapping/mapping_return_public_memory.sol @@ -0,0 +1,7 @@ +contract C { + function f() public pure returns (mapping(uint=>uint) memory m) { + } +} +// ---- +// TypeError: (51-79): Type is required to live outside storage. +// TypeError: (51-79): Internal or recursive type is not allowed for public or external functions. diff --git a/test/libsolidity/syntaxTests/types/no_singleton_tuple.sol b/test/libsolidity/syntaxTests/types/no_singleton_tuple.sol new file mode 100644 index 00000000..62a58f83 --- /dev/null +++ b/test/libsolidity/syntaxTests/types/no_singleton_tuple.sol @@ -0,0 +1,8 @@ +contract C { + function f() public pure { + uint a; + (a,) = (uint(1),); + } +} +// ---- +// TypeError: (60-70): Tuple component cannot be empty. diff --git a/test/libsolidity/syntaxTests/types/rational_negative_numerator_negative_exp.sol b/test/libsolidity/syntaxTests/types/rational_negative_numerator_negative_exp.sol new file mode 100644 index 00000000..b694992c --- /dev/null +++ b/test/libsolidity/syntaxTests/types/rational_negative_numerator_negative_exp.sol @@ -0,0 +1,5 @@ +contract C { + function f() public pure returns (int) { + return (-1 / 2) ** -1; + } +} diff --git a/test/libsolidity/syntaxTests/types/rational_number_exp_limit.sol b/test/libsolidity/syntaxTests/types/rational_number_exp_limit.sol deleted file mode 100644 index 6785f580..00000000 --- a/test/libsolidity/syntaxTests/types/rational_number_exp_limit.sol +++ /dev/null @@ -1,50 +0,0 @@ -contract c { - function f() public pure { - int a; - a = 4 ** 4 ** 2 ** 4 ** 4 ** 4 ** 4; - a = -4 ** 4 ** 2 ** 4 ** 4 ** 4 ** 4 ** 4; - a = 4 ** (-(2 ** 4 ** 4 ** 4 ** 4 ** 4)); - a = 0 ** 1E1233; // fine - a = 1 ** 1E1233; // fine - a = -1 ** 1E1233; // fine - a = 2 ** 1E1233; - a = -2 ** 1E1233; - a = 2 ** -1E1233; - a = -2 ** -1E1233; - a = 1E1233 ** 2; - a = -1E1233 ** 2; - a = 1E1233 ** -2; - a = -1E1233 ** -2; - a = 1E1233 ** 1E1233; - a = 1E1233 ** -1E1233; - a = -1E1233 ** 1E1233; - a = -1E1233 ** -1E1233; - } -} -// ---- -// TypeError: (71-102): Operator ** not compatible with types int_const 1797...(301 digits omitted)...7216 and int_const 4 -// TypeError: (71-102): Type int_const 1797...(301 digits omitted)...7216 is not implicitly convertible to expected type int256. -// TypeError: (116-148): Operator ** not compatible with types int_const 1797...(301 digits omitted)...7216 and int_const 4 -// TypeError: (116-153): Operator ** not compatible with types int_const 1797...(301 digits omitted)...7216 and int_const 4 -// TypeError: (116-153): Type int_const 1797...(301 digits omitted)...7216 is not implicitly convertible to expected type int256. -// TypeError: (167-203): Operator ** not compatible with types int_const 4 and int_const -179...(302 digits omitted)...7216 -// TypeError: (317-328): Operator ** not compatible with types int_const 2 and int_const 1000...(1226 digits omitted)...0000 -// TypeError: (342-354): Operator ** not compatible with types int_const -2 and int_const 1000...(1226 digits omitted)...0000 -// TypeError: (368-380): Operator ** not compatible with types int_const 2 and int_const -100...(1227 digits omitted)...0000 -// TypeError: (394-407): Operator ** not compatible with types int_const -2 and int_const -100...(1227 digits omitted)...0000 -// TypeError: (421-432): Operator ** not compatible with types int_const 1000...(1226 digits omitted)...0000 and int_const 2 -// TypeError: (421-432): Type int_const 1000...(1226 digits omitted)...0000 is not implicitly convertible to expected type int256. -// TypeError: (446-458): Operator ** not compatible with types int_const -100...(1227 digits omitted)...0000 and int_const 2 -// TypeError: (446-458): Type int_const -100...(1227 digits omitted)...0000 is not implicitly convertible to expected type int256. -// TypeError: (472-484): Operator ** not compatible with types int_const 1000...(1226 digits omitted)...0000 and int_const -2 -// TypeError: (472-484): Type int_const 1000...(1226 digits omitted)...0000 is not implicitly convertible to expected type int256. -// TypeError: (498-511): Operator ** not compatible with types int_const -100...(1227 digits omitted)...0000 and int_const -2 -// TypeError: (498-511): Type int_const -100...(1227 digits omitted)...0000 is not implicitly convertible to expected type int256. -// TypeError: (525-541): Operator ** not compatible with types int_const 1000...(1226 digits omitted)...0000 and int_const 1000...(1226 digits omitted)...0000 -// TypeError: (525-541): Type int_const 1000...(1226 digits omitted)...0000 is not implicitly convertible to expected type int256. -// TypeError: (555-572): Operator ** not compatible with types int_const 1000...(1226 digits omitted)...0000 and int_const -100...(1227 digits omitted)...0000 -// TypeError: (555-572): Type int_const 1000...(1226 digits omitted)...0000 is not implicitly convertible to expected type int256. -// TypeError: (586-603): Operator ** not compatible with types int_const -100...(1227 digits omitted)...0000 and int_const 1000...(1226 digits omitted)...0000 -// TypeError: (586-603): Type int_const -100...(1227 digits omitted)...0000 is not implicitly convertible to expected type int256. -// TypeError: (617-635): Operator ** not compatible with types int_const -100...(1227 digits omitted)...0000 and int_const -100...(1227 digits omitted)...0000 -// TypeError: (617-635): Type int_const -100...(1227 digits omitted)...0000 is not implicitly convertible to expected type int256. diff --git a/test/libsolidity/syntaxTests/types/rational_number_exp_limit_fail.sol b/test/libsolidity/syntaxTests/types/rational_number_exp_limit_fail.sol new file mode 100644 index 00000000..058db2e9 --- /dev/null +++ b/test/libsolidity/syntaxTests/types/rational_number_exp_limit_fail.sol @@ -0,0 +1,47 @@ +contract c { + function f() public pure { + int a; + a = 4 ** 4 ** 2 ** 4 ** 4 ** 4 ** 4; + a = -4 ** 4 ** 2 ** 4 ** 4 ** 4 ** 4 ** 4; + a = 4 ** (-(2 ** 4 ** 4 ** 4 ** 4 ** 4)); + a = 2 ** 1E1233; + a = -2 ** 1E1233; + a = 2 ** -1E1233; + a = -2 ** -1E1233; + a = 1E1233 ** 2; + a = -1E1233 ** 2; + a = 1E1233 ** -2; + a = -1E1233 ** -2; + a = 1E1233 ** 1E1233; + a = 1E1233 ** -1E1233; + a = -1E1233 ** 1E1233; + a = -1E1233 ** -1E1233; + } +} +// ---- +// TypeError: (71-102): Operator ** not compatible with types int_const 1797...(301 digits omitted)...7216 and int_const 4 +// TypeError: (71-102): Type int_const 1797...(301 digits omitted)...7216 is not implicitly convertible to expected type int256. +// TypeError: (116-148): Operator ** not compatible with types int_const 1797...(301 digits omitted)...7216 and int_const 4 +// TypeError: (116-153): Operator ** not compatible with types int_const 1797...(301 digits omitted)...7216 and int_const 4 +// TypeError: (116-153): Type int_const 1797...(301 digits omitted)...7216 is not implicitly convertible to expected type int256. +// TypeError: (167-203): Operator ** not compatible with types int_const 4 and int_const -179...(302 digits omitted)...7216 +// TypeError: (217-228): Operator ** not compatible with types int_const 2 and int_const 1000...(1226 digits omitted)...0000 +// TypeError: (242-254): Operator ** not compatible with types int_const -2 and int_const 1000...(1226 digits omitted)...0000 +// TypeError: (268-280): Operator ** not compatible with types int_const 2 and int_const -100...(1227 digits omitted)...0000 +// TypeError: (294-307): Operator ** not compatible with types int_const -2 and int_const -100...(1227 digits omitted)...0000 +// TypeError: (321-332): Operator ** not compatible with types int_const 1000...(1226 digits omitted)...0000 and int_const 2 +// TypeError: (321-332): Type int_const 1000...(1226 digits omitted)...0000 is not implicitly convertible to expected type int256. +// TypeError: (346-358): Operator ** not compatible with types int_const -100...(1227 digits omitted)...0000 and int_const 2 +// TypeError: (346-358): Type int_const -100...(1227 digits omitted)...0000 is not implicitly convertible to expected type int256. +// TypeError: (372-384): Operator ** not compatible with types int_const 1000...(1226 digits omitted)...0000 and int_const -2 +// TypeError: (372-384): Type int_const 1000...(1226 digits omitted)...0000 is not implicitly convertible to expected type int256. +// TypeError: (398-411): Operator ** not compatible with types int_const -100...(1227 digits omitted)...0000 and int_const -2 +// TypeError: (398-411): Type int_const -100...(1227 digits omitted)...0000 is not implicitly convertible to expected type int256. +// TypeError: (425-441): Operator ** not compatible with types int_const 1000...(1226 digits omitted)...0000 and int_const 1000...(1226 digits omitted)...0000 +// TypeError: (425-441): Type int_const 1000...(1226 digits omitted)...0000 is not implicitly convertible to expected type int256. +// TypeError: (455-472): Operator ** not compatible with types int_const 1000...(1226 digits omitted)...0000 and int_const -100...(1227 digits omitted)...0000 +// TypeError: (455-472): Type int_const 1000...(1226 digits omitted)...0000 is not implicitly convertible to expected type int256. +// TypeError: (486-503): Operator ** not compatible with types int_const -100...(1227 digits omitted)...0000 and int_const 1000...(1226 digits omitted)...0000 +// TypeError: (486-503): Type int_const -100...(1227 digits omitted)...0000 is not implicitly convertible to expected type int256. +// TypeError: (517-535): Operator ** not compatible with types int_const -100...(1227 digits omitted)...0000 and int_const -100...(1227 digits omitted)...0000 +// TypeError: (517-535): Type int_const -100...(1227 digits omitted)...0000 is not implicitly convertible to expected type int256. diff --git a/test/libsolidity/syntaxTests/types/rational_number_exp_limit_fine.sol b/test/libsolidity/syntaxTests/types/rational_number_exp_limit_fine.sol new file mode 100644 index 00000000..66d02eb9 --- /dev/null +++ b/test/libsolidity/syntaxTests/types/rational_number_exp_limit_fine.sol @@ -0,0 +1,9 @@ +contract c { + function f() public pure { + int a; + a = 0 ** 1E1233; + a = 1 ** 1E1233; + a = -1 ** 1E1233; + a = 0E123456789; + } +} diff --git a/test/libsolidity/syntaxTests/types/rational_number_huge.sol b/test/libsolidity/syntaxTests/types/rational_number_huge.sol new file mode 100644 index 00000000..378de201 --- /dev/null +++ b/test/libsolidity/syntaxTests/types/rational_number_huge.sol @@ -0,0 +1,10 @@ +contract C { + function f(uint y) public pure { + // fits FixedBytes with exactly 32-bytes + y = 0xffffffff00000000ffffffff00000000ffffffff00000000ffffffff00000000; // FixedBytes (32) + + // fits exactly into FixedBytes (32), ensures underscored literals won't hurt + y = 0xffffffff00000000ffffffff00000000ffffffff00000000ffffffff_00000000; + } +} +// ---- diff --git a/test/libsolidity/syntaxTests/types/rational_number_huge_fail.sol b/test/libsolidity/syntaxTests/types/rational_number_huge_fail.sol new file mode 100644 index 00000000..08e50656 --- /dev/null +++ b/test/libsolidity/syntaxTests/types/rational_number_huge_fail.sol @@ -0,0 +1,8 @@ +contract C { + function f(uint y) public pure { + // one byte too long for storing in Fixedbytes (would require 33 bytes) + y = 0xffffffff00000000ffffffff00000000ffffffff00000000ffffffff000000001; + } +} +// ---- +// TypeError: (142-209): Type int_const 1852...(71 digits omitted)...7281 is not implicitly convertible to expected type uint256. diff --git a/test/libsolidity/syntaxTests/types/too_small_negative_numbers.sol b/test/libsolidity/syntaxTests/types/too_small_negative_numbers.sol new file mode 100644 index 00000000..66bd9a8e --- /dev/null +++ b/test/libsolidity/syntaxTests/types/too_small_negative_numbers.sol @@ -0,0 +1,4 @@ +contract C { + fixed8x80 a = -1e-100; +} +// ---- diff --git a/test/libsolidity/syntaxTests/types/uint256_to_bytes1.sol b/test/libsolidity/syntaxTests/types/uint256_to_bytes1.sol new file mode 100644 index 00000000..f70c89ed --- /dev/null +++ b/test/libsolidity/syntaxTests/types/uint256_to_bytes1.sol @@ -0,0 +1,7 @@ +contract C { + function f() public pure returns(bytes1) { + return bytes1(uint256(0)); + } +} +// ---- +// TypeError: (75-93): Explicit type conversion not allowed from "uint256" to "bytes1". diff --git a/test/libsolidity/syntaxTests/types/uint32_to_bytes32.sol b/test/libsolidity/syntaxTests/types/uint32_to_bytes32.sol new file mode 100644 index 00000000..4153c5c3 --- /dev/null +++ b/test/libsolidity/syntaxTests/types/uint32_to_bytes32.sol @@ -0,0 +1,7 @@ +contract C { + function f() public pure returns(bytes32) { + return bytes32(uint32(0)); + } +} +// ---- +// TypeError: (76-94): Explicit type conversion not allowed from "uint32" to "bytes32". diff --git a/test/libsolidity/syntaxTests/types/unnamed_tuple_decl.sol b/test/libsolidity/syntaxTests/types/unnamed_tuple_decl.sol new file mode 100644 index 00000000..36b3df9f --- /dev/null +++ b/test/libsolidity/syntaxTests/types/unnamed_tuple_decl.sol @@ -0,0 +1,16 @@ +contract C { + function f() internal pure {} + function g() internal pure returns (uint) { return 1; } + function h() internal pure returns (uint, uint) { return (1, 2); } + + function test() internal pure { + var () = f(); + var () = g(); + var (,) = h(); + } +} + +// ---- +// SyntaxError: (223-235): The use of the "var" keyword is disallowed. The declaration part of the statement can be removed, since it is empty. +// SyntaxError: (245-257): The use of the "var" keyword is disallowed. The declaration part of the statement can be removed, since it is empty. +// SyntaxError: (267-280): The use of the "var" keyword is disallowed. The declaration part of the statement can be removed, since it is empty. diff --git a/test/libsolidity/syntaxTests/types/var_empty_decl_0.sol b/test/libsolidity/syntaxTests/types/var_empty_decl_0.sol new file mode 100644 index 00000000..51b949de --- /dev/null +++ b/test/libsolidity/syntaxTests/types/var_empty_decl_0.sol @@ -0,0 +1,9 @@ +contract C { + function f() public pure { + var (); + var (,); + } +} +// ---- +// SyntaxError: (52-58): The use of the "var" keyword is disallowed. The declaration part of the statement can be removed, since it is empty. +// SyntaxError: (68-75): The use of the "var" keyword is disallowed. The declaration part of the statement can be removed, since it is empty. diff --git a/test/libsolidity/syntaxTests/types/var_empty_decl_1.sol b/test/libsolidity/syntaxTests/types/var_empty_decl_1.sol new file mode 100644 index 00000000..20a004ff --- /dev/null +++ b/test/libsolidity/syntaxTests/types/var_empty_decl_1.sol @@ -0,0 +1,8 @@ +contract C { + function f() public pure { + var a; + a.NeverReachedByParser(); + } +} +// ---- +// TypeError: (52-57): Use of the "var" keyword is disallowed. diff --git a/test/libsolidity/syntaxTests/types/var_empty_decl_2.sol b/test/libsolidity/syntaxTests/types/var_empty_decl_2.sol new file mode 100644 index 00000000..de2abc9a --- /dev/null +++ b/test/libsolidity/syntaxTests/types/var_empty_decl_2.sol @@ -0,0 +1,9 @@ +contract C { + function f() public pure { + var (b, c); + b.WeMustNotReachHere(); + c.FailsToLookupToo(); + } +} +// ---- +// TypeError: (52-62): Use of the "var" keyword is disallowed. diff --git a/test/libsolidity/syntaxTests/types/var_empty_decl_3.sol b/test/libsolidity/syntaxTests/types/var_empty_decl_3.sol new file mode 100644 index 00000000..26ee824e --- /dev/null +++ b/test/libsolidity/syntaxTests/types/var_empty_decl_3.sol @@ -0,0 +1,7 @@ +contract C { + function f() public pure { + var (d, e,); + } +} +// ---- +// TypeError: (52-63): Use of the "var" keyword is disallowed. diff --git a/test/libsolidity/syntaxTests/types/var_type_suggest.sol b/test/libsolidity/syntaxTests/types/var_type_suggest.sol new file mode 100644 index 00000000..cc35fdd6 --- /dev/null +++ b/test/libsolidity/syntaxTests/types/var_type_suggest.sol @@ -0,0 +1,35 @@ +contract C { + function h() internal pure returns (uint, uint, uint) { + return (1, 2, 4); + } + function g(uint x) internal pure returns (uint) { + return x; + } + function f() internal pure { + var i = 31415; + var t = "string"; + var g2 = g; + var myblockhash = block.blockhash; + var (a, b) = (2, "troi"); + var (x,, z) = h(); + var (c, d) = (""); + var (k, l) = (2); + var (m, n) = 1; + var (o, p) = ""; + } +} +// ---- +// SyntaxError: (224-237): Use of the "var" keyword is disallowed. Use explicit declaration `uint16 i = ...´ instead. +// SyntaxError: (247-263): Use of the "var" keyword is disallowed. Use explicit declaration `string memory t = ...´ instead. +// SyntaxError: (273-283): Use of the "var" keyword is disallowed. Use explicit declaration `function (uint256) pure returns (uint256) g2 = ...´ instead. +// SyntaxError: (293-326): Use of the "var" keyword is disallowed. Type cannot be expressed in syntax. +// SyntaxError: (336-360): Use of the "var" keyword is disallowed. Use explicit declaration `(uint8 a, string memory b) = ...´ instead. +// SyntaxError: (370-387): Use of the "var" keyword is disallowed. Use explicit declaration `(uint256 x, , uint256 z) = ...´ instead. +// TypeError: (397-414): Different number of components on the left hand side (2) than on the right hand side (1). +// SyntaxError: (397-414): Use of the "var" keyword is disallowed. Type cannot be expressed in syntax. +// TypeError: (424-440): Different number of components on the left hand side (2) than on the right hand side (1). +// SyntaxError: (424-440): Use of the "var" keyword is disallowed. Type cannot be expressed in syntax. +// TypeError: (450-464): Different number of components on the left hand side (2) than on the right hand side (1). +// SyntaxError: (450-464): Use of the "var" keyword is disallowed. Type cannot be expressed in syntax. +// TypeError: (474-489): Different number of components on the left hand side (2) than on the right hand side (1). +// SyntaxError: (474-489): Use of the "var" keyword is disallowed. Type cannot be expressed in syntax. diff --git a/test/libsolidity/syntaxTests/types/zero_literal_to_bytesXX_explicit.sol b/test/libsolidity/syntaxTests/types/zero_literal_to_bytesXX_explicit.sol new file mode 100644 index 00000000..5d606089 --- /dev/null +++ b/test/libsolidity/syntaxTests/types/zero_literal_to_bytesXX_explicit.sol @@ -0,0 +1,30 @@ +contract C { + function f() public pure { + bytes1 b1 = bytes1(0); + bytes2 b2 = bytes2(0); + bytes3 b3 = bytes3(0); + bytes4 b4 = bytes4(0); + bytes8 b8 = bytes8(0); + bytes16 b16 = bytes16(0); + bytes32 b32 = bytes32(0); + b1; b2; b3; b4; b8; b16; b32; + } + function g() public pure { + bytes1 b1 = bytes1(0x000); + bytes2 b2 = bytes2(0x00000); + bytes3 b3 = bytes3(0x0000000); + bytes4 b4 = bytes4(0x000000000); + bytes8 b8 = bytes8(0x00000000000000000); + b1; b2; b3; b4; b8; + } + function h() public pure { + bytes1 b1 = bytes1(0x0); + bytes2 b2 = bytes2(0x0); + bytes3 b3 = bytes3(0x0); + bytes4 b4 = bytes4(0x0); + bytes8 b8 = bytes8(0x0); + bytes16 b16 = bytes16(0x0); + bytes32 b32 = bytes32(0x0); + b1; b2; b3; b4; b8; b16; b32; + } +} diff --git a/test/libsolidity/syntaxTests/types/zero_literal_to_bytesXX_implicit.sol b/test/libsolidity/syntaxTests/types/zero_literal_to_bytesXX_implicit.sol new file mode 100644 index 00000000..48be0655 --- /dev/null +++ b/test/libsolidity/syntaxTests/types/zero_literal_to_bytesXX_implicit.sol @@ -0,0 +1,30 @@ +contract C { + function f() public pure { + bytes1 b1 = 0; + bytes2 b2 = 0; + bytes3 b3 = 0; + bytes4 b4 = 0; + bytes8 b8 = 0; + bytes16 b16 = 0; + bytes32 b32 = 0; + b1; b2; b3; b4; b8; b16; b32; + } + function g() public pure { + bytes1 b1 = 0x000; + bytes2 b2 = 0x00000; + bytes3 b3 = 0x0000000; + bytes4 b4 = 0x000000000; + bytes8 b8 = 0x00000000000000000; + b1; b2; b3; b4; b8; + } + function h() public pure { + bytes1 b1 = 0x0; + bytes2 b2 = 0x0; + bytes3 b3 = 0x0; + bytes4 b4 = 0x0; + bytes8 b8 = 0x0; + bytes16 b16 = 0x0; + bytes32 b32 = 0x0; + b1; b2; b3; b4; b8; b16; b32; + } +} diff --git a/test/libsolidity/syntaxTests/unicode_escape_literals.sol b/test/libsolidity/syntaxTests/unicode_escape_literals.sol new file mode 100644 index 00000000..a340487b --- /dev/null +++ b/test/libsolidity/syntaxTests/unicode_escape_literals.sol @@ -0,0 +1,31 @@ +contract test { + + function oneByteUTF8() public pure returns (bytes32) { + bytes32 usdollar = "aaa\u0024aaa"; + return usdollar; + } + + function twoBytesUTF8() public pure returns (bytes32) { + bytes32 cent = "aaa\u00A2aaa"; + return cent; + } + + function threeBytesUTF8() public pure returns (bytes32) { + bytes32 eur = "aaa\u20ACaaa"; + return eur; + } + + function together() public pure returns (bytes32) { + bytes32 res = "\u0024\u00A2\u20AC"; + return res; + } + + // this function returns an invalid unicode character + function invalidLiteral() public pure returns(bytes32) { + bytes32 invalid = "\u00xx"; + return invalid; + } + +} +// ---- +// ParserError: (678-681): Expected primary expression. diff --git a/test/libsolidity/syntaxTests/unterminatedBlocks/one_dot.sol b/test/libsolidity/syntaxTests/unterminatedBlocks/one_dot.sol new file mode 100644 index 00000000..a678f004 --- /dev/null +++ b/test/libsolidity/syntaxTests/unterminatedBlocks/one_dot.sol @@ -0,0 +1,4 @@ +contract c { + function f() pure public { 1. +// ---- +// ParserError: (47-47): Expected identifier but got end of source
\ No newline at end of file diff --git a/test/libsolidity/syntaxTests/unterminatedBlocks/one_dot_x.sol b/test/libsolidity/syntaxTests/unterminatedBlocks/one_dot_x.sol new file mode 100644 index 00000000..3cc59374 --- /dev/null +++ b/test/libsolidity/syntaxTests/unterminatedBlocks/one_dot_x.sol @@ -0,0 +1,5 @@ +contract test { + function f() pure public { 1.x; } +} +// ---- +// TypeError: (47-50): Member "x" not found or not visible after argument-dependent lookup in int_const 1.
\ No newline at end of file diff --git a/test/libsolidity/syntaxTests/unterminatedBlocks/zero_dot.sol b/test/libsolidity/syntaxTests/unterminatedBlocks/zero_dot.sol new file mode 100644 index 00000000..6ba2b4c2 --- /dev/null +++ b/test/libsolidity/syntaxTests/unterminatedBlocks/zero_dot.sol @@ -0,0 +1,4 @@ +contract c { + function f() pure public { 0. +// ---- +// ParserError: (47-47): Expected identifier but got end of source
\ No newline at end of file diff --git a/test/libsolidity/syntaxTests/unterminatedBlocks/zero_dot_x.sol b/test/libsolidity/syntaxTests/unterminatedBlocks/zero_dot_x.sol new file mode 100644 index 00000000..8648bce2 --- /dev/null +++ b/test/libsolidity/syntaxTests/unterminatedBlocks/zero_dot_x.sol @@ -0,0 +1,5 @@ +contract test { + function f() pure public { 0.x; } +} +// ---- +// TypeError: (47-50): Member "x" not found or not visible after argument-dependent lookup in int_const 0.
\ No newline at end of file diff --git a/test/libsolidity/syntaxTests/upper_case_hex_literals.sol b/test/libsolidity/syntaxTests/upper_case_hex_literals.sol new file mode 100644 index 00000000..0842c2ec --- /dev/null +++ b/test/libsolidity/syntaxTests/upper_case_hex_literals.sol @@ -0,0 +1,9 @@ +contract test { + + function f() public pure returns (uint256) { + uint256 a = 0x1234aAbcC; + uint256 b = 0x1234ABCDEF; + return a + b; + } +} +// ---- diff --git a/test/libsolidity/syntaxTests/variableDeclaration/do_while.sol b/test/libsolidity/syntaxTests/variableDeclaration/do_while.sol new file mode 100644 index 00000000..8fc48b33 --- /dev/null +++ b/test/libsolidity/syntaxTests/variableDeclaration/do_while.sol @@ -0,0 +1,12 @@ +pragma solidity >0.4.24; + +contract C +{ + function f(uint x) public pure { + do + uint y; + while (x > 0); + } +} +// ---- +// SyntaxError: (81-87): Variable declarations can only be used inside blocks. diff --git a/test/libsolidity/syntaxTests/variableDeclaration/else.sol b/test/libsolidity/syntaxTests/variableDeclaration/else.sol new file mode 100644 index 00000000..914e0c0c --- /dev/null +++ b/test/libsolidity/syntaxTests/variableDeclaration/else.sol @@ -0,0 +1,13 @@ +pragma solidity >0.4.24; + +contract C +{ + function f(uint x) public pure { + if (x > 0) + {uint y;} + else + uint z; + } +} +// ---- +// SyntaxError: (109-115): Variable declarations can only be used inside blocks. diff --git a/test/libsolidity/syntaxTests/variableDeclaration/for.sol b/test/libsolidity/syntaxTests/variableDeclaration/for.sol new file mode 100644 index 00000000..bc137f93 --- /dev/null +++ b/test/libsolidity/syntaxTests/variableDeclaration/for.sol @@ -0,0 +1,11 @@ +pragma solidity >0.4.24; + +contract C +{ + function f(uint x) public pure { + for (uint i = 0; i < x; ++i) + uint y; + } +} +// ---- +// SyntaxError: (107-113): Variable declarations can only be used inside blocks. diff --git a/test/libsolidity/syntaxTests/variableDeclaration/if.sol b/test/libsolidity/syntaxTests/variableDeclaration/if.sol new file mode 100644 index 00000000..75ab2026 --- /dev/null +++ b/test/libsolidity/syntaxTests/variableDeclaration/if.sol @@ -0,0 +1,11 @@ +pragma solidity >0.4.24; + +contract C +{ + function f(uint x) public pure { + if (x > 0) + uint y; + } +} +// ---- +// SyntaxError: (89-95): Variable declarations can only be used inside blocks. diff --git a/test/libsolidity/syntaxTests/variableDeclaration/while.sol b/test/libsolidity/syntaxTests/variableDeclaration/while.sol new file mode 100644 index 00000000..2997d80c --- /dev/null +++ b/test/libsolidity/syntaxTests/variableDeclaration/while.sol @@ -0,0 +1,11 @@ +pragma solidity >0.4.24; + +contract C +{ + function f(uint x) public pure { + while (x > 0) + uint y; + } +} +// ---- +// SyntaxError: (92-98): Variable declarations can only be used inside blocks. diff --git a/test/libsolidity/syntaxTests/viewPure/view_pure_abi_encode.sol b/test/libsolidity/syntaxTests/viewPure/view_pure_abi_encode.sol index ca7db42e..e0e031c2 100644 --- a/test/libsolidity/syntaxTests/viewPure/view_pure_abi_encode.sol +++ b/test/libsolidity/syntaxTests/viewPure/view_pure_abi_encode.sol @@ -1,5 +1,5 @@ contract C { - function f() pure public returns (bytes r) { + function f() pure public returns (bytes memory r) { r = abi.encode(1, 2); r = abi.encodePacked(f()); r = abi.encodeWithSelector(0x12345678, 1); diff --git a/test/libsolidity/syntaxTests/viewPure/view_pure_abi_encode_arguments.sol b/test/libsolidity/syntaxTests/viewPure/view_pure_abi_encode_arguments.sol index 547362c3..cc845d51 100644 --- a/test/libsolidity/syntaxTests/viewPure/view_pure_abi_encode_arguments.sol +++ b/test/libsolidity/syntaxTests/viewPure/view_pure_abi_encode_arguments.sol @@ -3,34 +3,34 @@ contract C { function gView() public view returns (uint) { return x; } function gNonPayable() public returns (uint) { x = 4; return 0; } - function f1() view public returns (bytes) { + function f1() view public returns (bytes memory) { return abi.encode(gView()); } - function f2() view public returns (bytes) { + function f2() view public returns (bytes memory) { return abi.encodePacked(gView()); } - function f3() view public returns (bytes) { + function f3() view public returns (bytes memory) { return abi.encodeWithSelector(0x12345678, gView()); } - function f4() view public returns (bytes) { + function f4() view public returns (bytes memory) { return abi.encodeWithSignature("f(uint256)", gView()); } - function g1() public returns (bytes) { + function g1() public returns (bytes memory) { return abi.encode(gNonPayable()); } - function g2() public returns (bytes) { + function g2() public returns (bytes memory) { return abi.encodePacked(gNonPayable()); } - function g3() public returns (bytes) { + function g3() public returns (bytes memory) { return abi.encodeWithSelector(0x12345678, gNonPayable()); } - function g4() public returns (bytes) { + function g4() public returns (bytes memory) { return abi.encodeWithSignature("f(uint256)", gNonPayable()); } // This will generate the only warning. - function check() public returns (bytes) { + function check() public returns (bytes memory) { return abi.encode(2); } } // ---- -// Warning: (1044-1121): Function state mutability can be restricted to pure +// Warning: (1100-1184): Function state mutability can be restricted to pure diff --git a/test/libsolidity/syntaxTests/viewPureChecker/assembly.sol b/test/libsolidity/syntaxTests/viewPureChecker/assembly.sol new file mode 100644 index 00000000..0a11dc3a --- /dev/null +++ b/test/libsolidity/syntaxTests/viewPureChecker/assembly.sol @@ -0,0 +1,23 @@ +contract C { + struct S { uint x; } + S s; + function e() pure public { + assembly { mstore(keccak256(0, 20), mul(s_slot, 2)) } + } + function f() pure public { + uint x; + assembly { x := 7 } + } + function g() view public { + assembly { for {} 1 { pop(sload(0)) } { } pop(gas) } + } + function h() view public { + assembly { function g() { pop(blockhash(20)) } } + } + function j() public { + assembly { pop(call(0, 1, 2, 3, 4, 5, 6)) } + } + function k() public { + assembly { pop(call(gas, 1, 2, 3, 4, 5, 6)) } + } +} diff --git a/test/libsolidity/syntaxTests/viewPureChecker/builtin_functions.sol b/test/libsolidity/syntaxTests/viewPureChecker/builtin_functions.sol new file mode 100644 index 00000000..2503a319 --- /dev/null +++ b/test/libsolidity/syntaxTests/viewPureChecker/builtin_functions.sol @@ -0,0 +1,20 @@ +contract C { + function f() public { + address(this).transfer(1); + require(address(this).send(2)); + selfdestruct(address(this)); + (bool success,) = address(this).delegatecall(""); + require(success); + (success,) = address(this).call(""); + require(success); + } + function g() pure public { + bytes32 x = keccak256("abc"); + bytes32 y = sha256("abc"); + address z = ecrecover(bytes32(uint256(1)), uint8(2), bytes32(uint256(3)), bytes32(uint256(4))); + require(true); + assert(true); + x; y; z; + } + function() payable external {} +} diff --git a/test/libsolidity/syntaxTests/viewPureChecker/builtin_functions_restrict_warning.sol b/test/libsolidity/syntaxTests/viewPureChecker/builtin_functions_restrict_warning.sol new file mode 100644 index 00000000..4a651d21 --- /dev/null +++ b/test/libsolidity/syntaxTests/viewPureChecker/builtin_functions_restrict_warning.sol @@ -0,0 +1,21 @@ +contract C { + function f() view public { + bytes32 x = keccak256("abc"); + bytes32 y = sha256("abc"); + address z = ecrecover(bytes32(uint256(1)), uint8(2), bytes32(uint256(3)), bytes32(uint256(4))); + require(true); + assert(true); + x; y; z; + } + function g() public { + bytes32 x = keccak256("abc"); + bytes32 y = sha256("abc"); + address z = ecrecover(bytes32(uint256(1)), uint8(2), bytes32(uint256(3)), bytes32(uint256(4))); + require(true); + assert(true); + x; y; z; + } +} +// ---- +// Warning: (17-288): Function state mutability can be restricted to pure +// Warning: (293-559): Function state mutability can be restricted to pure diff --git a/test/libsolidity/syntaxTests/viewPureChecker/builtin_functions_view_fail.sol b/test/libsolidity/syntaxTests/viewPureChecker/builtin_functions_view_fail.sol new file mode 100644 index 00000000..5356f0b8 --- /dev/null +++ b/test/libsolidity/syntaxTests/viewPureChecker/builtin_functions_view_fail.sol @@ -0,0 +1,27 @@ +contract C { + function f() view public { + address(this).transfer(1); + } + function g() view public { + require(address(this).send(2)); + } + function h() view public { + selfdestruct(address(this)); + } + function i() view public { + (bool success,) = address(this).delegatecall(""); + require(success); + } + function j() view public { + (bool success,) = address(this).call(""); + require(success); + } + function() payable external { + } +} +// ---- +// TypeError: (52-77): Function declared as view, but this expression (potentially) modifies the state and thus requires non-payable (the default) or payable. +// TypeError: (132-153): Function declared as view, but this expression (potentially) modifies the state and thus requires non-payable (the default) or payable. +// TypeError: (201-228): Function declared as view, but this expression (potentially) modifies the state and thus requires non-payable (the default) or payable. +// TypeError: (293-323): Function declared as view, but this expression (potentially) modifies the state and thus requires non-payable (the default) or payable. +// TypeError: (414-436): Function declared as view, but this expression (potentially) modifies the state and thus requires non-payable (the default) or payable. diff --git a/test/libsolidity/syntaxTests/viewPureChecker/call_internal_functions_fail.sol b/test/libsolidity/syntaxTests/viewPureChecker/call_internal_functions_fail.sol new file mode 100644 index 00000000..e21037bd --- /dev/null +++ b/test/libsolidity/syntaxTests/viewPureChecker/call_internal_functions_fail.sol @@ -0,0 +1,10 @@ +contract C { + uint x; + function f() pure public { g(); } + function g() view public { x; } + function h() view public { i(); } + function i() public { x = 2; } +} +// ---- +// TypeError: (56-59): Function declared as pure, but this expression (potentially) reads from the environment or state and thus requires "view". +// TypeError: (130-133): Function declared as view, but this expression (potentially) modifies the state and thus requires non-payable (the default) or payable. diff --git a/test/libsolidity/syntaxTests/viewPureChecker/call_internal_functions_success.sol b/test/libsolidity/syntaxTests/viewPureChecker/call_internal_functions_success.sol new file mode 100644 index 00000000..5aa21ce1 --- /dev/null +++ b/test/libsolidity/syntaxTests/viewPureChecker/call_internal_functions_success.sol @@ -0,0 +1,6 @@ +contract C { + function g() pure public { g(); } + function f() view public returns (uint) { f(); g(); } + function h() public { h(); g(); f(); } + function i() payable public { i(); h(); g(); f(); } +} diff --git a/test/libsolidity/syntaxTests/viewPureChecker/constant.sol b/test/libsolidity/syntaxTests/viewPureChecker/constant.sol new file mode 100644 index 00000000..36d93497 --- /dev/null +++ b/test/libsolidity/syntaxTests/viewPureChecker/constant.sol @@ -0,0 +1,6 @@ +contract C { + uint constant x = 2; + function k() pure public returns (uint) { + return x; + } +} diff --git a/test/libsolidity/syntaxTests/viewPureChecker/constant_restrict_warning.sol b/test/libsolidity/syntaxTests/viewPureChecker/constant_restrict_warning.sol new file mode 100644 index 00000000..a4b4a353 --- /dev/null +++ b/test/libsolidity/syntaxTests/viewPureChecker/constant_restrict_warning.sol @@ -0,0 +1,12 @@ +contract C { + uint constant x = 2; + function f() view public returns (uint) { + return x; + } + function g() public returns (uint) { + return x; + } +} +// ---- +// Warning: (42-107): Function state mutability can be restricted to pure +// Warning: (112-172): Function state mutability can be restricted to pure diff --git a/test/libsolidity/syntaxTests/viewPureChecker/creation_no_restrict_warning.sol b/test/libsolidity/syntaxTests/viewPureChecker/creation_no_restrict_warning.sol new file mode 100644 index 00000000..d80edd1b --- /dev/null +++ b/test/libsolidity/syntaxTests/viewPureChecker/creation_no_restrict_warning.sol @@ -0,0 +1,4 @@ +contract D {} +contract C { + function f() public { new D(); } +} diff --git a/test/libsolidity/syntaxTests/viewPureChecker/creation_view_fail.sol b/test/libsolidity/syntaxTests/viewPureChecker/creation_view_fail.sol new file mode 100644 index 00000000..08e45ea1 --- /dev/null +++ b/test/libsolidity/syntaxTests/viewPureChecker/creation_view_fail.sol @@ -0,0 +1,6 @@ +contract D {} +contract C { + function f() public view { new D(); } +} +// ---- +// TypeError: (58-65): Function declared as view, but this expression (potentially) modifies the state and thus requires non-payable (the default) or payable. diff --git a/test/libsolidity/syntaxTests/viewPureChecker/function_types.sol b/test/libsolidity/syntaxTests/viewPureChecker/function_types.sol new file mode 100644 index 00000000..92943889 --- /dev/null +++ b/test/libsolidity/syntaxTests/viewPureChecker/function_types.sol @@ -0,0 +1,22 @@ +contract C { + function f() pure public { + function () external nonpayFun; + function () external view viewFun; + function () external pure pureFun; + + nonpayFun; + viewFun; + pureFun; + pureFun(); + } + function g() view public { + function () external view viewFun; + + viewFun(); + } + function h() public { + function () external nonpayFun; + + nonpayFun(); + } +} diff --git a/test/libsolidity/syntaxTests/viewPureChecker/function_types_fail.sol b/test/libsolidity/syntaxTests/viewPureChecker/function_types_fail.sol new file mode 100644 index 00000000..d00f65c9 --- /dev/null +++ b/test/libsolidity/syntaxTests/viewPureChecker/function_types_fail.sol @@ -0,0 +1,18 @@ +contract C { + function f() pure public { + function () external nonpayFun; + nonpayFun(); + } + function g() pure public { + function () external view viewFun; + viewFun(); + } + function h() view public { + function () external nonpayFun; + nonpayFun(); + } +} +// ---- +// TypeError: (92-103): Function declared as pure, but this expression (potentially) modifies the state and thus requires non-payable (the default) or payable. +// TypeError: (193-202): Function declared as pure, but this expression (potentially) reads from the environment or state and thus requires "view". +// TypeError: (289-300): Function declared as view, but this expression (potentially) modifies the state and thus requires non-payable (the default) or payable. diff --git a/test/libsolidity/syntaxTests/viewPureChecker/interface.sol b/test/libsolidity/syntaxTests/viewPureChecker/interface.sol new file mode 100644 index 00000000..0874e78a --- /dev/null +++ b/test/libsolidity/syntaxTests/viewPureChecker/interface.sol @@ -0,0 +1,6 @@ +interface D { + function f() view external; +} +contract C is D { + function f() view external {} +} diff --git a/test/libsolidity/syntaxTests/viewPureChecker/local_storage_variables.sol b/test/libsolidity/syntaxTests/viewPureChecker/local_storage_variables.sol new file mode 100644 index 00000000..7d01118a --- /dev/null +++ b/test/libsolidity/syntaxTests/viewPureChecker/local_storage_variables.sol @@ -0,0 +1,19 @@ +contract C { + struct S { uint a; } + S s; + function f() view public { + S storage x = s; + x; + } + function g() view public { + S storage x = s; + x = s; + } + function i() public { + s.a = 2; + } + function h() public { + S storage x = s; + x.a = 2; + } +} diff --git a/test/libsolidity/syntaxTests/viewPureChecker/local_storage_variables_fail.sol b/test/libsolidity/syntaxTests/viewPureChecker/local_storage_variables_fail.sol new file mode 100644 index 00000000..0ff1ac24 --- /dev/null +++ b/test/libsolidity/syntaxTests/viewPureChecker/local_storage_variables_fail.sol @@ -0,0 +1,15 @@ +contract C { + struct S { uint a; } + S s; + function f() pure public { + S storage x = s; + x; + } + function g() view public { + S storage x = s; + x.a = 1; + } +} +// ---- +// TypeError: (100-101): Function declared as pure, but this expression (potentially) reads from the environment or state and thus requires "view". +// TypeError: (184-187): Function declared as view, but this expression (potentially) modifies the state and thus requires non-payable (the default) or payable. diff --git a/test/libsolidity/syntaxTests/viewPureChecker/mappings.sol b/test/libsolidity/syntaxTests/viewPureChecker/mappings.sol new file mode 100644 index 00000000..eb0ccbfb --- /dev/null +++ b/test/libsolidity/syntaxTests/viewPureChecker/mappings.sol @@ -0,0 +1,12 @@ +contract C { + mapping(uint => uint) a; + function f() view public { + a; + } + function g() view public { + a[2]; + } + function h() public { + a[2] = 3; + } +} diff --git a/test/libsolidity/syntaxTests/viewPureChecker/modifiers.sol b/test/libsolidity/syntaxTests/viewPureChecker/modifiers.sol new file mode 100644 index 00000000..f8f6b2cb --- /dev/null +++ b/test/libsolidity/syntaxTests/viewPureChecker/modifiers.sol @@ -0,0 +1,17 @@ +contract D { + uint x; + modifier purem(uint) { _; } + modifier viewm(uint) { uint a = x; _; a; } + modifier nonpayablem(uint) { x = 2; _; } +} +contract C is D { + function f() purem(0) pure public {} + function g() viewm(0) view public {} + function h() nonpayablem(0) public {} + function i() purem(x) view public {} + function j() viewm(x) view public {} + function k() nonpayablem(x) public {} + function l() purem(x = 2) public {} + function m() viewm(x = 2) public {} + function n() nonpayablem(x = 2) public {} +} diff --git a/test/libsolidity/syntaxTests/viewPureChecker/modifiers_fail.sol b/test/libsolidity/syntaxTests/viewPureChecker/modifiers_fail.sol new file mode 100644 index 00000000..513850f7 --- /dev/null +++ b/test/libsolidity/syntaxTests/viewPureChecker/modifiers_fail.sol @@ -0,0 +1,12 @@ +contract D { + uint x; + modifier viewm(uint) { uint a = x; _; a; } + modifier nonpayablem(uint) { x = 2; _; } +} +contract C is D { + function f() viewm(0) pure public {} + function g() nonpayablem(0) view public {} +} +// ---- +// TypeError: (154-162): Function declared as pure, but this expression (potentially) reads from the environment or state and thus requires "view". +// TypeError: (195-209): Function declared as view, but this expression (potentially) modifies the state and thus requires non-payable (the default) or payable. diff --git a/test/libsolidity/syntaxTests/viewPureChecker/msg_value_modifier.sol b/test/libsolidity/syntaxTests/viewPureChecker/msg_value_modifier.sol new file mode 100644 index 00000000..160b20a7 --- /dev/null +++ b/test/libsolidity/syntaxTests/viewPureChecker/msg_value_modifier.sol @@ -0,0 +1,6 @@ +contract C { + modifier m(uint _amount, uint _avail) { require(_avail >= _amount); _; } + function f() m(1 ether, msg.value) public pure {} +} +// ---- +// TypeError: (118-127): Function declared as pure, but this expression (potentially) reads from the environment or state and thus requires "view". diff --git a/test/libsolidity/syntaxTests/viewPureChecker/msg_value_modifier_view.sol b/test/libsolidity/syntaxTests/viewPureChecker/msg_value_modifier_view.sol new file mode 100644 index 00000000..613b0198 --- /dev/null +++ b/test/libsolidity/syntaxTests/viewPureChecker/msg_value_modifier_view.sol @@ -0,0 +1,6 @@ +contract C { + modifier m(uint _amount, uint _avail) { require(_avail >= _amount); _; } + function f() m(1 ether, msg.value) public view {} +} +// ---- +// TypeError: (118-127): "msg.value" can only be used in payable public functions. Make the function "payable" or use an internal function to avoid this error. diff --git a/test/libsolidity/syntaxTests/viewPureChecker/overriding_fail.sol b/test/libsolidity/syntaxTests/viewPureChecker/overriding_fail.sol new file mode 100644 index 00000000..61702495 --- /dev/null +++ b/test/libsolidity/syntaxTests/viewPureChecker/overriding_fail.sol @@ -0,0 +1,16 @@ +contract D { + uint x; + function f() public view { x; } + function g() public pure {} +} +contract C1 is D { + function f() public {} + function g() public view {} +} +contract C2 is D { + function g() public {} +} +// ---- +// TypeError: (118-140): Overriding function changes state mutability from "view" to "nonpayable". +// TypeError: (145-172): Overriding function changes state mutability from "pure" to "view". +// TypeError: (198-220): Overriding function changes state mutability from "pure" to "nonpayable". diff --git a/test/libsolidity/syntaxTests/viewPureChecker/overriding_no_restrict_warning.sol b/test/libsolidity/syntaxTests/viewPureChecker/overriding_no_restrict_warning.sol new file mode 100644 index 00000000..c82c7908 --- /dev/null +++ b/test/libsolidity/syntaxTests/viewPureChecker/overriding_no_restrict_warning.sol @@ -0,0 +1,7 @@ +contract D { + uint x; + function f() public { x = 2; } +} +contract C is D { + function f() public {} +} diff --git a/test/libsolidity/syntaxTests/viewPureChecker/read_storage_pure_fail.sol b/test/libsolidity/syntaxTests/viewPureChecker/read_storage_pure_fail.sol new file mode 100644 index 00000000..785656b9 --- /dev/null +++ b/test/libsolidity/syntaxTests/viewPureChecker/read_storage_pure_fail.sol @@ -0,0 +1,8 @@ +contract C { + uint x; + function f() public pure returns (uint) { + return x; + } +} +// ---- +// TypeError: (86-87): Function declared as pure, but this expression (potentially) reads from the environment or state and thus requires "view". diff --git a/test/libsolidity/syntaxTests/viewPureChecker/returning_structs_fail.sol b/test/libsolidity/syntaxTests/viewPureChecker/returning_structs_fail.sol new file mode 100644 index 00000000..e04d0825 --- /dev/null +++ b/test/libsolidity/syntaxTests/viewPureChecker/returning_structs_fail.sol @@ -0,0 +1,13 @@ +contract C { + struct S { uint x; } + S s; + function f() pure internal returns (S storage) { + return s; + } + function g() pure public { + f().x; + } +} +// ---- +// TypeError: (115-116): Function declared as pure, but this expression (potentially) reads from the environment or state and thus requires "view". +// TypeError: (163-168): Function declared as pure, but this expression (potentially) reads from the environment or state and thus requires "view". diff --git a/test/libsolidity/syntaxTests/viewPureChecker/returning_structs_no_restrict_warning.sol b/test/libsolidity/syntaxTests/viewPureChecker/returning_structs_no_restrict_warning.sol new file mode 100644 index 00000000..9b4eb466 --- /dev/null +++ b/test/libsolidity/syntaxTests/viewPureChecker/returning_structs_no_restrict_warning.sol @@ -0,0 +1,14 @@ +contract C { + struct S { uint x; } + S s; + function f() view internal returns (S storage) { + return s; + } + function g() public { + f().x = 2; + } + function h() view public { + f(); + f().x; + } +} diff --git a/test/libsolidity/syntaxTests/viewPureChecker/selector.sol b/test/libsolidity/syntaxTests/viewPureChecker/selector.sol new file mode 100644 index 00000000..c4e30075 --- /dev/null +++ b/test/libsolidity/syntaxTests/viewPureChecker/selector.sol @@ -0,0 +1,12 @@ +contract C { + uint public x; + function f() payable public { + } + function g() pure public returns (bytes4) { + return this.f.selector ^ this.x.selector; + } + function h() view public returns (bytes4) { + x; + return this.f.selector ^ this.x.selector; + } +} diff --git a/test/libsolidity/syntaxTests/viewPureChecker/selector_complex.sol b/test/libsolidity/syntaxTests/viewPureChecker/selector_complex.sol new file mode 100644 index 00000000..311dec4a --- /dev/null +++ b/test/libsolidity/syntaxTests/viewPureChecker/selector_complex.sol @@ -0,0 +1,11 @@ +contract C { + function f(C c) pure public returns (C) { + return c; + } + function g() pure public returns (bytes4) { + // By passing `this`, we read from the state, even if f itself is pure. + return f(this).f.selector; + } +} +// ---- +// TypeError: (228-232): Function declared as pure, but this expression (potentially) reads from the environment or state and thus requires "view". diff --git a/test/libsolidity/syntaxTests/viewPureChecker/selector_complex2.sol b/test/libsolidity/syntaxTests/viewPureChecker/selector_complex2.sol new file mode 100644 index 00000000..d1543fed --- /dev/null +++ b/test/libsolidity/syntaxTests/viewPureChecker/selector_complex2.sol @@ -0,0 +1,9 @@ +contract C { + function f() payable public returns (C) { + return this; + } + function g() pure public returns (bytes4) { + C x = C(0x123); + return x.f.selector; + } +} diff --git a/test/libsolidity/syntaxTests/viewPureChecker/smoke_test.sol b/test/libsolidity/syntaxTests/viewPureChecker/smoke_test.sol new file mode 100644 index 00000000..0e397efc --- /dev/null +++ b/test/libsolidity/syntaxTests/viewPureChecker/smoke_test.sol @@ -0,0 +1,7 @@ +contract C { + uint x; + function g() pure public {} + function f() view public returns (uint) { return now; } + function h() public { x = 2; } + function i() payable public { x = 2; } +} diff --git a/test/libsolidity/syntaxTests/viewPureChecker/suggest_pure.sol b/test/libsolidity/syntaxTests/viewPureChecker/suggest_pure.sol new file mode 100644 index 00000000..87719eb3 --- /dev/null +++ b/test/libsolidity/syntaxTests/viewPureChecker/suggest_pure.sol @@ -0,0 +1,5 @@ +contract C { + function g() view public { } +} +// ---- +// Warning: (17-45): Function state mutability can be restricted to pure diff --git a/test/libsolidity/syntaxTests/viewPureChecker/suggest_view.sol b/test/libsolidity/syntaxTests/viewPureChecker/suggest_view.sol new file mode 100644 index 00000000..c045dfc4 --- /dev/null +++ b/test/libsolidity/syntaxTests/viewPureChecker/suggest_view.sol @@ -0,0 +1,6 @@ +contract C { + uint x; + function g() public returns (uint) { return x; } +} +// ---- +// Warning: (29-77): Function state mutability can be restricted to view diff --git a/test/libsolidity/syntaxTests/viewPureChecker/write_storage_fail.sol b/test/libsolidity/syntaxTests/viewPureChecker/write_storage_fail.sol new file mode 100644 index 00000000..3fed4d29 --- /dev/null +++ b/test/libsolidity/syntaxTests/viewPureChecker/write_storage_fail.sol @@ -0,0 +1,6 @@ +contract C { + uint x; + function f() view public { x = 2; } +} +// ---- +// TypeError: (56-57): Function declared as view, but this expression (potentially) modifies the state and thus requires non-payable (the default) or payable. diff --git a/test/libsolidity/syntaxTests/visibility/function_no_visibility.sol b/test/libsolidity/syntaxTests/visibility/function_no_visibility.sol new file mode 100644 index 00000000..4fc7900f --- /dev/null +++ b/test/libsolidity/syntaxTests/visibility/function_no_visibility.sol @@ -0,0 +1,5 @@ +contract C { + function f() pure { } +} +// ---- +// SyntaxError: (17-38): No visibility specified. Did you intend to add "public"? diff --git a/test/libsolidity/syntaxTests/visibility/interface/function_default.sol b/test/libsolidity/syntaxTests/visibility/interface/function_default.sol index 72ce3b40..b7e96e5e 100644 --- a/test/libsolidity/syntaxTests/visibility/interface/function_default.sol +++ b/test/libsolidity/syntaxTests/visibility/interface/function_default.sol @@ -2,5 +2,5 @@ interface I { function f(); } // ---- -// Warning: (15-28): Functions in interfaces should be declared external. -// Warning: (15-28): No visibility specified. Defaulting to "public". In interfaces it defaults to external. +// SyntaxError: (15-28): No visibility specified. Did you intend to add "external"? +// TypeError: (15-28): Functions in interfaces must be declared external. diff --git a/test/libsolidity/syntaxTests/visibility/interface/function_default050.sol b/test/libsolidity/syntaxTests/visibility/interface/function_default050.sol deleted file mode 100644 index 513df26b..00000000 --- a/test/libsolidity/syntaxTests/visibility/interface/function_default050.sol +++ /dev/null @@ -1,7 +0,0 @@ -pragma experimental "v0.5.0"; -interface I { - function f(); -} -// ---- -// SyntaxError: (45-58): No visibility specified. -// TypeError: (45-58): Functions in interfaces must be declared external. diff --git a/test/libsolidity/syntaxTests/visibility/interface/function_external.sol b/test/libsolidity/syntaxTests/visibility/interface/function_external.sol new file mode 100644 index 00000000..ed409e58 --- /dev/null +++ b/test/libsolidity/syntaxTests/visibility/interface/function_external.sol @@ -0,0 +1,4 @@ +interface I { + function f() external; +} +// ---- diff --git a/test/libsolidity/syntaxTests/visibility/interface/function_external050.sol b/test/libsolidity/syntaxTests/visibility/interface/function_external050.sol deleted file mode 100644 index 3f0a9aca..00000000 --- a/test/libsolidity/syntaxTests/visibility/interface/function_external050.sol +++ /dev/null @@ -1,5 +0,0 @@ -pragma experimental "v0.5.0"; -interface I { - function f() external; -} -// ---- diff --git a/test/libsolidity/syntaxTests/visibility/interface/function_internal.sol b/test/libsolidity/syntaxTests/visibility/interface/function_internal.sol index ac62e69b..06c1547a 100644 --- a/test/libsolidity/syntaxTests/visibility/interface/function_internal.sol +++ b/test/libsolidity/syntaxTests/visibility/interface/function_internal.sol @@ -2,4 +2,4 @@ interface I { function f() internal; } // ---- -// TypeError: (15-37): Functions in interfaces cannot be internal or private. +// TypeError: (15-37): Functions in interfaces must be declared external. diff --git a/test/libsolidity/syntaxTests/visibility/interface/function_private.sol b/test/libsolidity/syntaxTests/visibility/interface/function_private.sol index 881e647e..98198c3d 100644 --- a/test/libsolidity/syntaxTests/visibility/interface/function_private.sol +++ b/test/libsolidity/syntaxTests/visibility/interface/function_private.sol @@ -2,4 +2,4 @@ interface I { function f() private; } // ---- -// TypeError: (15-36): Functions in interfaces cannot be internal or private. +// TypeError: (15-36): Functions in interfaces must be declared external. diff --git a/test/libsolidity/syntaxTests/visibility/interface/function_public.sol b/test/libsolidity/syntaxTests/visibility/interface/function_public.sol index 891d9fdf..a8cea199 100644 --- a/test/libsolidity/syntaxTests/visibility/interface/function_public.sol +++ b/test/libsolidity/syntaxTests/visibility/interface/function_public.sol @@ -2,4 +2,4 @@ interface I { function f() public; } // ---- -// Warning: (15-35): Functions in interfaces should be declared external. +// TypeError: (15-35): Functions in interfaces must be declared external. diff --git a/test/libsolidity/syntaxTests/visibility/interface/function_public050.sol b/test/libsolidity/syntaxTests/visibility/interface/function_public050.sol deleted file mode 100644 index e0c04095..00000000 --- a/test/libsolidity/syntaxTests/visibility/interface/function_public050.sol +++ /dev/null @@ -1,6 +0,0 @@ -pragma experimental "v0.5.0"; -interface I { - function f() public; -} -// ---- -// TypeError: (45-65): Functions in interfaces must be declared external. diff --git a/test/libsolidity/syntaxTests/visibility/interface/interface_contract_function_default.sol b/test/libsolidity/syntaxTests/visibility/interface/interface_contract_function_default.sol new file mode 100644 index 00000000..b1a820ed --- /dev/null +++ b/test/libsolidity/syntaxTests/visibility/interface/interface_contract_function_default.sol @@ -0,0 +1,12 @@ +// State of the syntax checker has to be reset after the interface +// was visited. The suggested visibility for g() should not be external. +interface I { + function f(); +} +contract C { + function g(); +} +// ---- +// SyntaxError: (158-171): No visibility specified. Did you intend to add "external"? +// SyntaxError: (191-204): No visibility specified. Did you intend to add "public"? +// TypeError: (158-171): Functions in interfaces must be declared external. diff --git a/test/libyul/Common.cpp b/test/libyul/Common.cpp new file mode 100644 index 00000000..d224bdcd --- /dev/null +++ b/test/libyul/Common.cpp @@ -0,0 +1,95 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see <http://www.gnu.org/licenses/>. +*/ +/** + * @date 2017 + * Common functions the Yul tests. + */ + +#include <test/libyul/Common.h> + +#include <test/Options.h> + +#include <libyul/optimiser/Disambiguator.h> + +#include <libsolidity/parsing/Scanner.h> + +#include <libsolidity/inlineasm/AsmParser.h> +#include <libsolidity/inlineasm/AsmAnalysis.h> +#include <libsolidity/inlineasm/AsmPrinter.h> + +#include <libsolidity/interface/SourceReferenceFormatter.h> +#include <libsolidity/interface/ErrorReporter.h> + +#include <boost/test/unit_test.hpp> + +using namespace std; +using namespace dev::yul; +using namespace dev::solidity; + +void dev::yul::test::printErrors(ErrorList const& _errors, Scanner const& _scanner) +{ + SourceReferenceFormatter formatter(cout, [&](std::string const&) -> Scanner const& { return _scanner; }); + + for (auto const& error: _errors) + formatter.printExceptionInformation( + *error, + (error->type() == Error::Type::Warning) ? "Warning" : "Error" + ); +} + + +pair<shared_ptr<Block>, shared_ptr<assembly::AsmAnalysisInfo>> dev::yul::test::parse(string const& _source, bool _yul) +{ + auto flavour = _yul ? assembly::AsmFlavour::Yul : assembly::AsmFlavour::Strict; + ErrorList errors; + ErrorReporter errorReporter(errors); + auto scanner = make_shared<Scanner>(CharStream(_source), ""); + auto parserResult = assembly::Parser(errorReporter, flavour).parse(scanner, false); + if (parserResult) + { + BOOST_REQUIRE(errorReporter.errors().empty()); + auto analysisInfo = make_shared<assembly::AsmAnalysisInfo>(); + assembly::AsmAnalyzer analyzer( + *analysisInfo, + errorReporter, + dev::test::Options::get().evmVersion(), + boost::none, + flavour + ); + if (analyzer.analyze(*parserResult)) + { + BOOST_REQUIRE(errorReporter.errors().empty()); + return make_pair(parserResult, analysisInfo); + } + } + printErrors(errors, *scanner); + BOOST_FAIL("Invalid source."); + + // Unreachable. + return {}; +} + +assembly::Block dev::yul::test::disambiguate(string const& _source, bool _yul) +{ + auto result = parse(_source, _yul); + return boost::get<Block>(Disambiguator(*result.second, {})(*result.first)); +} + +string dev::yul::test::format(string const& _source, bool _yul) +{ + return assembly::AsmPrinter(_yul)(*parse(_source, _yul).first); +} diff --git a/test/libyul/Common.h b/test/libyul/Common.h new file mode 100644 index 00000000..ee191494 --- /dev/null +++ b/test/libyul/Common.h @@ -0,0 +1,55 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see <http://www.gnu.org/licenses/>. +*/ +/** + * @date 2017 + * Common functions the Yul tests. + */ + +#pragma once + +#include <libsolidity/inlineasm/AsmData.h> + +#include <string> +#include <vector> +#include <memory> + +namespace dev +{ +namespace solidity +{ +class Scanner; +class Error; +using ErrorList = std::vector<std::shared_ptr<Error const>>; +namespace assembly +{ +struct AsmAnalysisInfo; +} +} +namespace yul +{ +namespace test +{ + +void printErrors(solidity::ErrorList const& _errors, solidity::Scanner const& _scanner); +std::pair<std::shared_ptr<solidity::assembly::Block>, std::shared_ptr<solidity::assembly::AsmAnalysisInfo>> +parse(std::string const& _source, bool _yul = true); +solidity::assembly::Block disambiguate(std::string const& _source, bool _yul = true); +std::string format(std::string const& _source, bool _yul = true); + +} +} +} diff --git a/test/libyul/Inliner.cpp b/test/libyul/Inliner.cpp new file mode 100644 index 00000000..66810298 --- /dev/null +++ b/test/libyul/Inliner.cpp @@ -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 <http://www.gnu.org/licenses/>. +*/ +/** + * @date 2017 + * Unit tests for the Yul function inliner. + */ + +#include <test/libyul/Common.h> + +#include <libyul/optimiser/ExpressionInliner.h> +#include <libyul/optimiser/InlinableExpressionFunctionFinder.h> +#include <libyul/optimiser/FullInliner.h> +#include <libyul/optimiser/FunctionHoister.h> +#include <libyul/optimiser/FunctionGrouper.h> + +#include <libsolidity/inlineasm/AsmPrinter.h> + +#include <boost/test/unit_test.hpp> + +#include <boost/range/adaptors.hpp> +#include <boost/algorithm/string/join.hpp> + +using namespace std; +using namespace dev; +using namespace dev::yul; +using namespace dev::yul::test; +using namespace dev::solidity; + +namespace +{ +string inlinableFunctions(string const& _source) +{ + auto ast = disambiguate(_source); + + InlinableExpressionFunctionFinder funFinder; + funFinder(ast); + + vector<string> functionNames; + for (auto const& f: funFinder.inlinableFunctions()) + functionNames.emplace_back(f.first.str()); + return boost::algorithm::join(functionNames, ","); +} + +} + + +BOOST_AUTO_TEST_SUITE(YulInlinableFunctionFilter) + +BOOST_AUTO_TEST_CASE(smoke_test) +{ + BOOST_CHECK_EQUAL(inlinableFunctions("{ }"), ""); +} + +BOOST_AUTO_TEST_CASE(simple) +{ + BOOST_CHECK_EQUAL(inlinableFunctions("{ function f() -> x:u256 { x := 2:u256 } }"), "f"); + BOOST_CHECK_EQUAL(inlinableFunctions("{" + "function g(a:u256) -> b:u256 { b := a }" + "function f() -> x:u256 { x := g(2:u256) }" + "}"), "g,f"); +} + +BOOST_AUTO_TEST_CASE(simple_inside_structures) +{ + BOOST_CHECK_EQUAL(inlinableFunctions("{" + "switch 2:u256 " + "case 2:u256 {" + "function g(a:u256) -> b:u256 { b := a }" + "function f() -> x:u256 { x := g(2:u256) }" + "}" + "}"), "g,f"); + BOOST_CHECK_EQUAL(inlinableFunctions("{" + "for {" + "function g(a:u256) -> b:u256 { b := a }" + "} 1:u256 {" + "function f() -> x:u256 { x := g(2:u256) }" + "}" + "{" + "function h() -> y:u256 { y := 2:u256 }" + "}" + "}"), "h,g,f"); +} + +BOOST_AUTO_TEST_CASE(negative) +{ + BOOST_CHECK_EQUAL(inlinableFunctions("{ function f() -> x:u256 { } }"), ""); + BOOST_CHECK_EQUAL(inlinableFunctions("{ function f() -> x:u256 { x := 2:u256 {} } }"), ""); + BOOST_CHECK_EQUAL(inlinableFunctions("{ function f() -> x:u256 { x := f() } }"), ""); + BOOST_CHECK_EQUAL(inlinableFunctions("{ function f() -> x:u256 { x := x } }"), ""); + BOOST_CHECK_EQUAL(inlinableFunctions("{ function f() -> x:u256, y:u256 { x := 2:u256 } }"), ""); + BOOST_CHECK_EQUAL(inlinableFunctions( + "{ function g() -> x:u256, y:u256 {} function f(y:u256) -> x:u256 { x,y := g() } }"), ""); + BOOST_CHECK_EQUAL(inlinableFunctions("{ function f(y:u256) -> x:u256 { y := 2:u256 } }"), ""); +} + + +BOOST_AUTO_TEST_SUITE_END() diff --git a/test/libyul/Parser.cpp b/test/libyul/Parser.cpp new file mode 100644 index 00000000..3f329d28 --- /dev/null +++ b/test/libyul/Parser.cpp @@ -0,0 +1,306 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see <http://www.gnu.org/licenses/>. +*/ +/** + * @date 2017 + * Unit tests for parsing Yul. + */ + +#include <test/Options.h> + +#include <test/libsolidity/ErrorCheck.h> + +#include <libsolidity/inlineasm/AsmParser.h> +#include <libsolidity/inlineasm/AsmAnalysis.h> +#include <libsolidity/inlineasm/AsmAnalysisInfo.h> +#include <libsolidity/parsing/Scanner.h> +#include <libsolidity/interface/ErrorReporter.h> + +#include <boost/optional.hpp> +#include <boost/algorithm/string/replace.hpp> + +#include <string> +#include <memory> + +using namespace std; + +namespace dev +{ +namespace solidity +{ +namespace test +{ + +namespace +{ + +bool parse(string const& _source, ErrorReporter& errorReporter) +{ + try + { + auto scanner = make_shared<Scanner>(CharStream(_source)); + auto parserResult = assembly::Parser(errorReporter, assembly::AsmFlavour::Yul).parse(scanner, false); + if (parserResult) + { + assembly::AsmAnalysisInfo analysisInfo; + return (assembly::AsmAnalyzer( + analysisInfo, + errorReporter, + dev::test::Options::get().evmVersion(), + boost::none, + assembly::AsmFlavour::Yul + )).analyze(*parserResult); + } + } + catch (FatalError const&) + { + BOOST_FAIL("Fatal error leaked."); + } + return false; +} + +boost::optional<Error> parseAndReturnFirstError(string const& _source, bool _allowWarnings = true) +{ + ErrorList errors; + ErrorReporter errorReporter(errors); + if (!parse(_source, errorReporter)) + { + BOOST_REQUIRE_EQUAL(errors.size(), 1); + return *errors.front(); + } + else + { + // If success is true, there might still be an error in the assembly stage. + if (_allowWarnings && Error::containsOnlyWarnings(errors)) + return {}; + else if (!errors.empty()) + { + if (!_allowWarnings) + BOOST_CHECK_EQUAL(errors.size(), 1); + return *errors.front(); + } + } + return {}; +} + +bool successParse(std::string const& _source, bool _allowWarnings = true) +{ + return !parseAndReturnFirstError(_source, _allowWarnings); +} + +Error expectError(std::string const& _source, bool _allowWarnings = false) +{ + + auto error = parseAndReturnFirstError(_source, _allowWarnings); + BOOST_REQUIRE(error); + return *error; +} + +} + +#define CHECK_ERROR(text, typ, substring) \ +do \ +{ \ + Error err = expectError((text), false); \ + BOOST_CHECK(err.type() == (Error::Type::typ)); \ + BOOST_CHECK(searchErrorMessage(err, (substring))); \ +} while(0) + +BOOST_AUTO_TEST_SUITE(YulParser) + +BOOST_AUTO_TEST_CASE(smoke_test) +{ + BOOST_CHECK(successParse("{ }")); +} + +BOOST_AUTO_TEST_CASE(vardecl) +{ + BOOST_CHECK(successParse("{ let x:u256 := 7:u256 }")); +} + +BOOST_AUTO_TEST_CASE(vardecl_bool) +{ + BOOST_CHECK(successParse("{ let x:bool := true:bool }")); + BOOST_CHECK(successParse("{ let x:bool := false:bool }")); +} + +BOOST_AUTO_TEST_CASE(vardecl_empty) +{ + BOOST_CHECK(successParse("{ let x:u256 }")); +} + +BOOST_AUTO_TEST_CASE(assignment) +{ + BOOST_CHECK(successParse("{ let x:u256 := 2:u256 let y:u256 := x }")); +} + +BOOST_AUTO_TEST_CASE(vardecl_complex) +{ + BOOST_CHECK(successParse("{ function add(a:u256, b:u256) -> c:u256 {} let y:u256 := 2:u256 let x:u256 := add(7:u256, add(6:u256, y)) }")); +} + +BOOST_AUTO_TEST_CASE(blocks) +{ + BOOST_CHECK(successParse("{ let x:u256 := 7:u256 { let y:u256 := 3:u256 } { let z:u256 := 2:u256 } }")); +} + +BOOST_AUTO_TEST_CASE(function_definitions) +{ + BOOST_CHECK(successParse("{ function f() { } function g(a:u256) -> x:u256 { } }")); +} + +BOOST_AUTO_TEST_CASE(function_definitions_multiple_args) +{ + BOOST_CHECK(successParse("{ function f(a:u256, d:u256) { } function g(a:u256, d:u256) -> x:u256, y:u256 { } }")); +} + +BOOST_AUTO_TEST_CASE(function_calls) +{ + BOOST_CHECK(successParse("{ function f(a:u256) -> b:u256 {} function g(a:u256, b:u256, c:u256) {} function x() { g(1:u256, 2:u256, f(3:u256)) x() } }")); +} + +BOOST_AUTO_TEST_CASE(tuple_assignment) +{ + BOOST_CHECK(successParse("{ function f() -> a:u256, b:u256, c:u256 {} let x:u256, y:u256, z:u256 := f() }")); +} + +BOOST_AUTO_TEST_CASE(label) +{ + CHECK_ERROR("{ label: }", ParserError, "Labels are not supported."); +} + +BOOST_AUTO_TEST_CASE(instructions) +{ + CHECK_ERROR("{ pop }", ParserError, "Call or assignment expected."); +} + +BOOST_AUTO_TEST_CASE(push) +{ + CHECK_ERROR("{ 0x42:u256 }", ParserError, "Call or assignment expected."); +} + +BOOST_AUTO_TEST_CASE(assign_from_stack) +{ + CHECK_ERROR("{ =: x:u256 }", ParserError, "Literal or identifier expected."); +} + +BOOST_AUTO_TEST_CASE(empty_call) +{ + CHECK_ERROR("{ () }", ParserError, "Literal or identifier expected."); +} + +BOOST_AUTO_TEST_CASE(tokens_as_identifers) +{ + BOOST_CHECK(successParse("{ let return:u256 := 1:u256 }")); + BOOST_CHECK(successParse("{ let byte:u256 := 1:u256 }")); + BOOST_CHECK(successParse("{ let address:u256 := 1:u256 }")); + BOOST_CHECK(successParse("{ let bool:u256 := 1:u256 }")); +} + +BOOST_AUTO_TEST_CASE(lacking_types) +{ + CHECK_ERROR("{ let x := 1:u256 }", ParserError, "Expected identifier but got '='"); + CHECK_ERROR("{ let x:u256 := 1 }", ParserError, "Expected ':' but got '}'"); + CHECK_ERROR("{ function f(a) {} }", ParserError, "Expected ':' but got ')'"); + CHECK_ERROR("{ function f(a:u256) -> b {} }", ParserError, "Expected ':' but got '{'"); +} + +BOOST_AUTO_TEST_CASE(invalid_types) +{ + /// testing invalid literal + /// NOTE: these will need to change when types are compared + CHECK_ERROR("{ let x:bool := 1:invalid }", TypeError, "\"invalid\" is not a valid type (user defined types are not yet supported)."); + /// testing invalid variable declaration + CHECK_ERROR("{ let x:invalid := 1:bool }", TypeError, "\"invalid\" is not a valid type (user defined types are not yet supported)."); + CHECK_ERROR("{ function f(a:invalid) {} }", TypeError, "\"invalid\" is not a valid type (user defined types are not yet supported)."); +} + +BOOST_AUTO_TEST_CASE(number_literals) +{ + BOOST_CHECK(successParse("{ let x:u256 := 1:u256 }")); + CHECK_ERROR("{ let x:u256 := .1:u256 }", ParserError, "Invalid number literal."); + CHECK_ERROR("{ let x:u256 := 1e5:u256 }", ParserError, "Invalid number literal."); + CHECK_ERROR("{ let x:u256 := 67.235:u256 }", ParserError, "Invalid number literal."); + CHECK_ERROR("{ let x:u256 := 0x1ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff:u256 }", TypeError, "Number literal too large (> 256 bits)"); +} + +BOOST_AUTO_TEST_CASE(builtin_types) +{ + BOOST_CHECK(successParse("{ let x:bool := true:bool }")); + BOOST_CHECK(successParse("{ let x:u8 := 1:u8 }")); + BOOST_CHECK(successParse("{ let x:s8 := 1:u8 }")); + BOOST_CHECK(successParse("{ let x:u32 := 1:u32 }")); + BOOST_CHECK(successParse("{ let x:s32 := 1:s32 }")); + BOOST_CHECK(successParse("{ let x:u64 := 1:u64 }")); + BOOST_CHECK(successParse("{ let x:s64 := 1:s64 }")); + BOOST_CHECK(successParse("{ let x:u128 := 1:u128 }")); + BOOST_CHECK(successParse("{ let x:s128 := 1:s128 }")); + BOOST_CHECK(successParse("{ let x:u256 := 1:u256 }")); + BOOST_CHECK(successParse("{ let x:s256 := 1:s256 }")); +} + +BOOST_AUTO_TEST_CASE(recursion_depth) +{ + string input; + for (size_t i = 0; i < 20000; i++) + input += "{"; + input += "let x:u256 := 0:u256"; + for (size_t i = 0; i < 20000; i++) + input += "}"; + + CHECK_ERROR(input, ParserError, "recursion"); +} + +BOOST_AUTO_TEST_CASE(multiple_assignment) +{ + CHECK_ERROR("{ let x:u256 function f() -> a:u256, b:u256 {} 123:u256, x := f() }", ParserError, "Label name / variable name must precede \",\" (multiple assignment)."); + CHECK_ERROR("{ let x:u256 function f() -> a:u256, b:u256 {} x, 123:u256 := f() }", ParserError, "Variable name expected in multiple assignment."); + + /// NOTE: Travis hiccups if not having a variable + char const* text = R"( + { + function f(a:u256) -> r1:u256, r2:u256 { + r1 := a + r2 := 7:u256 + } + let x:u256 := 9:u256 + let y:u256 := 2:u256 + x, y := f(x) + } + )"; + BOOST_CHECK(successParse(text)); +} + +BOOST_AUTO_TEST_CASE(if_statement) +{ + BOOST_CHECK(successParse("{ if true:bool {} }")); + BOOST_CHECK(successParse("{ if false:bool { let x:u256 := 3:u256 } }")); + BOOST_CHECK(successParse("{ function f() -> x:bool {} if f() { let b:bool := f() } }")); +} + +BOOST_AUTO_TEST_CASE(if_statement_invalid) +{ + CHECK_ERROR("{ if let x:u256 {} }", ParserError, "Literal or identifier expected."); + CHECK_ERROR("{ if true:bool let x:u256 := 3:u256 }", ParserError, "Expected '{' but got reserved keyword 'let'"); + // TODO change this to an error once we check types. + BOOST_CHECK(successParse("{ if 42:u256 { } }")); +} + +BOOST_AUTO_TEST_SUITE_END() + +} +} +} // end namespaces diff --git a/test/libyul/YulOptimizerTest.cpp b/test/libyul/YulOptimizerTest.cpp new file mode 100644 index 00000000..03cd6446 --- /dev/null +++ b/test/libyul/YulOptimizerTest.cpp @@ -0,0 +1,295 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <test/libyul/YulOptimizerTest.h> + +#include <test/libsolidity/FormattedScope.h> + +#include <test/Options.h> + +#include <libyul/optimiser/BlockFlattener.h> +#include <libyul/optimiser/VarDeclPropagator.h> +#include <libyul/optimiser/Disambiguator.h> +#include <libyul/optimiser/CommonSubexpressionEliminator.h> +#include <libyul/optimiser/NameCollector.h> +#include <libyul/optimiser/ExpressionSplitter.h> +#include <libyul/optimiser/FunctionGrouper.h> +#include <libyul/optimiser/FunctionHoister.h> +#include <libyul/optimiser/ExpressionInliner.h> +#include <libyul/optimiser/FullInliner.h> +#include <libyul/optimiser/MainFunction.h> +#include <libyul/optimiser/Rematerialiser.h> +#include <libyul/optimiser/ExpressionSimplifier.h> +#include <libyul/optimiser/UnusedPruner.h> +#include <libyul/optimiser/ExpressionJoiner.h> +#include <libyul/optimiser/SSATransform.h> +#include <libyul/optimiser/RedundantAssignEliminator.h> +#include <libyul/optimiser/Suite.h> + +#include <libsolidity/parsing/Scanner.h> +#include <libsolidity/inlineasm/AsmPrinter.h> +#include <libsolidity/inlineasm/AsmParser.h> +#include <libsolidity/inlineasm/AsmAnalysis.h> +#include <libsolidity/interface/SourceReferenceFormatter.h> +#include <libsolidity/interface/ErrorReporter.h> + +#include <boost/test/unit_test.hpp> +#include <boost/algorithm/string.hpp> + +#include <fstream> + +using namespace dev; +using namespace dev::yul; +using namespace dev::yul::test; +using namespace dev::solidity; +using namespace dev::solidity::test; +using namespace std; + +YulOptimizerTest::YulOptimizerTest(string const& _filename) +{ + boost::filesystem::path path(_filename); + + if (path.empty() || std::next(path.begin()) == path.end() || std::next(std::next(path.begin())) == path.end()) + BOOST_THROW_EXCEPTION(runtime_error("Filename path has to contain a directory: \"" + _filename + "\".")); + m_optimizerStep = std::prev(std::prev(path.end()))->string(); + + ifstream file(_filename); + if (!file) + BOOST_THROW_EXCEPTION(runtime_error("Cannot open test case: \"" + _filename + "\".")); + file.exceptions(ios::badbit); + + string line; + while (getline(file, line)) + { + if (boost::algorithm::starts_with(line, "// ----")) + break; + if (m_source.empty() && boost::algorithm::starts_with(line, "// yul")) + m_yul = true; + m_source += line + "\n"; + } + while (getline(file, line)) + if (boost::algorithm::starts_with(line, "// ")) + m_expectation += line.substr(3) + "\n"; + else + m_expectation += line + "\n"; +} + +bool YulOptimizerTest::run(ostream& _stream, string const& _linePrefix, bool const _formatted) +{ + assembly::AsmPrinter printer{m_yul}; + shared_ptr<Block> ast; + shared_ptr<assembly::AsmAnalysisInfo> analysisInfo; + if (!parse(_stream, _linePrefix, _formatted)) + return false; + + if (m_optimizerStep == "disambiguator") + disambiguate(); + else if (m_optimizerStep == "blockFlattener") + { + disambiguate(); + BlockFlattener{}(*m_ast); + } + else if (m_optimizerStep == "varDeclPropagator") + { + disambiguate(); + VarDeclPropagator{}(*m_ast); + } + else if (m_optimizerStep == "commonSubexpressionEliminator") + { + disambiguate(); + (CommonSubexpressionEliminator{})(*m_ast); + } + else if (m_optimizerStep == "expressionSplitter") + { + NameDispenser nameDispenser(*m_ast); + ExpressionSplitter{nameDispenser}(*m_ast); + } + else if (m_optimizerStep == "expressionJoiner") + { + disambiguate(); + ExpressionJoiner::run(*m_ast); + } + else if (m_optimizerStep == "splitJoin") + { + disambiguate(); + NameDispenser nameDispenser(*m_ast); + ExpressionSplitter{nameDispenser}(*m_ast); + ExpressionJoiner::run(*m_ast); + ExpressionJoiner::run(*m_ast); + } + else if (m_optimizerStep == "functionGrouper") + { + disambiguate(); + (FunctionGrouper{})(*m_ast); + } + else if (m_optimizerStep == "functionHoister") + { + disambiguate(); + (FunctionHoister{})(*m_ast); + } + else if (m_optimizerStep == "expressionInliner") + { + disambiguate(); + ExpressionInliner(*m_ast).run(); + } + else if (m_optimizerStep == "fullInliner") + { + disambiguate(); + (FunctionHoister{})(*m_ast); + (FunctionGrouper{})(*m_ast); + NameDispenser nameDispenser(*m_ast); + ExpressionSplitter{nameDispenser}(*m_ast); + FullInliner(*m_ast, nameDispenser).run(); + ExpressionJoiner::run(*m_ast); + } + else if (m_optimizerStep == "mainFunction") + { + disambiguate(); + (FunctionGrouper{})(*m_ast); + (MainFunction{})(*m_ast); + } + else if (m_optimizerStep == "rematerialiser") + { + disambiguate(); + (Rematerialiser{})(*m_ast); + } + else if (m_optimizerStep == "expressionSimplifier") + { + disambiguate(); + ExpressionSimplifier::run(*m_ast); + } + else if (m_optimizerStep == "fullSimplify") + { + disambiguate(); + NameDispenser nameDispenser(*m_ast); + ExpressionSplitter{nameDispenser}(*m_ast); + CommonSubexpressionEliminator{}(*m_ast); + ExpressionSimplifier::run(*m_ast); + UnusedPruner::runUntilStabilised(*m_ast); + ExpressionJoiner::run(*m_ast); + ExpressionJoiner::run(*m_ast); + } + else if (m_optimizerStep == "unusedPruner") + { + disambiguate(); + UnusedPruner::runUntilStabilised(*m_ast); + } + else if (m_optimizerStep == "ssaTransform") + { + disambiguate(); + NameDispenser nameDispenser(*m_ast); + SSATransform::run(*m_ast, nameDispenser); + } + else if (m_optimizerStep == "redundantAssignEliminator") + { + disambiguate(); + RedundantAssignEliminator::run(*m_ast); + } + else if (m_optimizerStep == "ssaPlusCleanup") + { + disambiguate(); + NameDispenser nameDispenser(*m_ast); + SSATransform::run(*m_ast, nameDispenser); + RedundantAssignEliminator::run(*m_ast); + } + else if (m_optimizerStep == "fullSuite") + OptimiserSuite::run(*m_ast, *m_analysisInfo); + else + { + FormattedScope(_stream, _formatted, {formatting::BOLD, formatting::RED}) << _linePrefix << "Invalid optimizer step: " << m_optimizerStep << endl; + return false; + } + + m_obtainedResult = m_optimizerStep + "\n" + printer(*m_ast) + "\n"; + + if (m_expectation != m_obtainedResult) + { + string nextIndentLevel = _linePrefix + " "; + FormattedScope(_stream, _formatted, {formatting::BOLD, formatting::CYAN}) << _linePrefix << "Expected result:" << endl; + // TODO could compute a simple diff with highlighted lines + printIndented(_stream, m_expectation, nextIndentLevel); + FormattedScope(_stream, _formatted, {formatting::BOLD, formatting::CYAN}) << _linePrefix << "Obtained result:" << endl; + printIndented(_stream, m_obtainedResult, nextIndentLevel); + return false; + } + return true; +} + +void YulOptimizerTest::printSource(ostream& _stream, string const& _linePrefix, bool const) const +{ + printIndented(_stream, m_source, _linePrefix); +} + +void YulOptimizerTest::printUpdatedExpectations(ostream& _stream, string const& _linePrefix) const +{ + printIndented(_stream, m_obtainedResult, _linePrefix); +} + +void YulOptimizerTest::printIndented(ostream& _stream, string const& _output, string const& _linePrefix) const +{ + stringstream output(_output); + string line; + while (getline(output, line)) + _stream << _linePrefix << line << endl; +} + +bool YulOptimizerTest::parse(ostream& _stream, string const& _linePrefix, bool const _formatted) +{ + assembly::AsmFlavour flavour = m_yul ? assembly::AsmFlavour::Yul : assembly::AsmFlavour::Strict; + ErrorList errors; + ErrorReporter errorReporter(errors); + shared_ptr<Scanner> scanner = make_shared<Scanner>(CharStream(m_source), ""); + m_ast = assembly::Parser(errorReporter, flavour).parse(scanner, false); + if (!m_ast || !errorReporter.errors().empty()) + { + FormattedScope(_stream, _formatted, {formatting::BOLD, formatting::RED}) << _linePrefix << "Error parsing source." << endl; + printErrors(_stream, errorReporter.errors(), *scanner); + return false; + } + m_analysisInfo = make_shared<assembly::AsmAnalysisInfo>(); + assembly::AsmAnalyzer analyzer( + *m_analysisInfo, + errorReporter, + dev::test::Options::get().evmVersion(), + boost::none, + flavour + ); + if (!analyzer.analyze(*m_ast) || !errorReporter.errors().empty()) + { + FormattedScope(_stream, _formatted, {formatting::BOLD, formatting::RED}) << _linePrefix << "Error analyzing source." << endl; + printErrors(_stream, errorReporter.errors(), *scanner); + return false; + } + return true; +} + +void YulOptimizerTest::disambiguate() +{ + *m_ast = boost::get<Block>(Disambiguator(*m_analysisInfo)(*m_ast)); + m_analysisInfo.reset(); +} + +void YulOptimizerTest::printErrors(ostream& _stream, ErrorList const& _errors, Scanner const& _scanner) +{ + SourceReferenceFormatter formatter(_stream, [&](string const&) -> Scanner const& { return _scanner; }); + + for (auto const& error: _errors) + formatter.printExceptionInformation( + *error, + (error->type() == Error::Type::Warning) ? "Warning" : "Error" + ); +} diff --git a/test/libyul/YulOptimizerTest.h b/test/libyul/YulOptimizerTest.h new file mode 100644 index 00000000..7db17ceb --- /dev/null +++ b/test/libyul/YulOptimizerTest.h @@ -0,0 +1,75 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see <http://www.gnu.org/licenses/>. +*/ + +#pragma once + +#include <test/libsolidity/TestCase.h> + + +namespace dev +{ +namespace solidity +{ +class Scanner; +class Error; +using ErrorList = std::vector<std::shared_ptr<Error const>>; +namespace assembly +{ +struct AsmAnalysisInfo; +struct Block; +} +} +namespace yul +{ +namespace test +{ + +class YulOptimizerTest: public solidity::test::TestCase +{ +public: + static std::unique_ptr<TestCase> create(std::string const& _filename) + { + return std::unique_ptr<TestCase>(new YulOptimizerTest(_filename)); + } + + explicit YulOptimizerTest(std::string const& _filename); + + bool run(std::ostream& _stream, std::string const& _linePrefix = "", bool const _formatted = false) override; + + void printSource(std::ostream& _stream, std::string const &_linePrefix = "", bool const _formatted = false) const override; + void printUpdatedExpectations(std::ostream& _stream, std::string const& _linePrefix) const override; + +private: + void printIndented(std::ostream& _stream, std::string const& _output, std::string const& _linePrefix = "") const; + bool parse(std::ostream& _stream, std::string const& _linePrefix, bool const _formatted); + void disambiguate(); + + static void printErrors(std::ostream& _stream, solidity::ErrorList const& _errors, solidity::Scanner const& _scanner); + + std::string m_source; + bool m_yul = false; + std::string m_optimizerStep; + std::string m_expectation; + + std::shared_ptr<solidity::assembly::Block> m_ast; + std::shared_ptr<solidity::assembly::AsmAnalysisInfo> m_analysisInfo; + std::string m_obtainedResult; +}; + +} +} +} diff --git a/test/libyul/yulOptimizerTests/blockFlattener/basic.yul b/test/libyul/yulOptimizerTests/blockFlattener/basic.yul new file mode 100644 index 00000000..adcaedd0 --- /dev/null +++ b/test/libyul/yulOptimizerTests/blockFlattener/basic.yul @@ -0,0 +1,20 @@ +{ + let _1 := mload(0) + let f_a := mload(1) + let f_r + { + f_a := mload(f_a) + f_r := add(f_a, calldatasize()) + } + let z := mload(2) +} +// ---- +// blockFlattener +// { +// let _1 := mload(0) +// let f_a := mload(1) +// let f_r +// f_a := mload(f_a) +// f_r := add(f_a, calldatasize()) +// let z := mload(2) +// } diff --git a/test/libyul/yulOptimizerTests/blockFlattener/for_stmt.yul b/test/libyul/yulOptimizerTests/blockFlattener/for_stmt.yul new file mode 100644 index 00000000..07bd5c18 --- /dev/null +++ b/test/libyul/yulOptimizerTests/blockFlattener/for_stmt.yul @@ -0,0 +1,19 @@ +{ + for { let a := 1 } iszero(eq(a, 10)) { a := add(a, 1) } { + a := add(a, 1) + } +} +// ---- +// blockFlattener +// { +// for { +// let a := 1 +// } +// iszero(eq(a, 10)) +// { +// a := add(a, 1) +// } +// { +// a := add(a, 1) +// } +// } diff --git a/test/libyul/yulOptimizerTests/blockFlattener/if_stmt.yul b/test/libyul/yulOptimizerTests/blockFlattener/if_stmt.yul new file mode 100644 index 00000000..4d6ccf0e --- /dev/null +++ b/test/libyul/yulOptimizerTests/blockFlattener/if_stmt.yul @@ -0,0 +1,20 @@ +{ + if add(mload(7), sload(mload(3))) + { + let y := add(mload(3), 3) + { + y := add(y, 7) + } + } + let t := add(3, 9) +} +// ---- +// blockFlattener +// { +// if add(mload(7), sload(mload(3))) +// { +// let y := add(mload(3), 3) +// y := add(y, 7) +// } +// let t := add(3, 9) +// } diff --git a/test/libyul/yulOptimizerTests/blockFlattener/many_nested_blocks.yul b/test/libyul/yulOptimizerTests/blockFlattener/many_nested_blocks.yul new file mode 100644 index 00000000..ae2a066b --- /dev/null +++ b/test/libyul/yulOptimizerTests/blockFlattener/many_nested_blocks.yul @@ -0,0 +1,28 @@ +{ + let a := 3 + let b := 4 + { + a := add(b, 3) + let c := 5 + { + b := add(b, 4) + { + c := add(a, 5) + } + b := add(a, b) + } + a := add(a, c) + } +} +// ---- +// blockFlattener +// { +// let a := 3 +// let b := 4 +// a := add(b, 3) +// let c := 5 +// b := add(b, 4) +// c := add(a, 5) +// b := add(a, b) +// a := add(a, c) +// } diff --git a/test/libyul/yulOptimizerTests/blockFlattener/switch_stmt.yul b/test/libyul/yulOptimizerTests/blockFlattener/switch_stmt.yul new file mode 100644 index 00000000..2df4f9d0 --- /dev/null +++ b/test/libyul/yulOptimizerTests/blockFlattener/switch_stmt.yul @@ -0,0 +1,22 @@ +{ + let a := 1 + switch calldataload(0) + case 0 { { { mstore(0, 1) } } a := 8 } + default { a := 3 { a := 4 } } + a := 5 +} +// ---- +// blockFlattener +// { +// let a := 1 +// switch calldataload(0) +// case 0 { +// mstore(0, 1) +// a := 8 +// } +// default { +// a := 3 +// a := 4 +// } +// a := 5 +// } diff --git a/test/libyul/yulOptimizerTests/commonSubexpressionEliminator/branches_for.yul b/test/libyul/yulOptimizerTests/commonSubexpressionEliminator/branches_for.yul new file mode 100644 index 00000000..c59bced7 --- /dev/null +++ b/test/libyul/yulOptimizerTests/commonSubexpressionEliminator/branches_for.yul @@ -0,0 +1,24 @@ +{ + let a := 1 let b := codesize() + for { } lt(1, codesize()) { mstore(1, codesize()) a := add(a, codesize()) } { + mstore(1, codesize()) + } + mstore(1, codesize()) +} +// ---- +// commonSubexpressionEliminator +// { +// let a := 1 +// let b := codesize() +// for { +// } +// lt(1, b) +// { +// mstore(1, b) +// a := add(a, b) +// } +// { +// mstore(1, b) +// } +// mstore(1, b) +// } diff --git a/test/libyul/yulOptimizerTests/commonSubexpressionEliminator/branches_if.yul b/test/libyul/yulOptimizerTests/commonSubexpressionEliminator/branches_if.yul new file mode 100644 index 00000000..5b8200d9 --- /dev/null +++ b/test/libyul/yulOptimizerTests/commonSubexpressionEliminator/branches_if.yul @@ -0,0 +1,15 @@ +{ + let b := 1 + if b { b := 1 } + let c := 1 +} +// ---- +// commonSubexpressionEliminator +// { +// let b := 1 +// if b +// { +// b := b +// } +// let c := 1 +// } diff --git a/test/libyul/yulOptimizerTests/commonSubexpressionEliminator/case2.yul b/test/libyul/yulOptimizerTests/commonSubexpressionEliminator/case2.yul new file mode 100644 index 00000000..fd8b4bc8 --- /dev/null +++ b/test/libyul/yulOptimizerTests/commonSubexpressionEliminator/case2.yul @@ -0,0 +1,52 @@ +{ + let _13 := 0x20 + let _14 := allocate(_13) + pop(_14) + let _15 := 2 + let _16 := 3 + let _17 := 0x40 + let _18 := allocate(_17) + let _19 := array_index_access(_18, _16) + mstore(_19, _15) + function allocate(size) -> p + { + let _1 := 0x40 + let p_2 := mload(_1) + p := p_2 + let _20 := add(p_2, size) + mstore(_1, _20) + } + function array_index_access(array, index) -> p_1 + { + let _21 := 0x20 + let _22 := mul(index, _21) + p_1 := add(array, _22) + } +} +// ---- +// commonSubexpressionEliminator +// { +// let _13 := 0x20 +// let _14 := allocate(_13) +// pop(_14) +// let _15 := 2 +// let _16 := 3 +// let _17 := 0x40 +// let _18 := allocate(_17) +// let _19 := array_index_access(_18, _16) +// mstore(_19, _15) +// function allocate(size) -> p +// { +// let _1 := 0x40 +// let p_2 := mload(_1) +// p := p_2 +// let _20 := add(p_2, size) +// mstore(_1, _20) +// } +// function array_index_access(array, index) -> p_1 +// { +// let _21 := 0x20 +// let _22 := mul(index, _21) +// p_1 := add(array, _22) +// } +// } diff --git a/test/libyul/yulOptimizerTests/commonSubexpressionEliminator/function_scopes.yul b/test/libyul/yulOptimizerTests/commonSubexpressionEliminator/function_scopes.yul new file mode 100644 index 00000000..28e840cf --- /dev/null +++ b/test/libyul/yulOptimizerTests/commonSubexpressionEliminator/function_scopes.yul @@ -0,0 +1,52 @@ +{ + function allocate(size) -> p + { + let _1 := 0x40 + p := mload(_1) + let _2 := add(p, size) + let _3 := 0x40 + mstore(_3, _2) + } + function array_index_access(array, index) -> p_1 + { + let _4 := 0x20 + let _5 := mul(index, _4) + p_1 := add(array, _5) + } + let _6 := 0x20 + let _7 := allocate(_6) + pop(_7) + let _8 := 0x40 + let x := allocate(_8) + let _9 := 2 + let _10 := 3 + let _11 := array_index_access(x, _10) + mstore(_11, _9) +} +// ---- +// commonSubexpressionEliminator +// { +// function allocate(size) -> p +// { +// let _1 := 0x40 +// p := mload(_1) +// let _2 := add(p, size) +// let _3 := _1 +// mstore(_1, _2) +// } +// function array_index_access(array, index) -> p_1 +// { +// let _4 := 0x20 +// let _5 := mul(index, _4) +// p_1 := add(array, _5) +// } +// let _6 := 0x20 +// let _7 := allocate(_6) +// pop(_7) +// let _8 := 0x40 +// let x := allocate(_8) +// let _9 := 2 +// let _10 := 3 +// let _11 := array_index_access(x, _10) +// mstore(_11, _9) +// } diff --git a/test/libyul/yulOptimizerTests/commonSubexpressionEliminator/non_movable_instr.yul b/test/libyul/yulOptimizerTests/commonSubexpressionEliminator/non_movable_instr.yul new file mode 100644 index 00000000..cb0ca38d --- /dev/null +++ b/test/libyul/yulOptimizerTests/commonSubexpressionEliminator/non_movable_instr.yul @@ -0,0 +1,10 @@ +{ + let a := mload(1) + let b := mload(1) +} +// ---- +// commonSubexpressionEliminator +// { +// let a := mload(1) +// let b := mload(1) +// } diff --git a/test/libyul/yulOptimizerTests/commonSubexpressionEliminator/non_movable_instr2.yul b/test/libyul/yulOptimizerTests/commonSubexpressionEliminator/non_movable_instr2.yul new file mode 100644 index 00000000..ebc17446 --- /dev/null +++ b/test/libyul/yulOptimizerTests/commonSubexpressionEliminator/non_movable_instr2.yul @@ -0,0 +1,10 @@ +{ + let a := gas() + let b := gas() +} +// ---- +// commonSubexpressionEliminator +// { +// let a := gas() +// let b := gas() +// } diff --git a/test/libyul/yulOptimizerTests/commonSubexpressionEliminator/scopes.yul b/test/libyul/yulOptimizerTests/commonSubexpressionEliminator/scopes.yul new file mode 100644 index 00000000..49b4c916 --- /dev/null +++ b/test/libyul/yulOptimizerTests/commonSubexpressionEliminator/scopes.yul @@ -0,0 +1,25 @@ +{ + let a := 10 + let x := 20 + { + let b := calldataload(0) + let d := calldataload(1) + x := d + } + // We had a bug where "calldataload(0)" was incorrectly replaced by "b" + mstore(0, calldataload(0)) + mstore(0, x) +} +// ---- +// commonSubexpressionEliminator +// { +// let a := 10 +// let x := 20 +// { +// let b := calldataload(0) +// let d := calldataload(1) +// x := d +// } +// mstore(0, calldataload(0)) +// mstore(0, x) +// } diff --git a/test/libyul/yulOptimizerTests/commonSubexpressionEliminator/smoke.yul b/test/libyul/yulOptimizerTests/commonSubexpressionEliminator/smoke.yul new file mode 100644 index 00000000..b9457229 --- /dev/null +++ b/test/libyul/yulOptimizerTests/commonSubexpressionEliminator/smoke.yul @@ -0,0 +1,5 @@ +{ } +// ---- +// commonSubexpressionEliminator +// { +// } diff --git a/test/libyul/yulOptimizerTests/commonSubexpressionEliminator/trivial.yul b/test/libyul/yulOptimizerTests/commonSubexpressionEliminator/trivial.yul new file mode 100644 index 00000000..684272f5 --- /dev/null +++ b/test/libyul/yulOptimizerTests/commonSubexpressionEliminator/trivial.yul @@ -0,0 +1,10 @@ +{ + let a := mul(1, codesize()) + let b := mul(1, codesize()) +} +// ---- +// commonSubexpressionEliminator +// { +// let a := mul(1, codesize()) +// let b := a +// } diff --git a/test/libyul/yulOptimizerTests/commonSubexpressionEliminator/variable_for_variable.yul b/test/libyul/yulOptimizerTests/commonSubexpressionEliminator/variable_for_variable.yul new file mode 100644 index 00000000..ab94afc2 --- /dev/null +++ b/test/libyul/yulOptimizerTests/commonSubexpressionEliminator/variable_for_variable.yul @@ -0,0 +1,27 @@ +{ + + let a := mload(0) + let b := add(a, 7) + let c := a + let d := c + let x := add(a, b) + // CSE has to recognize equality with x here. + let y := add(d, add(c, 7)) + // some reassignments + b := mload(a) + a := b + mstore(2, a) +} +// ---- +// commonSubexpressionEliminator +// { +// let a := mload(0) +// let b := add(a, 7) +// let c := a +// let d := a +// let x := add(a, b) +// let y := x +// b := mload(a) +// a := b +// mstore(2, b) +// } diff --git a/test/libyul/yulOptimizerTests/disambiguator/for_statement.yul b/test/libyul/yulOptimizerTests/disambiguator/for_statement.yul new file mode 100644 index 00000000..6875abec --- /dev/null +++ b/test/libyul/yulOptimizerTests/disambiguator/for_statement.yul @@ -0,0 +1,28 @@ +// yul +{ + { let a:u256, b:u256 } + { + for { let a:u256 } a { a := a } { + let b:u256 := a + } + } +} +// ---- +// disambiguator +// { +// { +// let a:u256, b:u256 +// } +// { +// for { +// let a_1:u256 +// } +// a_1 +// { +// a_1 := a_1 +// } +// { +// let b_2:u256 := a_1 +// } +// } +// } diff --git a/test/libyul/yulOptimizerTests/disambiguator/funtion_call.yul b/test/libyul/yulOptimizerTests/disambiguator/funtion_call.yul new file mode 100644 index 00000000..df49b92a --- /dev/null +++ b/test/libyul/yulOptimizerTests/disambiguator/funtion_call.yul @@ -0,0 +1,22 @@ +// yul +{ + { let a:u256, b:u256, c:u256, d:u256, f:u256 } + { + function f(a:u256) -> c:u256, d:u256 { + let b:u256, c_1:u256 := f(a) + } + } +} +// ---- +// disambiguator +// { +// { +// let a:u256, b:u256, c:u256, d:u256, f:u256 +// } +// { +// function f_1(a_2:u256) -> c_3:u256, d_4:u256 +// { +// let b_5:u256, c_1:u256 := f_1(a_2) +// } +// } +// } diff --git a/test/libyul/yulOptimizerTests/disambiguator/if_statement.yul b/test/libyul/yulOptimizerTests/disambiguator/if_statement.yul new file mode 100644 index 00000000..bc3aa30f --- /dev/null +++ b/test/libyul/yulOptimizerTests/disambiguator/if_statement.yul @@ -0,0 +1,22 @@ +// yul +{ + { let a:u256, b:u256, c:u256 } + { + let a:bool + if a { let b:bool := a } + } +} +// ---- +// disambiguator +// { +// { +// let a:u256, b:u256, c:u256 +// } +// { +// let a_1:bool +// if a_1 +// { +// let b_2:bool := a_1 +// } +// } +// } diff --git a/test/libyul/yulOptimizerTests/disambiguator/long_names.yul b/test/libyul/yulOptimizerTests/disambiguator/long_names.yul new file mode 100644 index 00000000..933e1e8f --- /dev/null +++ b/test/libyul/yulOptimizerTests/disambiguator/long_names.yul @@ -0,0 +1,12 @@ +// yul +{ { let aanteuhdaoneudbrgkjiuaothduiathudaoeuh:u256 } { let aanteuhdaoneudbrgkjiuaothduiathudaoeuh:u256 } } +// ---- +// disambiguator +// { +// { +// let aanteuhdaoneudbrgkjiuaothduiathudaoeuh:u256 +// } +// { +// let aanteuhdaoneudbrgkjiuaothduiathudaoeuh_1:u256 +// } +// } diff --git a/test/libyul/yulOptimizerTests/disambiguator/smoke.yul b/test/libyul/yulOptimizerTests/disambiguator/smoke.yul new file mode 100644 index 00000000..d6cd8a61 --- /dev/null +++ b/test/libyul/yulOptimizerTests/disambiguator/smoke.yul @@ -0,0 +1,5 @@ +{ } +// ---- +// disambiguator +// { +// } diff --git a/test/libyul/yulOptimizerTests/disambiguator/smoke_yul.yul b/test/libyul/yulOptimizerTests/disambiguator/smoke_yul.yul new file mode 100644 index 00000000..e55f4cd3 --- /dev/null +++ b/test/libyul/yulOptimizerTests/disambiguator/smoke_yul.yul @@ -0,0 +1,6 @@ +// yul +{ } +// ---- +// disambiguator +// { +// } diff --git a/test/libyul/yulOptimizerTests/disambiguator/switch_statement.yul b/test/libyul/yulOptimizerTests/disambiguator/switch_statement.yul new file mode 100644 index 00000000..e62e957f --- /dev/null +++ b/test/libyul/yulOptimizerTests/disambiguator/switch_statement.yul @@ -0,0 +1,27 @@ +// yul +{ + { let a:u256, b:u256, c:u256 } + { + let a:u256 + switch a + case 0:u256 { let b:u256 := a } + default { let c:u256 := a } + } +} +// ---- +// disambiguator +// { +// { +// let a:u256, b:u256, c:u256 +// } +// { +// let a_1:u256 +// switch a_1 +// case 0:u256 { +// let b_2:u256 := a_1 +// } +// default { +// let c_3:u256 := a_1 +// } +// } +// } diff --git a/test/libyul/yulOptimizerTests/disambiguator/variables.yul b/test/libyul/yulOptimizerTests/disambiguator/variables.yul new file mode 100644 index 00000000..65bd4c8f --- /dev/null +++ b/test/libyul/yulOptimizerTests/disambiguator/variables.yul @@ -0,0 +1,12 @@ +// yul +{ { let a:u256 } { let a:u256 } } +// ---- +// disambiguator +// { +// { +// let a:u256 +// } +// { +// let a_1:u256 +// } +// } diff --git a/test/libyul/yulOptimizerTests/disambiguator/variables_clash.yul b/test/libyul/yulOptimizerTests/disambiguator/variables_clash.yul new file mode 100644 index 00000000..e462442a --- /dev/null +++ b/test/libyul/yulOptimizerTests/disambiguator/variables_clash.yul @@ -0,0 +1,13 @@ +// yul +{ { let a:u256 let a_1:u256 } { let a:u256 } } +// ---- +// disambiguator +// { +// { +// let a:u256 +// let a_1:u256 +// } +// { +// let a_2:u256 +// } +// } diff --git a/test/libyul/yulOptimizerTests/disambiguator/variables_inside_functions.yul b/test/libyul/yulOptimizerTests/disambiguator/variables_inside_functions.yul new file mode 100644 index 00000000..839692bc --- /dev/null +++ b/test/libyul/yulOptimizerTests/disambiguator/variables_inside_functions.yul @@ -0,0 +1,24 @@ +// yul +{ + { let c:u256 let b:u256 } + function f(a:u256, c:u256) -> b:u256 { let x:u256 } + { + let a:u256 let x:u256 + } +} +// ---- +// disambiguator +// { +// { +// let c:u256 +// let b:u256 +// } +// function f(a:u256, c_1:u256) -> b_2:u256 +// { +// let x:u256 +// } +// { +// let a_3:u256 +// let x_4:u256 +// } +// } diff --git a/test/libyul/yulOptimizerTests/expressionInliner/complex_with_evm.yul b/test/libyul/yulOptimizerTests/expressionInliner/complex_with_evm.yul new file mode 100644 index 00000000..519a2af8 --- /dev/null +++ b/test/libyul/yulOptimizerTests/expressionInliner/complex_with_evm.yul @@ -0,0 +1,13 @@ +{ + function f(a) -> x { x := add(a, a) } + let y := f(calldatasize()) +} +// ---- +// expressionInliner +// { +// function f(a) -> x +// { +// x := add(a, a) +// } +// let y := add(calldatasize(), calldatasize()) +// } diff --git a/test/libyul/yulOptimizerTests/expressionInliner/double_calls.yul b/test/libyul/yulOptimizerTests/expressionInliner/double_calls.yul new file mode 100644 index 00000000..e1da8e07 --- /dev/null +++ b/test/libyul/yulOptimizerTests/expressionInliner/double_calls.yul @@ -0,0 +1,18 @@ +{ + function f(a) -> x { x := add(a, a) } + function g(b, c) -> y { y := mul(mload(c), f(b)) } + let y := g(calldatasize(), 7) +} +// ---- +// expressionInliner +// { +// function f(a) -> x +// { +// x := add(a, a) +// } +// function g(b, c) -> y +// { +// y := mul(mload(c), add(b, b)) +// } +// let y_1 := mul(mload(7), add(calldatasize(), calldatasize())) +// } diff --git a/test/libyul/yulOptimizerTests/expressionInliner/double_recursive_calls.yul b/test/libyul/yulOptimizerTests/expressionInliner/double_recursive_calls.yul new file mode 100644 index 00000000..082cb53f --- /dev/null +++ b/test/libyul/yulOptimizerTests/expressionInliner/double_recursive_calls.yul @@ -0,0 +1,18 @@ +{ + function f(a, r) -> x { x := g(a, g(r, r)) } + function g(b, s) -> y { y := f(b, f(s, s)) } + let y := g(calldatasize(), 7) +} +// ---- +// expressionInliner +// { +// function f(a, r) -> x +// { +// x := g(a, f(r, f(r, r))) +// } +// function g(b, s) -> y +// { +// y := f(b, g(s, f(s, f(s, s)))) +// } +// let y_1 := f(calldatasize(), g(7, f(7, f(7, 7)))) +// } diff --git a/test/libyul/yulOptimizerTests/expressionInliner/no_inline_mload.yul b/test/libyul/yulOptimizerTests/expressionInliner/no_inline_mload.yul new file mode 100644 index 00000000..0fb43a9d --- /dev/null +++ b/test/libyul/yulOptimizerTests/expressionInliner/no_inline_mload.yul @@ -0,0 +1,14 @@ +// Does not inline because mload could be moved out of sequence +{ + function f(a) -> x { x := a } + let y := f(mload(2)) +} +// ---- +// expressionInliner +// { +// function f(a) -> x +// { +// x := a +// } +// let y := f(mload(2)) +// } diff --git a/test/libyul/yulOptimizerTests/expressionInliner/no_move_with_sideeffects.yul b/test/libyul/yulOptimizerTests/expressionInliner/no_move_with_sideeffects.yul new file mode 100644 index 00000000..7fdad6c4 --- /dev/null +++ b/test/libyul/yulOptimizerTests/expressionInliner/no_move_with_sideeffects.yul @@ -0,0 +1,27 @@ +// The calls to g and h cannot be moved because g and h are not movable. Therefore, the call +// to f is not inlined. +{ + function f(a, b) -> x { x := add(b, a) } + function g() -> y { y := mload(0) mstore(0, 4) } + function h() -> z { mstore(0, 4) z := mload(0) } + let r := f(g(), h()) +} +// ---- +// expressionInliner +// { +// function f(a, b) -> x +// { +// x := add(b, a) +// } +// function g() -> y +// { +// y := mload(0) +// mstore(0, 4) +// } +// function h() -> z +// { +// mstore(0, 4) +// z := mload(0) +// } +// let r := f(g(), h()) +// } diff --git a/test/libyul/yulOptimizerTests/expressionInliner/simple.yul b/test/libyul/yulOptimizerTests/expressionInliner/simple.yul new file mode 100644 index 00000000..c186eafd --- /dev/null +++ b/test/libyul/yulOptimizerTests/expressionInliner/simple.yul @@ -0,0 +1,14 @@ +// yul +{ + function f() -> x:u256 { x := 2:u256 } + let y:u256 := f() +} +// ---- +// expressionInliner +// { +// function f() -> x:u256 +// { +// x := 2:u256 +// } +// let y:u256 := 2:u256 +// } diff --git a/test/libyul/yulOptimizerTests/expressionInliner/with_args.yul b/test/libyul/yulOptimizerTests/expressionInliner/with_args.yul new file mode 100644 index 00000000..b5f4d515 --- /dev/null +++ b/test/libyul/yulOptimizerTests/expressionInliner/with_args.yul @@ -0,0 +1,14 @@ +// yul +{ + function f(a:u256) -> x:u256 { x := a } + let y:u256 := f(7:u256) +} +// ---- +// expressionInliner +// { +// function f(a:u256) -> x:u256 +// { +// x := a +// } +// let y:u256 := 7:u256 +// } diff --git a/test/libyul/yulOptimizerTests/expressionJoiner/if_condition.yul b/test/libyul/yulOptimizerTests/expressionJoiner/if_condition.yul new file mode 100644 index 00000000..a1349511 --- /dev/null +++ b/test/libyul/yulOptimizerTests/expressionJoiner/if_condition.yul @@ -0,0 +1,21 @@ +{ + let a := mload(3) + let b := sload(a) + let c := mload(7) + let d := add(c, b) + if d { + let x := mload(3) + let y := add(x, 3) + } + let z := 3 + let t := add(z, 9) +} +// ---- +// expressionJoiner +// { +// if add(mload(7), sload(mload(3))) +// { +// let y := add(mload(3), 3) +// } +// let t := add(3, 9) +// } diff --git a/test/libyul/yulOptimizerTests/expressionJoiner/muli_wrong_order3.yul b/test/libyul/yulOptimizerTests/expressionJoiner/muli_wrong_order3.yul new file mode 100644 index 00000000..0d407c7c --- /dev/null +++ b/test/libyul/yulOptimizerTests/expressionJoiner/muli_wrong_order3.yul @@ -0,0 +1,13 @@ +{ + let a := mload(3) + let b := mload(6) + let x := mul(add(b, a), mload(2)) + sstore(x, 3) +} +// ---- +// expressionJoiner +// { +// let a := mload(3) +// let b := mload(6) +// sstore(mul(add(b, a), mload(2)), 3) +// } diff --git a/test/libyul/yulOptimizerTests/expressionJoiner/multi.yul b/test/libyul/yulOptimizerTests/expressionJoiner/multi.yul new file mode 100644 index 00000000..fd53ca51 --- /dev/null +++ b/test/libyul/yulOptimizerTests/expressionJoiner/multi.yul @@ -0,0 +1,11 @@ +{ + let a := mload(2) + let b := mload(6) + let x := mul(add(b, a), 2) + sstore(x, 3) +} +// ---- +// expressionJoiner +// { +// sstore(mul(add(mload(6), mload(2)), 2), 3) +// } diff --git a/test/libyul/yulOptimizerTests/expressionJoiner/multi_reference.yul b/test/libyul/yulOptimizerTests/expressionJoiner/multi_reference.yul new file mode 100644 index 00000000..078a12a5 --- /dev/null +++ b/test/libyul/yulOptimizerTests/expressionJoiner/multi_reference.yul @@ -0,0 +1,11 @@ +{ + // This is not joined because a is referenced multiple times + let a := mload(2) + let b := add(a, a) +} +// ---- +// expressionJoiner +// { +// let a := mload(2) +// let b := add(a, a) +// } diff --git a/test/libyul/yulOptimizerTests/expressionJoiner/multi_wrong_order.yul b/test/libyul/yulOptimizerTests/expressionJoiner/multi_wrong_order.yul new file mode 100644 index 00000000..965e07e9 --- /dev/null +++ b/test/libyul/yulOptimizerTests/expressionJoiner/multi_wrong_order.yul @@ -0,0 +1,15 @@ +{ + // We have an interleaved "add" here, so we cannot inline "a" + // (note that this component does not analyze whether + // functions are pure or not) + let a := mload(2) + let b := mload(6) + let x := mul(a, add(2, b)) + sstore(x, 3) +} +// ---- +// expressionJoiner +// { +// let a := mload(2) +// sstore(mul(a, add(2, mload(6))), 3) +// } diff --git a/test/libyul/yulOptimizerTests/expressionJoiner/multi_wrong_order2.yul b/test/libyul/yulOptimizerTests/expressionJoiner/multi_wrong_order2.yul new file mode 100644 index 00000000..c577e182 --- /dev/null +++ b/test/libyul/yulOptimizerTests/expressionJoiner/multi_wrong_order2.yul @@ -0,0 +1,12 @@ +{ + let a := mload(2) + let b := mload(6) + let x := mul(add(a, b), 2) + sstore(x, 3) +} +// ---- +// expressionJoiner +// { +// let a := mload(2) +// sstore(mul(add(a, mload(6)), 2), 3) +// } diff --git a/test/libyul/yulOptimizerTests/expressionJoiner/no_replacement_across_blocks.yul b/test/libyul/yulOptimizerTests/expressionJoiner/no_replacement_across_blocks.yul new file mode 100644 index 00000000..a781bb2a --- /dev/null +++ b/test/libyul/yulOptimizerTests/expressionJoiner/no_replacement_across_blocks.yul @@ -0,0 +1,19 @@ +{ + // The component will remove the empty block after + // it has handled the outer block. + // The idea behind this test is that the component + // does not perform replacements across blocks because + // they usually have contents, but adding contents + // will reduce the scope of the test. + let a := mload(2) + let x := calldataload(a) + { + } + sstore(x, 3) +} +// ---- +// expressionJoiner +// { +// let x := calldataload(mload(2)) +// sstore(x, 3) +// } diff --git a/test/libyul/yulOptimizerTests/expressionJoiner/no_replacement_in_loop_condition1.yul b/test/libyul/yulOptimizerTests/expressionJoiner/no_replacement_in_loop_condition1.yul new file mode 100644 index 00000000..75218a5c --- /dev/null +++ b/test/libyul/yulOptimizerTests/expressionJoiner/no_replacement_in_loop_condition1.yul @@ -0,0 +1,15 @@ +{ + for { let b := mload(1) } b {} {} +} +// ---- +// expressionJoiner +// { +// for { +// let b := mload(1) +// } +// b +// { +// } +// { +// } +// } diff --git a/test/libyul/yulOptimizerTests/expressionJoiner/no_replacement_in_loop_condition2.yul b/test/libyul/yulOptimizerTests/expressionJoiner/no_replacement_in_loop_condition2.yul new file mode 100644 index 00000000..d5f7d8fa --- /dev/null +++ b/test/libyul/yulOptimizerTests/expressionJoiner/no_replacement_in_loop_condition2.yul @@ -0,0 +1,16 @@ +{ + let a := mload(0) + for { } a {} {} +} +// ---- +// expressionJoiner +// { +// let a := mload(0) +// for { +// } +// a +// { +// } +// { +// } +// } diff --git a/test/libyul/yulOptimizerTests/expressionJoiner/only_assignment.yul b/test/libyul/yulOptimizerTests/expressionJoiner/only_assignment.yul new file mode 100644 index 00000000..c7411211 --- /dev/null +++ b/test/libyul/yulOptimizerTests/expressionJoiner/only_assignment.yul @@ -0,0 +1,16 @@ +{ + // This is not joined because a is referenced multiple times + function f(a) -> x { + a := mload(2) + x := add(a, 3) + } +} +// ---- +// expressionJoiner +// { +// function f(a) -> x +// { +// a := mload(2) +// x := add(a, 3) +// } +// } diff --git a/test/libyul/yulOptimizerTests/expressionJoiner/reassignment.yul b/test/libyul/yulOptimizerTests/expressionJoiner/reassignment.yul new file mode 100644 index 00000000..1e502353 --- /dev/null +++ b/test/libyul/yulOptimizerTests/expressionJoiner/reassignment.yul @@ -0,0 +1,13 @@ +{ + // This is not joined because a is referenced multiple times + let a := mload(2) + let b := mload(a) + a := 4 +} +// ---- +// expressionJoiner +// { +// let a := mload(2) +// let b := mload(a) +// a := 4 +// } diff --git a/test/libyul/yulOptimizerTests/expressionJoiner/simple.yul b/test/libyul/yulOptimizerTests/expressionJoiner/simple.yul new file mode 100644 index 00000000..b03bcf45 --- /dev/null +++ b/test/libyul/yulOptimizerTests/expressionJoiner/simple.yul @@ -0,0 +1,10 @@ +{ + let a := mload(2) + let x := calldataload(a) + sstore(x, 3) +} +// ---- +// expressionJoiner +// { +// sstore(calldataload(mload(2)), 3) +// } diff --git a/test/libyul/yulOptimizerTests/expressionJoiner/single_wrong_order.yul b/test/libyul/yulOptimizerTests/expressionJoiner/single_wrong_order.yul new file mode 100644 index 00000000..3b433f78 --- /dev/null +++ b/test/libyul/yulOptimizerTests/expressionJoiner/single_wrong_order.yul @@ -0,0 +1,13 @@ +{ + let a := mload(3) + let b := sload(a) + let c := mload(7) + let d := add(b, c) + sstore(d, 0) +} +// ---- +// expressionJoiner +// { +// let b := sload(mload(3)) +// sstore(add(b, mload(7)), 0) +// } diff --git a/test/libyul/yulOptimizerTests/expressionJoiner/smoke.yul b/test/libyul/yulOptimizerTests/expressionJoiner/smoke.yul new file mode 100644 index 00000000..c0e2c6f2 --- /dev/null +++ b/test/libyul/yulOptimizerTests/expressionJoiner/smoke.yul @@ -0,0 +1,5 @@ +{ } +// ---- +// expressionJoiner +// { +// } diff --git a/test/libyul/yulOptimizerTests/expressionJoiner/switch_expression.yul b/test/libyul/yulOptimizerTests/expressionJoiner/switch_expression.yul new file mode 100644 index 00000000..0e4e540e --- /dev/null +++ b/test/libyul/yulOptimizerTests/expressionJoiner/switch_expression.yul @@ -0,0 +1,28 @@ +{ + let a := mload(3) + let b := sload(a) + let c := mload(7) + let d := add(c, b) + switch d + case 3 { + let x := mload(3) + let y := add(x, 3) + } + default { + sstore(1, 0) + } + let z := 3 + let t := add(z, 9) +} +// ---- +// expressionJoiner +// { +// switch add(mload(7), sload(mload(3))) +// case 3 { +// let y := add(mload(3), 3) +// } +// default { +// sstore(1, 0) +// } +// let t := add(3, 9) +// } diff --git a/test/libyul/yulOptimizerTests/expressionJoiner/triple.yul b/test/libyul/yulOptimizerTests/expressionJoiner/triple.yul new file mode 100644 index 00000000..7b722be1 --- /dev/null +++ b/test/libyul/yulOptimizerTests/expressionJoiner/triple.yul @@ -0,0 +1,12 @@ +{ + let a := mload(2) + let b := mload(6) + let c := mload(7) + let x := mul(add(c, b), a) + sstore(x, 3) +} +// ---- +// expressionJoiner +// { +// sstore(mul(add(mload(7), mload(6)), mload(2)), 3) +// } diff --git a/test/libyul/yulOptimizerTests/expressionSimplifier/constant_propagation.yul b/test/libyul/yulOptimizerTests/expressionSimplifier/constant_propagation.yul new file mode 100644 index 00000000..0b55adc5 --- /dev/null +++ b/test/libyul/yulOptimizerTests/expressionSimplifier/constant_propagation.yul @@ -0,0 +1,6 @@ +{ let a := add(7, sub(mload(0), 7)) } +// ---- +// expressionSimplifier +// { +// let a := mload(0) +// } diff --git a/test/libyul/yulOptimizerTests/expressionSimplifier/constants.yul b/test/libyul/yulOptimizerTests/expressionSimplifier/constants.yul new file mode 100644 index 00000000..bd1a5a53 --- /dev/null +++ b/test/libyul/yulOptimizerTests/expressionSimplifier/constants.yul @@ -0,0 +1,6 @@ +{ let a := add(1, mul(3, 4)) } +// ---- +// expressionSimplifier +// { +// let a := 13 +// } diff --git a/test/libyul/yulOptimizerTests/expressionSimplifier/identity_rules_complex.yul b/test/libyul/yulOptimizerTests/expressionSimplifier/identity_rules_complex.yul new file mode 100644 index 00000000..f6190622 --- /dev/null +++ b/test/libyul/yulOptimizerTests/expressionSimplifier/identity_rules_complex.yul @@ -0,0 +1,6 @@ +{ let a := sub(calldataload(0), calldataload(0)) } +// ---- +// expressionSimplifier +// { +// let a := 0 +// } diff --git a/test/libyul/yulOptimizerTests/expressionSimplifier/identity_rules_negative.yul b/test/libyul/yulOptimizerTests/expressionSimplifier/identity_rules_negative.yul new file mode 100644 index 00000000..e91403cd --- /dev/null +++ b/test/libyul/yulOptimizerTests/expressionSimplifier/identity_rules_negative.yul @@ -0,0 +1,6 @@ +{ let a := sub(calldataload(1), calldataload(0)) } +// ---- +// expressionSimplifier +// { +// let a := sub(calldataload(1), calldataload(0)) +// } diff --git a/test/libyul/yulOptimizerTests/expressionSimplifier/identity_rules_simple.yul b/test/libyul/yulOptimizerTests/expressionSimplifier/identity_rules_simple.yul new file mode 100644 index 00000000..d35686cd --- /dev/null +++ b/test/libyul/yulOptimizerTests/expressionSimplifier/identity_rules_simple.yul @@ -0,0 +1,10 @@ +{ + let a := mload(0) + let b := sub(a, a) +} +// ---- +// expressionSimplifier +// { +// let a := mload(0) +// let b := 0 +// } diff --git a/test/libyul/yulOptimizerTests/expressionSimplifier/including_function_calls.yul b/test/libyul/yulOptimizerTests/expressionSimplifier/including_function_calls.yul new file mode 100644 index 00000000..c2ca504a --- /dev/null +++ b/test/libyul/yulOptimizerTests/expressionSimplifier/including_function_calls.yul @@ -0,0 +1,12 @@ +{ + function f() -> a {} + let b := add(7, sub(f(), 7)) +} +// ---- +// expressionSimplifier +// { +// function f() -> a +// { +// } +// let b := f() +// } diff --git a/test/libyul/yulOptimizerTests/expressionSimplifier/inside_for.yul b/test/libyul/yulOptimizerTests/expressionSimplifier/inside_for.yul new file mode 100644 index 00000000..42c37826 --- /dev/null +++ b/test/libyul/yulOptimizerTests/expressionSimplifier/inside_for.yul @@ -0,0 +1,16 @@ +{ + for { let a := 10 } iszero(eq(a, 0)) { a := add(a, 1) } {} +} +// ---- +// expressionSimplifier +// { +// for { +// let a := 10 +// } +// iszero(iszero(a)) +// { +// a := add(a, 1) +// } +// { +// } +// } diff --git a/test/libyul/yulOptimizerTests/expressionSimplifier/invariant.yul b/test/libyul/yulOptimizerTests/expressionSimplifier/invariant.yul new file mode 100644 index 00000000..e6d84552 --- /dev/null +++ b/test/libyul/yulOptimizerTests/expressionSimplifier/invariant.yul @@ -0,0 +1,10 @@ +{ + let a := mload(sub(7, 7)) + let b := sub(a, 0) +} +// ---- +// expressionSimplifier +// { +// let a := mload(0) +// let b := a +// } diff --git a/test/libyul/yulOptimizerTests/expressionSimplifier/mod_and_1.yul b/test/libyul/yulOptimizerTests/expressionSimplifier/mod_and_1.yul new file mode 100644 index 00000000..88714ce0 --- /dev/null +++ b/test/libyul/yulOptimizerTests/expressionSimplifier/mod_and_1.yul @@ -0,0 +1,8 @@ +{ + mstore(0, mod(calldataload(0), exp(2, 8))) +} +// ---- +// expressionSimplifier +// { +// mstore(0, and(calldataload(0), 255)) +// } diff --git a/test/libyul/yulOptimizerTests/expressionSimplifier/mod_and_2.yul b/test/libyul/yulOptimizerTests/expressionSimplifier/mod_and_2.yul new file mode 100644 index 00000000..4d52abe8 --- /dev/null +++ b/test/libyul/yulOptimizerTests/expressionSimplifier/mod_and_2.yul @@ -0,0 +1,8 @@ +{ + mstore(0, mod(calldataload(0), exp(2, 255))) +} +// ---- +// expressionSimplifier +// { +// mstore(0, and(calldataload(0), 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff)) +// } diff --git a/test/libyul/yulOptimizerTests/expressionSimplifier/not_applied_function_call_different_arguments.yul b/test/libyul/yulOptimizerTests/expressionSimplifier/not_applied_function_call_different_arguments.yul new file mode 100644 index 00000000..53270b72 --- /dev/null +++ b/test/libyul/yulOptimizerTests/expressionSimplifier/not_applied_function_call_different_arguments.yul @@ -0,0 +1,12 @@ +{ + function f(a) -> b { } + let c := sub(f(0), f(1)) +} +// ---- +// expressionSimplifier +// { +// function f(a) -> b +// { +// } +// let c := sub(f(0), f(1)) +// } diff --git a/test/libyul/yulOptimizerTests/expressionSimplifier/not_applied_function_call_different_names.yul b/test/libyul/yulOptimizerTests/expressionSimplifier/not_applied_function_call_different_names.yul new file mode 100644 index 00000000..6ab65d29 --- /dev/null +++ b/test/libyul/yulOptimizerTests/expressionSimplifier/not_applied_function_call_different_names.yul @@ -0,0 +1,16 @@ +{ + function f1() -> a { } + function f2() -> b { } + let c := sub(f1(), f2()) +} +// ---- +// expressionSimplifier +// { +// function f1() -> a +// { +// } +// function f2() -> b +// { +// } +// let c := sub(f1(), f2()) +// } diff --git a/test/libyul/yulOptimizerTests/expressionSimplifier/not_applied_function_call_equality_not_movable.yul b/test/libyul/yulOptimizerTests/expressionSimplifier/not_applied_function_call_equality_not_movable.yul new file mode 100644 index 00000000..ab1bd128 --- /dev/null +++ b/test/libyul/yulOptimizerTests/expressionSimplifier/not_applied_function_call_equality_not_movable.yul @@ -0,0 +1,13 @@ +// Even if the functions pass the equality check, they are not movable. +{ + function f() -> a { } + let b := sub(f(), f()) +} +// ---- +// expressionSimplifier +// { +// function f() -> a +// { +// } +// let b := sub(f(), f()) +// } diff --git a/test/libyul/yulOptimizerTests/expressionSimplifier/not_applied_removes_non_constant_and_not_movable.yul b/test/libyul/yulOptimizerTests/expressionSimplifier/not_applied_removes_non_constant_and_not_movable.yul new file mode 100644 index 00000000..fc61c3df --- /dev/null +++ b/test/libyul/yulOptimizerTests/expressionSimplifier/not_applied_removes_non_constant_and_not_movable.yul @@ -0,0 +1,10 @@ +// The first argument of div is not constant. +// keccak256 is not movable. +{ + let a := div(keccak256(0, 0), 0) +} +// ---- +// expressionSimplifier +// { +// let a := div(keccak256(0, 0), 0) +// } diff --git a/test/libyul/yulOptimizerTests/expressionSimplifier/reversed.yul b/test/libyul/yulOptimizerTests/expressionSimplifier/reversed.yul new file mode 100644 index 00000000..6353cda9 --- /dev/null +++ b/test/libyul/yulOptimizerTests/expressionSimplifier/reversed.yul @@ -0,0 +1,8 @@ +{ + let a := add(0, mload(0)) +} +// ---- +// expressionSimplifier +// { +// let a := mload(0) +// } diff --git a/test/libyul/yulOptimizerTests/expressionSimplifier/smoke.yul b/test/libyul/yulOptimizerTests/expressionSimplifier/smoke.yul new file mode 100644 index 00000000..88420e92 --- /dev/null +++ b/test/libyul/yulOptimizerTests/expressionSimplifier/smoke.yul @@ -0,0 +1,5 @@ +{ } +// ---- +// expressionSimplifier +// { +// } diff --git a/test/libyul/yulOptimizerTests/expressionSplitter/control_flow.yul b/test/libyul/yulOptimizerTests/expressionSplitter/control_flow.yul new file mode 100644 index 00000000..d021129f --- /dev/null +++ b/test/libyul/yulOptimizerTests/expressionSplitter/control_flow.yul @@ -0,0 +1,40 @@ +{ + let x := calldataload(0) + if mul(add(x, 2), 3) { + for { let a := 2 } lt(a, mload(a)) { a := add(a, mul(a, 2)) } { + let b := mul(add(a, 2), 4) + sstore(b, mul(b, 2)) + } + } +} +// ---- +// expressionSplitter +// { +// let _1 := 0 +// let x := calldataload(_1) +// let _2 := 3 +// let _3 := 2 +// let _4 := add(x, _3) +// let _5 := mul(_4, _2) +// if _5 +// { +// for { +// let a := 2 +// } +// lt(a, mload(a)) +// { +// let _6 := 2 +// let _7 := mul(a, _6) +// a := add(a, _7) +// } +// { +// let _8 := 4 +// let _9 := 2 +// let _10 := add(a, _9) +// let b := mul(_10, _8) +// let _11 := 2 +// let _12 := mul(b, _11) +// sstore(b, _12) +// } +// } +// } diff --git a/test/libyul/yulOptimizerTests/expressionSplitter/inside_function.yul b/test/libyul/yulOptimizerTests/expressionSplitter/inside_function.yul new file mode 100644 index 00000000..53bbcea7 --- /dev/null +++ b/test/libyul/yulOptimizerTests/expressionSplitter/inside_function.yul @@ -0,0 +1,29 @@ +{ + let x := mul(f(0, mload(7)), 3) + function f(a, b) -> c { + c := mul(a, mload(add(b, c))) + } + sstore(x, f(mload(2), mload(2))) +} +// ---- +// expressionSplitter +// { +// let _1 := 3 +// let _2 := 7 +// let _3 := mload(_2) +// let _4 := 0 +// let _5 := f(_4, _3) +// let x := mul(_5, _1) +// function f(a, b) -> c +// { +// let _6 := add(b, c) +// let _7 := mload(_6) +// c := mul(a, _7) +// } +// let _8 := 2 +// let _9 := mload(_8) +// let _10 := 2 +// let _11 := mload(_10) +// let _12 := f(_11, _9) +// sstore(x, _12) +// } diff --git a/test/libyul/yulOptimizerTests/expressionSplitter/smoke.yul b/test/libyul/yulOptimizerTests/expressionSplitter/smoke.yul new file mode 100644 index 00000000..f69f60b6 --- /dev/null +++ b/test/libyul/yulOptimizerTests/expressionSplitter/smoke.yul @@ -0,0 +1,5 @@ +{ } +// ---- +// expressionSplitter +// { +// } diff --git a/test/libyul/yulOptimizerTests/expressionSplitter/switch.yul b/test/libyul/yulOptimizerTests/expressionSplitter/switch.yul new file mode 100644 index 00000000..aee7976f --- /dev/null +++ b/test/libyul/yulOptimizerTests/expressionSplitter/switch.yul @@ -0,0 +1,33 @@ +{ + let x := 8 + switch add(2, calldataload(0)) + case 0 { sstore(0, mload(2)) } + default { mstore(0, mload(3)) } + x := add(mload(3), 4) +} +// ---- +// expressionSplitter +// { +// let x := 8 +// let _1 := 0 +// let _2 := calldataload(_1) +// let _3 := 2 +// let _4 := add(_3, _2) +// switch _4 +// case 0 { +// let _5 := 2 +// let _6 := mload(_5) +// let _7 := 0 +// sstore(_7, _6) +// } +// default { +// let _8 := 3 +// let _9 := mload(_8) +// let _10 := 0 +// mstore(_10, _9) +// } +// let _11 := 4 +// let _12 := 3 +// let _13 := mload(_12) +// x := add(_13, _11) +// } diff --git a/test/libyul/yulOptimizerTests/expressionSplitter/trivial.yul b/test/libyul/yulOptimizerTests/expressionSplitter/trivial.yul new file mode 100644 index 00000000..bff70cd8 --- /dev/null +++ b/test/libyul/yulOptimizerTests/expressionSplitter/trivial.yul @@ -0,0 +1,14 @@ +{ + mstore(add(calldataload(2), mload(3)), 8) +} +// ---- +// expressionSplitter +// { +// let _1 := 8 +// let _2 := 3 +// let _3 := mload(_2) +// let _4 := 2 +// let _5 := calldataload(_4) +// let _6 := add(_5, _3) +// mstore(_6, _1) +// } diff --git a/test/libyul/yulOptimizerTests/fullInliner/double_inline.yul b/test/libyul/yulOptimizerTests/fullInliner/double_inline.yul new file mode 100644 index 00000000..ee7f5bf5 --- /dev/null +++ b/test/libyul/yulOptimizerTests/fullInliner/double_inline.yul @@ -0,0 +1,30 @@ +{ + function f(a) -> b, c { let x := mload(a) b := sload(x) c := 3 } + let a1 := calldataload(0) + let b3, c3 := f(a1) + let b4, c4 := f(c3) +} +// ---- +// fullInliner +// { +// { +// let f_a := calldataload(0) +// let f_b +// let f_c +// f_b := sload(mload(f_a)) +// f_c := 3 +// let b3 := f_b +// let f_a_2 := f_c +// let f_b_3 +// let f_c_4 +// f_b_3 := sload(mload(f_a_2)) +// f_c_4 := 3 +// let b4 := f_b_3 +// let c4 := f_c_4 +// } +// function f(a) -> b, c +// { +// b := sload(mload(a)) +// c := 3 +// } +// } diff --git a/test/libyul/yulOptimizerTests/fullInliner/inside_condition.yul b/test/libyul/yulOptimizerTests/fullInliner/inside_condition.yul new file mode 100644 index 00000000..00bb6577 --- /dev/null +++ b/test/libyul/yulOptimizerTests/fullInliner/inside_condition.yul @@ -0,0 +1,30 @@ +// This tests that splitting the expression inside the condition works properly. +{ + if gt(f(mload(1)), mload(0)) { + sstore(0, 2) + } + function f(a) -> r { + a := mload(a) + r := add(a, calldatasize()) + } +} +// ---- +// fullInliner +// { +// { +// let _2 := mload(0) +// let f_a := mload(1) +// let f_r +// f_a := mload(f_a) +// f_r := add(f_a, calldatasize()) +// if gt(f_r, _2) +// { +// sstore(0, 2) +// } +// } +// function f(a) -> r +// { +// a := mload(a) +// r := add(a, calldatasize()) +// } +// } diff --git a/test/libyul/yulOptimizerTests/fullInliner/large_function_multi_use.yul b/test/libyul/yulOptimizerTests/fullInliner/large_function_multi_use.yul new file mode 100644 index 00000000..0972ac56 --- /dev/null +++ b/test/libyul/yulOptimizerTests/fullInliner/large_function_multi_use.yul @@ -0,0 +1,43 @@ +{ + function f(a) -> b { + let x := mload(a) + b := sload(x) + let c := 3 + mstore(mul(a, b), mload(x)) + let y := add(a, x) + sstore(y, 10) + } + let a := mload(2) + let a2 := 2 + // This should not be inlined because it is not a constant + let r := f(a) + // This should be inlined because it is a constant + let t := f(a2) +} +// ---- +// fullInliner +// { +// { +// let a_1 := mload(2) +// let a2 := 2 +// let r := f(a_1) +// let f_a := a2 +// let f_b +// let f_x := mload(f_a) +// f_b := sload(f_x) +// let f_c := 3 +// mstore(mul(f_a, f_b), mload(f_x)) +// let f_y := add(f_a, f_x) +// sstore(f_y, 10) +// let t := f_b +// } +// function f(a) -> b +// { +// let x := mload(a) +// b := sload(x) +// let c := 3 +// mstore(mul(a, b), mload(x)) +// let y := add(a, x) +// sstore(y, 10) +// } +// } diff --git a/test/libyul/yulOptimizerTests/fullInliner/large_function_single_use.yul b/test/libyul/yulOptimizerTests/fullInliner/large_function_single_use.yul new file mode 100644 index 00000000..3302a35c --- /dev/null +++ b/test/libyul/yulOptimizerTests/fullInliner/large_function_single_use.yul @@ -0,0 +1,36 @@ +{ + function f(a) -> b { + let x := mload(a) + b := sload(x) + let c := 3 + mstore(mul(a, b), mload(x)) + let y := add(a, x) + sstore(y, 10) + } + // Single-use functions are always inlined. + let r := f(mload(1)) +} +// ---- +// fullInliner +// { +// { +// let f_a := mload(1) +// let f_b +// let f_x := mload(f_a) +// f_b := sload(f_x) +// let f_c := 3 +// mstore(mul(f_a, f_b), mload(f_x)) +// let f_y := add(f_a, f_x) +// sstore(f_y, 10) +// let r := f_b +// } +// function f(a) -> b +// { +// let x := mload(a) +// b := sload(x) +// let c := 3 +// mstore(mul(a, b), mload(x)) +// let y := add(a, x) +// sstore(y, 10) +// } +// } diff --git a/test/libyul/yulOptimizerTests/fullInliner/long_names.yul b/test/libyul/yulOptimizerTests/fullInliner/long_names.yul new file mode 100644 index 00000000..644e9126 --- /dev/null +++ b/test/libyul/yulOptimizerTests/fullInliner/long_names.yul @@ -0,0 +1,25 @@ +{ + function verylongfunctionname(verylongvariablename) -> verylongvariablename2 { + verylongvariablename2 := add(verylongvariablename, verylongvariablename) + } + // same long name + let verylongvariablename2 := 3 + mstore(0, verylongfunctionname(verylongvariablename2)) + mstore(1, verylongvariablename2) +} +// ---- +// fullInliner +// { +// { +// let verylongvariablename2_1 := 3 +// let verylongfu_verylongvariablename := verylongvariablename2_1 +// let verylongfu_verylongvariablename2 +// verylongfu_verylongvariablename2 := add(verylongfu_verylongvariablename, verylongfu_verylongvariablename) +// mstore(0, verylongfu_verylongvariablename2) +// mstore(1, verylongvariablename2_1) +// } +// function verylongfunctionname(verylongvariablename) -> verylongvariablename2 +// { +// verylongvariablename2 := add(verylongvariablename, verylongvariablename) +// } +// } diff --git a/test/libyul/yulOptimizerTests/fullInliner/move_up_rightwards_argument.yul b/test/libyul/yulOptimizerTests/fullInliner/move_up_rightwards_argument.yul new file mode 100644 index 00000000..f3d0b286 --- /dev/null +++ b/test/libyul/yulOptimizerTests/fullInliner/move_up_rightwards_argument.yul @@ -0,0 +1,29 @@ +{ + function f(a, b, c) -> x { + x := add(a, b) + x := mul(x, c) + } + let y := add(mload(1), add(f(mload(2), mload(3), mload(4)), mload(5))) +} +// ---- +// fullInliner +// { +// { +// let _2 := mload(5) +// let _4 := mload(4) +// let _6 := mload(3) +// let f_a := mload(2) +// let f_b := _6 +// let f_c := _4 +// let f_x +// f_x := add(f_a, f_b) +// f_x := mul(f_x, f_c) +// let _10 := add(f_x, _2) +// let y := add(mload(1), _10) +// } +// function f(a, b, c) -> x +// { +// x := add(a, b) +// x := mul(x, c) +// } +// } diff --git a/test/libyul/yulOptimizerTests/fullInliner/multi_fun.yul b/test/libyul/yulOptimizerTests/fullInliner/multi_fun.yul new file mode 100644 index 00000000..8bc6ec58 --- /dev/null +++ b/test/libyul/yulOptimizerTests/fullInliner/multi_fun.yul @@ -0,0 +1,31 @@ +{ + function f(a) -> x { x := add(a, a) } + function g(b, c) -> y { y := mul(mload(c), f(b)) } + let y := g(f(3), 7) +} +// ---- +// fullInliner +// { +// { +// let _1 := 7 +// let f_a := 3 +// let f_x +// f_x := add(f_a, f_a) +// let g_b := f_x +// let g_c := _1 +// let g_y +// g_y := mul(mload(g_c), f(g_b)) +// let y_1 := g_y +// } +// function f(a) -> x +// { +// x := add(a, a) +// } +// function g(b, c) -> y +// { +// let f_a_6 := b +// let f_x_7 +// f_x_7 := add(f_a_6, f_a_6) +// y := mul(mload(c), f_x_7) +// } +// } diff --git a/test/libyul/yulOptimizerTests/fullInliner/multi_fun_callback.yul b/test/libyul/yulOptimizerTests/fullInliner/multi_fun_callback.yul new file mode 100644 index 00000000..19ac945e --- /dev/null +++ b/test/libyul/yulOptimizerTests/fullInliner/multi_fun_callback.yul @@ -0,0 +1,63 @@ +{ + // This is a test for an older version where + // inlining was performed on a function + // just being called. This is a problem + // because the statemenst of the original + // function might be in an invalid state. + + function f(x) { + mstore(0, x) + mstore(7, h()) + g(10) + mstore(1, x) + } + function g(x) { + f(1) + } + function h() -> t { + t := 2 + + } + { + f(100) + } +} +// ---- +// fullInliner +// { +// { +// { +// let f_x := 100 +// mstore(0, f_x) +// mstore(7, h()) +// g(10) +// mstore(1, f_x) +// } +// } +// function f(x) +// { +// mstore(0, x) +// let h_t +// h_t := 2 +// mstore(7, h_t) +// let g_x_1 := 10 +// let g_f_x_8 := 1 +// mstore(0, g_f_x_8) +// mstore(7, h()) +// g(10) +// mstore(1, g_f_x_8) +// mstore(1, x) +// } +// function g(x_1) +// { +// let f_x_8 := 1 +// mstore(0, f_x_8) +// mstore(7, h()) +// g(10) +// mstore(1, f_x_8) +// } +// function h() -> t +// { +// t := 2 +// } +// } diff --git a/test/libyul/yulOptimizerTests/fullInliner/multi_return.yul b/test/libyul/yulOptimizerTests/fullInliner/multi_return.yul new file mode 100644 index 00000000..eebdec38 --- /dev/null +++ b/test/libyul/yulOptimizerTests/fullInliner/multi_return.yul @@ -0,0 +1,26 @@ +{ + function f(a) -> x, y { + x := mul(a, a) + y := add(a, x) + } + let r, s := f(mload(0)) + mstore(r, s) +} +// ---- +// fullInliner +// { +// { +// let f_a := mload(0) +// let f_x +// let f_y +// f_x := mul(f_a, f_a) +// f_y := add(f_a, f_x) +// let r := f_x +// mstore(r, f_y) +// } +// function f(a) -> x, y +// { +// x := mul(a, a) +// y := add(a, x) +// } +// } diff --git a/test/libyul/yulOptimizerTests/fullInliner/no_return.yul b/test/libyul/yulOptimizerTests/fullInliner/no_return.yul new file mode 100644 index 00000000..3708c557 --- /dev/null +++ b/test/libyul/yulOptimizerTests/fullInliner/no_return.yul @@ -0,0 +1,18 @@ +{ + function f(a) { + sstore(a, a) + } + f(mload(0)) +} +// ---- +// fullInliner +// { +// { +// let f_a := mload(0) +// sstore(f_a, f_a) +// } +// function f(a) +// { +// sstore(a, a) +// } +// } diff --git a/test/libyul/yulOptimizerTests/fullInliner/not_inside_for.yul b/test/libyul/yulOptimizerTests/fullInliner/not_inside_for.yul new file mode 100644 index 00000000..9644e6c1 --- /dev/null +++ b/test/libyul/yulOptimizerTests/fullInliner/not_inside_for.yul @@ -0,0 +1,43 @@ +{ + for { let x := f(0) } f(x) { x := f(x) } + { + let t := f(x) + } + function f(a) -> r { + sstore(a, 0) + r := a + } +} +// ---- +// fullInliner +// { +// { +// for { +// let f_a := 0 +// let f_r +// sstore(f_a, 0) +// f_r := f_a +// let x := f_r +// } +// f(x) +// { +// let f_a_3 := x +// let f_r_4 +// sstore(f_a_3, 0) +// f_r_4 := f_a_3 +// x := f_r_4 +// } +// { +// let f_a_6 := x +// let f_r_7 +// sstore(f_a_6, 0) +// f_r_7 := f_a_6 +// let t := f_r_7 +// } +// } +// function f(a) -> r +// { +// sstore(a, 0) +// r := a +// } +// } diff --git a/test/libyul/yulOptimizerTests/fullInliner/pop_result.yul b/test/libyul/yulOptimizerTests/fullInliner/pop_result.yul new file mode 100644 index 00000000..cd9e2746 --- /dev/null +++ b/test/libyul/yulOptimizerTests/fullInliner/pop_result.yul @@ -0,0 +1,27 @@ +// An earlier version of the inliner produced +// pop(...) statements and explicitly removed them. +// This used to test that they are removed. +{ + function f(a) -> x { + let r := mul(a, a) + x := add(r, r) + } + pop(add(f(7), 2)) +} +// ---- +// fullInliner +// { +// { +// let _1 := 2 +// let f_a := 7 +// let f_x +// let f_r := mul(f_a, f_a) +// f_x := add(f_r, f_r) +// pop(add(f_x, _1)) +// } +// function f(a) -> x +// { +// let r := mul(a, a) +// x := add(r, r) +// } +// } diff --git a/test/libyul/yulOptimizerTests/fullInliner/recursion.yul b/test/libyul/yulOptimizerTests/fullInliner/recursion.yul new file mode 100644 index 00000000..3e9a8021 --- /dev/null +++ b/test/libyul/yulOptimizerTests/fullInliner/recursion.yul @@ -0,0 +1,18 @@ +{ + function f(a) { + f(1) + } + f(mload(0)) +} +// ---- +// fullInliner +// { +// { +// let f_a := mload(0) +// f(1) +// } +// function f(a) +// { +// f(1) +// } +// } diff --git a/test/libyul/yulOptimizerTests/fullInliner/simple.yul b/test/libyul/yulOptimizerTests/fullInliner/simple.yul new file mode 100644 index 00000000..fcdf453b --- /dev/null +++ b/test/libyul/yulOptimizerTests/fullInliner/simple.yul @@ -0,0 +1,24 @@ +{ + function f(a) -> x { + let r := mul(a, a) + x := add(r, r) + } + let y := add(f(sload(mload(2))), mload(7)) +} +// ---- +// fullInliner +// { +// { +// let _2 := mload(7) +// let f_a := sload(mload(2)) +// let f_x +// let f_r := mul(f_a, f_a) +// f_x := add(f_r, f_r) +// let y := add(f_x, _2) +// } +// function f(a) -> x +// { +// let r := mul(a, a) +// x := add(r, r) +// } +// } diff --git a/test/libyul/yulOptimizerTests/fullSimplify/constant_propagation.yul b/test/libyul/yulOptimizerTests/fullSimplify/constant_propagation.yul new file mode 100644 index 00000000..90a3e16d --- /dev/null +++ b/test/libyul/yulOptimizerTests/fullSimplify/constant_propagation.yul @@ -0,0 +1,10 @@ +{ + let a := add(7, sub(mload(0), 7)) + mstore(a, 0) +} +// ---- +// fullSimplify +// { +// let _2 := 0 +// mstore(mload(_2), _2) +// } diff --git a/test/libyul/yulOptimizerTests/fullSimplify/constants.yul b/test/libyul/yulOptimizerTests/fullSimplify/constants.yul new file mode 100644 index 00000000..b9c7c1fc --- /dev/null +++ b/test/libyul/yulOptimizerTests/fullSimplify/constants.yul @@ -0,0 +1,9 @@ +{ + let a := add(1, mul(3, 4)) + mstore(0, a) +} +// ---- +// fullSimplify +// { +// mstore(0, 13) +// } diff --git a/test/libyul/yulOptimizerTests/fullSimplify/identity_rules_complex.yul b/test/libyul/yulOptimizerTests/fullSimplify/identity_rules_complex.yul new file mode 100644 index 00000000..4b17d7ea --- /dev/null +++ b/test/libyul/yulOptimizerTests/fullSimplify/identity_rules_complex.yul @@ -0,0 +1,9 @@ +{ + let a := sub(calldataload(0), calldataload(0)) + mstore(a, 0) +} +// ---- +// fullSimplify +// { +// mstore(0, 0) +// } diff --git a/test/libyul/yulOptimizerTests/fullSimplify/identity_rules_negative.yul b/test/libyul/yulOptimizerTests/fullSimplify/identity_rules_negative.yul new file mode 100644 index 00000000..a1737efa --- /dev/null +++ b/test/libyul/yulOptimizerTests/fullSimplify/identity_rules_negative.yul @@ -0,0 +1,10 @@ +{ + let a := sub(calldataload(1), calldataload(0)) + mstore(0, a) +} +// ---- +// fullSimplify +// { +// let _1 := 0 +// mstore(_1, sub(calldataload(1), calldataload(_1))) +// } diff --git a/test/libyul/yulOptimizerTests/fullSimplify/identity_rules_simple.yul b/test/libyul/yulOptimizerTests/fullSimplify/identity_rules_simple.yul new file mode 100644 index 00000000..22a358fd --- /dev/null +++ b/test/libyul/yulOptimizerTests/fullSimplify/identity_rules_simple.yul @@ -0,0 +1,11 @@ +{ + let a := mload(0) + mstore(0, sub(a, a)) +} +// ---- +// fullSimplify +// { +// let _1 := 0 +// pop(mload(_1)) +// mstore(_1, 0) +// } diff --git a/test/libyul/yulOptimizerTests/fullSimplify/including_function_calls.yul b/test/libyul/yulOptimizerTests/fullSimplify/including_function_calls.yul new file mode 100644 index 00000000..fa3ff07c --- /dev/null +++ b/test/libyul/yulOptimizerTests/fullSimplify/including_function_calls.yul @@ -0,0 +1,13 @@ +{ + function f() -> a {} + let b := add(7, sub(f(), 7)) + mstore(b, 0) +} +// ---- +// fullSimplify +// { +// function f() -> a +// { +// } +// mstore(f(), 0) +// } diff --git a/test/libyul/yulOptimizerTests/fullSimplify/inside_for.yul b/test/libyul/yulOptimizerTests/fullSimplify/inside_for.yul new file mode 100644 index 00000000..f1b40301 --- /dev/null +++ b/test/libyul/yulOptimizerTests/fullSimplify/inside_for.yul @@ -0,0 +1,17 @@ +{ + let x := calldataload(3) + for { let a := 10 } iszero(eq(a, sub(x, calldataload(3)))) { a := add(a, 1) } {} +} +// ---- +// fullSimplify +// { +// for { +// let a := 10 +// } +// iszero(iszero(a)) +// { +// a := add(a, 1) +// } +// { +// } +// } diff --git a/test/libyul/yulOptimizerTests/fullSimplify/invariant.yul b/test/libyul/yulOptimizerTests/fullSimplify/invariant.yul new file mode 100644 index 00000000..a8eedef1 --- /dev/null +++ b/test/libyul/yulOptimizerTests/fullSimplify/invariant.yul @@ -0,0 +1,17 @@ +{ + let a := calldataload(sub(7, 7)) + let b := sub(a, 0) + // Below, `b` is not eliminated, because + // we run CSE and then Simplify. + // Elimination of `b` would require another + // run of CSE afterwards. + mstore(b, eq(calldataload(0), a)) +} +// ---- +// fullSimplify +// { +// let a := calldataload(0) +// let _4 := 0 +// let b := a +// mstore(b, eq(calldataload(_4), a)) +// } diff --git a/test/libyul/yulOptimizerTests/fullSimplify/mod_and_1.yul b/test/libyul/yulOptimizerTests/fullSimplify/mod_and_1.yul new file mode 100644 index 00000000..bba16a94 --- /dev/null +++ b/test/libyul/yulOptimizerTests/fullSimplify/mod_and_1.yul @@ -0,0 +1,9 @@ +{ + mstore(0, mod(calldataload(0), exp(2, 8))) +} +// ---- +// fullSimplify +// { +// let _4 := 0 +// mstore(_4, and(calldataload(_4), 255)) +// } diff --git a/test/libyul/yulOptimizerTests/fullSimplify/mod_and_2.yul b/test/libyul/yulOptimizerTests/fullSimplify/mod_and_2.yul new file mode 100644 index 00000000..4a6eaa52 --- /dev/null +++ b/test/libyul/yulOptimizerTests/fullSimplify/mod_and_2.yul @@ -0,0 +1,9 @@ +{ + mstore(0, mod(calldataload(0), exp(2, 255))) +} +// ---- +// fullSimplify +// { +// let _4 := 0 +// mstore(_4, and(calldataload(_4), 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff)) +// } diff --git a/test/libyul/yulOptimizerTests/fullSimplify/not_applied_function_call_different_arguments.yul b/test/libyul/yulOptimizerTests/fullSimplify/not_applied_function_call_different_arguments.yul new file mode 100644 index 00000000..0c5e3ed9 --- /dev/null +++ b/test/libyul/yulOptimizerTests/fullSimplify/not_applied_function_call_different_arguments.yul @@ -0,0 +1,14 @@ +{ + function f(a) -> b { } + mstore(0, sub(f(0), f(1))) +} +// ---- +// fullSimplify +// { +// function f(a) -> b +// { +// } +// let _2 := f(1) +// let _3 := 0 +// mstore(_3, sub(f(_3), _2)) +// } diff --git a/test/libyul/yulOptimizerTests/fullSimplify/not_applied_function_call_different_names.yul b/test/libyul/yulOptimizerTests/fullSimplify/not_applied_function_call_different_names.yul new file mode 100644 index 00000000..90e89fe1 --- /dev/null +++ b/test/libyul/yulOptimizerTests/fullSimplify/not_applied_function_call_different_names.yul @@ -0,0 +1,17 @@ +{ + function f1() -> a { } + function f2() -> b { } + let c := sub(f1(), f2()) + mstore(0, c) +} +// ---- +// fullSimplify +// { +// function f1() -> a +// { +// } +// function f2() -> b +// { +// } +// mstore(0, sub(f1(), f2())) +// } diff --git a/test/libyul/yulOptimizerTests/fullSimplify/not_applied_function_call_equality_not_movable.yul b/test/libyul/yulOptimizerTests/fullSimplify/not_applied_function_call_equality_not_movable.yul new file mode 100644 index 00000000..92e50ebe --- /dev/null +++ b/test/libyul/yulOptimizerTests/fullSimplify/not_applied_function_call_equality_not_movable.yul @@ -0,0 +1,14 @@ +// Even if the functions pass the equality check, they are not movable. +{ + function f() -> a { } + let b := sub(f(), f()) + mstore(0, b) +} +// ---- +// fullSimplify +// { +// function f() -> a +// { +// } +// mstore(0, sub(f(), f())) +// } diff --git a/test/libyul/yulOptimizerTests/fullSimplify/not_applied_removes_non_constant_and_not_movable.yul b/test/libyul/yulOptimizerTests/fullSimplify/not_applied_removes_non_constant_and_not_movable.yul new file mode 100644 index 00000000..7dcdc280 --- /dev/null +++ b/test/libyul/yulOptimizerTests/fullSimplify/not_applied_removes_non_constant_and_not_movable.yul @@ -0,0 +1,12 @@ +// div is eliminated, but keccak256 has side-effects. +{ + let a := div(keccak256(0, 0), 0) + mstore(0, a) +} +// ---- +// fullSimplify +// { +// let _1 := 0 +// pop(keccak256(_1, _1)) +// mstore(_1, 0) +// } diff --git a/test/libyul/yulOptimizerTests/fullSimplify/operations.yul b/test/libyul/yulOptimizerTests/fullSimplify/operations.yul new file mode 100644 index 00000000..25467b62 --- /dev/null +++ b/test/libyul/yulOptimizerTests/fullSimplify/operations.yul @@ -0,0 +1,44 @@ +{ + let x := mload(0) + mstore(1, mul(x, 0)) + mstore(2, div(x, 0)) + mstore(3, div(0, x)) + mstore(4, sdiv(x, 0)) + mstore(5, sdiv(0, x)) + mstore(6, and(0, x)) + mstore(7, and(x, 0)) + mstore(8, mod(0, x)) + mstore(9, mod(x, 0)) + mstore(10, lt(x, x)) + mstore(11, gt(x, x)) + mstore(12, slt(x, x)) + mstore(13, sgt(x, x)) + mstore(14, mod(x, x)) + mstore(15, and(x, not(x))) + mstore(16, and(not(x), x)) + mstore(17, or(x, not(x))) + mstore(18, or(not(x), x)) +} +// ---- +// fullSimplify +// { +// pop(mload(0)) +// mstore(1, 0) +// mstore(2, 0) +// mstore(3, 0) +// mstore(4, 0) +// mstore(5, 0) +// mstore(6, 0) +// mstore(7, 0) +// mstore(8, 0) +// mstore(9, 0) +// mstore(10, 0) +// mstore(11, 0) +// mstore(12, 0) +// mstore(13, 0) +// mstore(14, 0) +// mstore(15, 0) +// mstore(16, 0) +// mstore(17, 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) +// mstore(18, 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) +// } diff --git a/test/libyul/yulOptimizerTests/fullSimplify/reversed.yul b/test/libyul/yulOptimizerTests/fullSimplify/reversed.yul new file mode 100644 index 00000000..fb916e6a --- /dev/null +++ b/test/libyul/yulOptimizerTests/fullSimplify/reversed.yul @@ -0,0 +1,10 @@ +{ + let a := add(0, mload(0)) + mstore(0, a) +} +// ---- +// fullSimplify +// { +// let _1 := 0 +// mstore(_1, mload(_1)) +// } diff --git a/test/libyul/yulOptimizerTests/fullSimplify/signextend.yul b/test/libyul/yulOptimizerTests/fullSimplify/signextend.yul new file mode 100644 index 00000000..714eb860 --- /dev/null +++ b/test/libyul/yulOptimizerTests/fullSimplify/signextend.yul @@ -0,0 +1,12 @@ +{ + let x := 7 + mstore(0, signextend(50, x)) + let y := 255 + mstore(1, signextend(0, y)) +} +// ---- +// fullSimplify +// { +// mstore(0, 7) +// mstore(1, 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) +// } diff --git a/test/libyul/yulOptimizerTests/fullSimplify/smoke.yul b/test/libyul/yulOptimizerTests/fullSimplify/smoke.yul new file mode 100644 index 00000000..a4fbb899 --- /dev/null +++ b/test/libyul/yulOptimizerTests/fullSimplify/smoke.yul @@ -0,0 +1,5 @@ +{ } +// ---- +// fullSimplify +// { +// } diff --git a/test/libyul/yulOptimizerTests/fullSuite/abi_example1.yul b/test/libyul/yulOptimizerTests/fullSuite/abi_example1.yul new file mode 100644 index 00000000..a34da198 --- /dev/null +++ b/test/libyul/yulOptimizerTests/fullSuite/abi_example1.yul @@ -0,0 +1,654 @@ +{ + let x := abi_encode_t_array$_t_array$_t_contract$_C_$55_$3_memory_$dyn_memory_ptr_to_t_array$_t_array$_t_address_$3_memory_$dyn_memory_ptr(mload(0), 0x20) + let a, b, c, d := abi_decode_tuple_t_uint256t_uint256t_array$_t_uint256_$dyn_memory_ptrt_array$_t_array$_t_uint256_$2_memory_$dyn_memory_ptr(mload(0x20), mload(0x40)) + sstore(a, b) + sstore(c, d) + sstore(0, x) + + function abi_decode_t_address(offset, end) -> value + { + value := cleanup_revert_t_address(calldataload(offset)) + } + function abi_decode_t_array$_t_address_$dyn_memory(offset, end) -> array + { + if iszero(slt(add(offset, 0x1f), end)) + { + revert(0, 0) + } + let length := calldataload(offset) + array := allocateMemory(array_allocation_size_t_array$_t_address_$dyn_memory(length)) + let dst := array + mstore(array, length) + offset := add(offset, 0x20) + dst := add(dst, 0x20) + let src := offset + if gt(add(src, mul(length, 0x20)), end) + { + revert(0, 0) + } + for { + let i := 0 + } + lt(i, length) + { + i := add(i, 1) + } + { + let elementPos := src + mstore(dst, abi_decode_t_address(elementPos, end)) + dst := add(dst, 0x20) + src := add(src, 0x20) + } + } + function abi_decode_t_array$_t_array$_t_uint256_$2_memory_$dyn_memory_ptr(offset, end) -> array + { + if iszero(slt(add(offset, 0x1f), end)) + { + revert(0, 0) + } + let length := calldataload(offset) + array := allocateMemory(array_allocation_size_t_array$_t_array$_t_uint256_$2_memory_$dyn_memory_ptr(length)) + let dst := array + mstore(array, length) + offset := add(offset, 0x20) + dst := add(dst, 0x20) + let src := offset + if gt(add(src, mul(length, 0x40)), end) + { + revert(0, 0) + } + for { + let i := 0 + } + lt(i, length) + { + i := add(i, 1) + } + { + let elementPos := src + mstore(dst, abi_decode_t_array$_t_uint256_$2_memory(elementPos, end)) + dst := add(dst, 0x20) + src := add(src, 0x40) + } + } + function abi_decode_t_array$_t_uint256_$2_memory(offset, end) -> array + { + if iszero(slt(add(offset, 0x1f), end)) + { + revert(0, 0) + } + let length := 0x2 + array := allocateMemory(array_allocation_size_t_array$_t_uint256_$2_memory(length)) + let dst := array + let src := offset + if gt(add(src, mul(length, 0x20)), end) + { + revert(0, 0) + } + for { + let i := 0 + } + lt(i, length) + { + i := add(i, 1) + } + { + let elementPos := src + mstore(dst, abi_decode_t_uint256(elementPos, end)) + dst := add(dst, 0x20) + src := add(src, 0x20) + } + } + function abi_decode_t_array$_t_uint256_$dyn_memory(offset, end) -> array + { + if iszero(slt(add(offset, 0x1f), end)) + { + revert(0, 0) + } + let length := calldataload(offset) + array := allocateMemory(array_allocation_size_t_array$_t_uint256_$dyn_memory(length)) + let dst := array + mstore(array, length) + offset := add(offset, 0x20) + dst := add(dst, 0x20) + let src := offset + if gt(add(src, mul(length, 0x20)), end) + { + revert(0, 0) + } + for { + let i := 0 + } + lt(i, length) + { + i := add(i, 1) + } + { + let elementPos := src + mstore(dst, abi_decode_t_uint256(elementPos, end)) + dst := add(dst, 0x20) + src := add(src, 0x20) + } + } + function abi_decode_t_array$_t_uint256_$dyn_memory_ptr(offset, end) -> array + { + if iszero(slt(add(offset, 0x1f), end)) + { + revert(0, 0) + } + let length := calldataload(offset) + array := allocateMemory(array_allocation_size_t_array$_t_uint256_$dyn_memory_ptr(length)) + let dst := array + mstore(array, length) + offset := add(offset, 0x20) + dst := add(dst, 0x20) + let src := offset + if gt(add(src, mul(length, 0x20)), end) + { + revert(0, 0) + } + for { + let i := 0 + } + lt(i, length) + { + i := add(i, 1) + } + { + let elementPos := src + mstore(dst, abi_decode_t_uint256(elementPos, end)) + dst := add(dst, 0x20) + src := add(src, 0x20) + } + } + function abi_decode_t_contract$_C_$55(offset, end) -> value + { + value := cleanup_revert_t_contract$_C_$55(calldataload(offset)) + } + function abi_decode_t_struct$_S_$11_memory_ptr(headStart, end) -> value + { + if slt(sub(end, headStart), 0x60) + { + revert(0, 0) + } + value := allocateMemory(0x60) + { + let offset := 0 + mstore(add(value, 0x0), abi_decode_t_uint256(add(headStart, offset), end)) + } + { + let offset := calldataload(add(headStart, 32)) + if gt(offset, 0xffffffffffffffff) + { + revert(0, 0) + } + mstore(add(value, 0x20), abi_decode_t_array$_t_uint256_$dyn_memory(add(headStart, offset), end)) + } + { + let offset := calldataload(add(headStart, 64)) + if gt(offset, 0xffffffffffffffff) + { + revert(0, 0) + } + mstore(add(value, 0x40), abi_decode_t_array$_t_address_$dyn_memory(add(headStart, offset), end)) + } + } + function abi_decode_t_uint256(offset, end) -> value + { + value := cleanup_revert_t_uint256(calldataload(offset)) + } + function abi_decode_t_uint8(offset, end) -> value + { + value := cleanup_revert_t_uint8(calldataload(offset)) + } + function abi_decode_tuple_t_contract$_C_$55t_uint8(headStart, dataEnd) -> value0, value1 + { + if slt(sub(dataEnd, headStart), 64) + { + revert(0, 0) + } + { + let offset := 0 + value0 := abi_decode_t_contract$_C_$55(add(headStart, offset), dataEnd) + } + { + let offset := 32 + value1 := abi_decode_t_uint8(add(headStart, offset), dataEnd) + } + } + function abi_decode_tuple_t_struct$_S_$11_memory_ptrt_uint256(headStart, dataEnd) -> value0, value1 + { + if slt(sub(dataEnd, headStart), 64) + { + revert(0, 0) + } + { + let offset := calldataload(add(headStart, 0)) + if gt(offset, 0xffffffffffffffff) + { + revert(0, 0) + } + value0 := abi_decode_t_struct$_S_$11_memory_ptr(add(headStart, offset), dataEnd) + } + { + let offset := 32 + value1 := abi_decode_t_uint256(add(headStart, offset), dataEnd) + } + } + function abi_decode_tuple_t_uint256t_uint256t_array$_t_uint256_$dyn_memory_ptrt_array$_t_array$_t_uint256_$2_memory_$dyn_memory_ptr(headStart, dataEnd) -> value0, value1, value2, value3 + { + if slt(sub(dataEnd, headStart), 128) + { + revert(0, 0) + } + { + let offset := 0 + value0 := abi_decode_t_uint256(add(headStart, offset), dataEnd) + } + { + let offset := 32 + value1 := abi_decode_t_uint256(add(headStart, offset), dataEnd) + } + { + let offset := calldataload(add(headStart, 64)) + if gt(offset, 0xffffffffffffffff) + { + revert(0, 0) + } + value2 := abi_decode_t_array$_t_uint256_$dyn_memory_ptr(add(headStart, offset), dataEnd) + } + { + let offset := calldataload(add(headStart, 96)) + if gt(offset, 0xffffffffffffffff) + { + revert(0, 0) + } + value3 := abi_decode_t_array$_t_array$_t_uint256_$2_memory_$dyn_memory_ptr(add(headStart, offset), dataEnd) + } + } + function abi_encode_t_array$_t_array$_t_contract$_C_$55_$3_memory_$dyn_memory_ptr_to_t_array$_t_array$_t_address_$3_memory_$dyn_memory_ptr(value, pos) -> end + { + let length := array_length_t_array$_t_array$_t_contract$_C_$55_$3_memory_$dyn_memory_ptr(value) + mstore(pos, length) + pos := add(pos, 0x20) + let srcPtr := array_dataslot_t_array$_t_array$_t_contract$_C_$55_$3_memory_$dyn_memory_ptr(value) + for { + let i := 0 + } + lt(i, length) + { + i := add(i, 1) + } + { + abi_encode_t_array$_t_contract$_C_$55_$3_memory_to_t_array$_t_address_$3_memory_ptr(mload(srcPtr), pos) + srcPtr := array_nextElement_t_array$_t_array$_t_contract$_C_$55_$3_memory_$dyn_memory_ptr(srcPtr) + pos := add(pos, 0x60) + } + end := pos + } + function abi_encode_t_array$_t_contract$_C_$55_$3_memory_to_t_array$_t_address_$3_memory_ptr(value, pos) + { + let length := array_length_t_array$_t_contract$_C_$55_$3_memory(value) + let srcPtr := array_dataslot_t_array$_t_contract$_C_$55_$3_memory(value) + for { + let i := 0 + } + lt(i, length) + { + i := add(i, 1) + } + { + abi_encode_t_contract$_C_$55_to_t_address(mload(srcPtr), pos) + srcPtr := array_nextElement_t_array$_t_contract$_C_$55_$3_memory(srcPtr) + pos := add(pos, 0x20) + } + } + function abi_encode_t_bool_to_t_bool(value, pos) + { + mstore(pos, cleanup_assert_t_bool(value)) + } + function abi_encode_t_contract$_C_$55_to_t_address(value, pos) + { + mstore(pos, convert_t_contract$_C_$55_to_t_address(value)) + } + function abi_encode_t_uint16_to_t_uint16(value, pos) + { + mstore(pos, cleanup_assert_t_uint16(value)) + } + function abi_encode_t_uint24_to_t_uint24(value, pos) + { + mstore(pos, cleanup_assert_t_uint24(value)) + } + function abi_encode_tuple_t_bool__to_t_bool_(headStart, value0) -> tail + { + tail := add(headStart, 32) + abi_encode_t_bool_to_t_bool(value0, add(headStart, 0)) + } + function abi_encode_tuple_t_uint16_t_uint24_t_array$_t_array$_t_contract$_C_$55_$3_memory_$dyn_memory_ptr__to_t_uint16_t_uint24_t_array$_t_array$_t_address_$3_memory_$dyn_memory_ptr_(headStart, value2, value1, value0) -> tail + { + tail := add(headStart, 96) + abi_encode_t_uint16_to_t_uint16(value0, add(headStart, 0)) + abi_encode_t_uint24_to_t_uint24(value1, add(headStart, 32)) + mstore(add(headStart, 64), sub(tail, headStart)) + tail := abi_encode_t_array$_t_array$_t_contract$_C_$55_$3_memory_$dyn_memory_ptr_to_t_array$_t_array$_t_address_$3_memory_$dyn_memory_ptr(value2, tail) + } + function allocateMemory(size) -> memPtr + { + memPtr := mload(64) + let newFreePtr := add(memPtr, size) + if or(gt(newFreePtr, 0xffffffffffffffff), lt(newFreePtr, memPtr)) + { + revert(0, 0) + } + mstore(64, newFreePtr) + } + function array_allocation_size_t_array$_t_address_$dyn_memory(length) -> size + { + if gt(length, 0xffffffffffffffff) + { + revert(0, 0) + } + size := mul(length, 0x20) + size := add(size, 0x20) + } + function array_allocation_size_t_array$_t_array$_t_uint256_$2_memory_$dyn_memory_ptr(length) -> size + { + if gt(length, 0xffffffffffffffff) + { + revert(0, 0) + } + size := mul(length, 0x20) + size := add(size, 0x20) + } + function array_allocation_size_t_array$_t_uint256_$2_memory(length) -> size + { + if gt(length, 0xffffffffffffffff) + { + revert(0, 0) + } + size := mul(length, 0x20) + } + function array_allocation_size_t_array$_t_uint256_$dyn_memory(length) -> size + { + if gt(length, 0xffffffffffffffff) + { + revert(0, 0) + } + size := mul(length, 0x20) + size := add(size, 0x20) + } + function array_allocation_size_t_array$_t_uint256_$dyn_memory_ptr(length) -> size + { + if gt(length, 0xffffffffffffffff) + { + revert(0, 0) + } + size := mul(length, 0x20) + size := add(size, 0x20) + } + function array_dataslot_t_array$_t_array$_t_contract$_C_$55_$3_memory_$dyn_memory_ptr(memPtr) -> dataPtr + { + dataPtr := add(memPtr, 0x20) + } + function array_dataslot_t_array$_t_contract$_C_$55_$3_memory(memPtr) -> dataPtr + { + dataPtr := memPtr + } + function array_length_t_array$_t_array$_t_contract$_C_$55_$3_memory_$dyn_memory_ptr(value) -> length + { + length := mload(value) + } + function array_length_t_array$_t_contract$_C_$55_$3_memory(value) -> length + { + length := 0x3 + } + function array_nextElement_t_array$_t_array$_t_contract$_C_$55_$3_memory_$dyn_memory_ptr(memPtr) -> nextPtr + { + nextPtr := add(memPtr, 0x20) + } + function array_nextElement_t_array$_t_contract$_C_$55_$3_memory(memPtr) -> nextPtr + { + nextPtr := add(memPtr, 0x20) + } + function cleanup_assert_t_address(value) -> cleaned + { + cleaned := cleanup_assert_t_uint160(value) + } + function cleanup_assert_t_bool(value) -> cleaned + { + cleaned := iszero(iszero(value)) + } + function cleanup_assert_t_uint16(value) -> cleaned + { + cleaned := and(value, 0xFFFF) + } + function cleanup_assert_t_uint160(value) -> cleaned + { + cleaned := and(value, 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF) + } + function cleanup_assert_t_uint24(value) -> cleaned + { + cleaned := and(value, 0xFFFFFF) + } + function cleanup_revert_t_address(value) -> cleaned + { + cleaned := cleanup_assert_t_uint160(value) + } + function cleanup_revert_t_contract$_C_$55(value) -> cleaned + { + cleaned := cleanup_assert_t_address(value) + } + function cleanup_revert_t_uint256(value) -> cleaned + { + cleaned := value + } + function cleanup_revert_t_uint8(value) -> cleaned + { + cleaned := and(value, 0xFF) + } + function convert_t_contract$_C_$55_to_t_address(value) -> converted + { + converted := convert_t_contract$_C_$55_to_t_uint160(value) + } + function convert_t_contract$_C_$55_to_t_uint160(value) -> converted + { + converted := cleanup_assert_t_uint160(value) + } +} +// ---- +// fullSuite +// { +// { +// let _1 := 0x20 +// let _2 := 0 +// let _485 := mload(_2) +// let abi_encode_pos := _1 +// let abi_encode_length_68 := mload(_485) +// mstore(_1, abi_encode_length_68) +// let abi_encode_pos_590 := 64 +// abi_encode_pos := abi_encode_pos_590 +// let abi_encode_srcPtr := add(_485, _1) +// for { +// let abi_encode_i_69 := _2 +// } +// lt(abi_encode_i_69, abi_encode_length_68) +// { +// abi_encode_i_69 := add(abi_encode_i_69, 1) +// } +// { +// let _931 := mload(abi_encode_srcPtr) +// let abi_encode_pos_71_1037 := abi_encode_pos +// let abi_encode_length_72_1038 := 0x3 +// let abi_encode_srcPtr_73_1039 := _931 +// for { +// let abi_encode_i_74_1040 := _2 +// } +// lt(abi_encode_i_74_1040, abi_encode_length_72_1038) +// { +// abi_encode_i_74_1040 := add(abi_encode_i_74_1040, 1) +// } +// { +// mstore(abi_encode_pos_71_1037, and(mload(abi_encode_srcPtr_73_1039), 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)) +// abi_encode_srcPtr_73_1039 := add(abi_encode_srcPtr_73_1039, _1) +// abi_encode_pos_71_1037 := add(abi_encode_pos_71_1037, _1) +// } +// abi_encode_srcPtr := add(abi_encode_srcPtr, _1) +// abi_encode_pos := add(abi_encode_pos, 0x60) +// } +// let _933 := 0x40 +// let _487 := mload(_933) +// let _488 := mload(_1) +// let abi_decode_value0_60_618 +// let abi_decode_value0_60 := abi_decode_value0_60_618 +// let abi_decode_value1_61_619 +// let abi_decode_value1_61 := abi_decode_value1_61_619 +// let abi_decode_value2_620 +// let abi_decode_value2 := abi_decode_value2_620 +// let abi_decode_value3_621 +// let abi_decode_value3 := abi_decode_value3_621 +// if slt(sub(_487, _488), 128) +// { +// revert(_2, _2) +// } +// { +// abi_decode_value0_60 := calldataload(_488) +// } +// { +// abi_decode_value1_61 := calldataload(add(_488, 32)) +// } +// { +// let abi_decode_offset_64 := calldataload(add(_488, abi_encode_pos_590)) +// let _940 := 0xffffffffffffffff +// if gt(abi_decode_offset_64, _940) +// { +// revert(_2, _2) +// } +// let _942 := add(_488, abi_decode_offset_64) +// if iszero(slt(add(_942, 0x1f), _487)) +// { +// revert(_2, _2) +// } +// let abi_decode_length_30_1046 := calldataload(_942) +// if gt(abi_decode_length_30_1046, _940) +// { +// revert(_2, _2) +// } +// let abi_decode_array_allo__561 := mul(abi_decode_length_30_1046, _1) +// let abi_decode_array_29_279_1047 := allocateMemory(add(abi_decode_array_allo__561, _1)) +// let abi_decode_dst_31_1048 := abi_decode_array_29_279_1047 +// mstore(abi_decode_array_29_279_1047, abi_decode_length_30_1046) +// let abi_decode_offset_27_281_1049 := add(_942, _1) +// abi_decode_dst_31_1048 := add(abi_decode_array_29_279_1047, _1) +// let abi_decode_src_32_1050 := abi_decode_offset_27_281_1049 +// if gt(add(add(_942, abi_decode_array_allo__561), _1), _487) +// { +// revert(_2, _2) +// } +// for { +// let abi_decode_i_33_1052 := _2 +// } +// lt(abi_decode_i_33_1052, abi_decode_length_30_1046) +// { +// abi_decode_i_33_1052 := add(abi_decode_i_33_1052, 1) +// } +// { +// mstore(abi_decode_dst_31_1048, calldataload(abi_decode_src_32_1050)) +// abi_decode_dst_31_1048 := add(abi_decode_dst_31_1048, _1) +// abi_decode_src_32_1050 := add(abi_decode_src_32_1050, _1) +// } +// abi_decode_value2 := abi_decode_array_29_279_1047 +// } +// { +// let abi_decode_offset_65 := calldataload(add(_488, 96)) +// let _945 := 0xffffffffffffffff +// if gt(abi_decode_offset_65, _945) +// { +// revert(_2, _2) +// } +// let _947 := add(_488, abi_decode_offset_65) +// let abi_decode__489_1056 := 0x1f +// if iszero(slt(add(_947, abi_decode__489_1056), _487)) +// { +// revert(_2, _2) +// } +// let abi_decode_length_6_1058 := calldataload(_947) +// if gt(abi_decode_length_6_1058, _945) +// { +// revert(_2, _2) +// } +// let abi_decode_array_5_254_1061 := allocateMemory(add(mul(abi_decode_length_6_1058, _1), _1)) +// let abi_decode_dst_7_1062 := abi_decode_array_5_254_1061 +// mstore(abi_decode_array_5_254_1061, abi_decode_length_6_1058) +// let abi_decode_offset_3_256_1063 := add(_947, _1) +// abi_decode_dst_7_1062 := add(abi_decode_array_5_254_1061, _1) +// let abi_decode_src_8_1064 := abi_decode_offset_3_256_1063 +// if gt(add(add(_947, mul(abi_decode_length_6_1058, _933)), _1), _487) +// { +// revert(_2, _2) +// } +// for { +// let abi_decode_i_9_1068 := _2 +// } +// lt(abi_decode_i_9_1068, abi_decode_length_6_1058) +// { +// abi_decode_i_9_1068 := add(abi_decode_i_9_1068, 1) +// } +// { +// if iszero(slt(add(abi_decode_src_8_1064, abi_decode__489_1056), _487)) +// { +// revert(_2, _2) +// } +// let abi_decode_abi_decode_length_14_1069 := 0x2 +// if _2 +// { +// revert(_2, _2) +// } +// let allocateMe_memPtr_315 := mload(abi_encode_pos_590) +// let allocateMe_newFreePtr := add(allocateMe_memPtr_315, abi_encode_pos_590) +// if or(gt(allocateMe_newFreePtr, _945), lt(allocateMe_newFreePtr, allocateMe_memPtr_315)) +// { +// revert(_2, _2) +// } +// mstore(abi_encode_pos_590, allocateMe_newFreePtr) +// let abi_decode_abi_decode_dst_15_1071 := allocateMe_memPtr_315 +// let abi_decode_abi_decode_src_16_1072 := abi_decode_src_8_1064 +// if gt(add(abi_decode_src_8_1064, abi_encode_pos_590), _487) +// { +// revert(_2, _2) +// } +// for { +// let abi_decode_abi_decode_i_17_1073 := _2 +// } +// lt(abi_decode_abi_decode_i_17_1073, abi_decode_abi_decode_length_14_1069) +// { +// abi_decode_abi_decode_i_17_1073 := add(abi_decode_abi_decode_i_17_1073, 1) +// } +// { +// mstore(abi_decode_abi_decode_dst_15_1071, calldataload(abi_decode_abi_decode_src_16_1072)) +// abi_decode_abi_decode_dst_15_1071 := add(abi_decode_abi_decode_dst_15_1071, _1) +// abi_decode_abi_decode_src_16_1072 := add(abi_decode_abi_decode_src_16_1072, _1) +// } +// mstore(abi_decode_dst_7_1062, allocateMe_memPtr_315) +// abi_decode_dst_7_1062 := add(abi_decode_dst_7_1062, _1) +// abi_decode_src_8_1064 := add(abi_decode_src_8_1064, _933) +// } +// abi_decode_value3 := abi_decode_array_5_254_1061 +// } +// sstore(abi_decode_value0_60, abi_decode_value1_61) +// sstore(abi_decode_value2, abi_decode_value3) +// sstore(_2, abi_encode_pos) +// } +// function allocateMemory(size) -> memPtr +// { +// let _199 := 64 +// let memPtr_315 := mload(_199) +// memPtr := memPtr_315 +// let newFreePtr := add(memPtr_315, size) +// if or(gt(newFreePtr, 0xffffffffffffffff), lt(newFreePtr, memPtr_315)) +// { +// let _204 := 0 +// revert(_204, _204) +// } +// mstore(_199, newFreePtr) +// } +// } diff --git a/test/libyul/yulOptimizerTests/fullSuite/medium.yul b/test/libyul/yulOptimizerTests/fullSuite/medium.yul new file mode 100644 index 00000000..deb02068 --- /dev/null +++ b/test/libyul/yulOptimizerTests/fullSuite/medium.yul @@ -0,0 +1,24 @@ +{ + function allocate(size) -> p { + p := mload(0x40) + mstore(0x40, add(p, size)) + } + function array_index_access(array, index) -> p { + p := add(array, mul(index, 0x20)) + } + pop(allocate(0x20)) + let x := allocate(0x40) + mstore(array_index_access(x, 3), 2) +} +// ---- +// fullSuite +// { +// { +// let _18 := 0x20 +// let allocate__7 := 0x40 +// mstore(allocate__7, add(mload(allocate__7), _18)) +// let allocate_p_12_31 := mload(allocate__7) +// mstore(allocate__7, add(allocate_p_12_31, allocate__7)) +// mstore(add(allocate_p_12_31, 96), 2) +// } +// } diff --git a/test/libyul/yulOptimizerTests/functionGrouper/empty_block.yul b/test/libyul/yulOptimizerTests/functionGrouper/empty_block.yul new file mode 100644 index 00000000..f0d49d7b --- /dev/null +++ b/test/libyul/yulOptimizerTests/functionGrouper/empty_block.yul @@ -0,0 +1,24 @@ +// yul +{ let a:u256 { } function f() -> x:bool { let b:u256 := 4:u256 {} for {} f() {} {} } } +// ---- +// functionGrouper +// { +// { +// let a:u256 +// { +// } +// } +// function f() -> x:bool +// { +// let b:u256 := 4:u256 +// { +// } +// for { +// } +// f() +// { +// } +// { +// } +// } +// } diff --git a/test/libyul/yulOptimizerTests/functionGrouper/multi_fun_mixed.yul b/test/libyul/yulOptimizerTests/functionGrouper/multi_fun_mixed.yul new file mode 100644 index 00000000..c830d5da --- /dev/null +++ b/test/libyul/yulOptimizerTests/functionGrouper/multi_fun_mixed.yul @@ -0,0 +1,24 @@ +// yul +{ + let a:u256 + function f() { let b:u256 } + let c:u256 function g() { let d:u256 } + let e:u256 +} +// ---- +// functionGrouper +// { +// { +// let a:u256 +// let c:u256 +// let e:u256 +// } +// function f() +// { +// let b:u256 +// } +// function g() +// { +// let d:u256 +// } +// } diff --git a/test/libyul/yulOptimizerTests/functionGrouper/nested_fun.yul b/test/libyul/yulOptimizerTests/functionGrouper/nested_fun.yul new file mode 100644 index 00000000..4a8be86a --- /dev/null +++ b/test/libyul/yulOptimizerTests/functionGrouper/nested_fun.yul @@ -0,0 +1,27 @@ +// yul +{ + let a:u256 + function f() { + let b:u256 + function g() { + let c:u256 + } + let d:u256 + } +} +// ---- +// functionGrouper +// { +// { +// let a:u256 +// } +// function f() +// { +// let b:u256 +// function g() +// { +// let c:u256 +// } +// let d:u256 +// } +// } diff --git a/test/libyul/yulOptimizerTests/functionGrouper/single_fun.yul b/test/libyul/yulOptimizerTests/functionGrouper/single_fun.yul new file mode 100644 index 00000000..149a44eb --- /dev/null +++ b/test/libyul/yulOptimizerTests/functionGrouper/single_fun.yul @@ -0,0 +1,14 @@ +// yul +{ + let a:u256 function f() {} +} +// ---- +// functionGrouper +// { +// { +// let a:u256 +// } +// function f() +// { +// } +// } diff --git a/test/libyul/yulOptimizerTests/functionGrouper/smoke.yul b/test/libyul/yulOptimizerTests/functionGrouper/smoke.yul new file mode 100644 index 00000000..650a163e --- /dev/null +++ b/test/libyul/yulOptimizerTests/functionGrouper/smoke.yul @@ -0,0 +1,7 @@ +{ } +// ---- +// functionGrouper +// { +// { +// } +// } diff --git a/test/libyul/yulOptimizerTests/functionHoister/empty_block.yul b/test/libyul/yulOptimizerTests/functionHoister/empty_block.yul new file mode 100644 index 00000000..6ea9f59d --- /dev/null +++ b/test/libyul/yulOptimizerTests/functionHoister/empty_block.yul @@ -0,0 +1,26 @@ +// yul +{ + let a:u256 + { } + function f() -> x:bool { + let b:u256 := 4:u256 + { } + for {} f() {} {} + } +} +// ---- +// functionHoister +// { +// let a:u256 +// function f() -> x:bool +// { +// let b:u256 := 4:u256 +// for { +// } +// f() +// { +// } +// { +// } +// } +// } diff --git a/test/libyul/yulOptimizerTests/functionHoister/multi_mixed.yul b/test/libyul/yulOptimizerTests/functionHoister/multi_mixed.yul new file mode 100644 index 00000000..1e3bc5a1 --- /dev/null +++ b/test/libyul/yulOptimizerTests/functionHoister/multi_mixed.yul @@ -0,0 +1,23 @@ +// yul +{ + let a:u256 + function f() { let b:u256 } + let c:u256 + function g() { let d:u256 } + let e:u256 +} +// ---- +// functionHoister +// { +// let a:u256 +// let c:u256 +// let e:u256 +// function f() +// { +// let b:u256 +// } +// function g() +// { +// let d:u256 +// } +// } diff --git a/test/libyul/yulOptimizerTests/functionHoister/nested.yul b/test/libyul/yulOptimizerTests/functionHoister/nested.yul new file mode 100644 index 00000000..20f094f1 --- /dev/null +++ b/test/libyul/yulOptimizerTests/functionHoister/nested.yul @@ -0,0 +1,23 @@ +// yul +{ + let a:u256 + function f() { + let b:u256 + function g() { let c:u256 } + let d:u256 + } +} +// ---- +// functionHoister +// { +// let a:u256 +// function g() +// { +// let c:u256 +// } +// function f() +// { +// let b:u256 +// let d:u256 +// } +// } diff --git a/test/libyul/yulOptimizerTests/functionHoister/single.yul b/test/libyul/yulOptimizerTests/functionHoister/single.yul new file mode 100644 index 00000000..ba922612 --- /dev/null +++ b/test/libyul/yulOptimizerTests/functionHoister/single.yul @@ -0,0 +1,13 @@ +// yul +{ + let a:u256 + function f() {} +} +// ---- +// functionHoister +// { +// let a:u256 +// function f() +// { +// } +// } diff --git a/test/libyul/yulOptimizerTests/functionHoister/smoke.yul b/test/libyul/yulOptimizerTests/functionHoister/smoke.yul new file mode 100644 index 00000000..35c1ce5f --- /dev/null +++ b/test/libyul/yulOptimizerTests/functionHoister/smoke.yul @@ -0,0 +1,6 @@ +{ +} +// ---- +// functionHoister +// { +// } diff --git a/test/libyul/yulOptimizerTests/mainFunction/empty_block.yul b/test/libyul/yulOptimizerTests/mainFunction/empty_block.yul new file mode 100644 index 00000000..bae6bd48 --- /dev/null +++ b/test/libyul/yulOptimizerTests/mainFunction/empty_block.yul @@ -0,0 +1,33 @@ +// yul +{ + let a:u256 + { } + function f() -> x:bool { + let b:u256 := 4:u256 + {} + for {} f() {} {} + } +} +// ---- +// mainFunction +// { +// function main() +// { +// let a:u256 +// { +// } +// } +// function f() -> x:bool +// { +// let b:u256 := 4:u256 +// { +// } +// for { +// } +// f() +// { +// } +// { +// } +// } +// } diff --git a/test/libyul/yulOptimizerTests/mainFunction/multi_fun_mixed.yul b/test/libyul/yulOptimizerTests/mainFunction/multi_fun_mixed.yul new file mode 100644 index 00000000..dd5caaec --- /dev/null +++ b/test/libyul/yulOptimizerTests/mainFunction/multi_fun_mixed.yul @@ -0,0 +1,26 @@ +// yul +{ + let a:u256 + function f() { let b:u256 } + let c:u256 + function g() { let d:u256 } + let e:u256 +} +// ---- +// mainFunction +// { +// function main() +// { +// let a:u256 +// let c:u256 +// let e:u256 +// } +// function f() +// { +// let b:u256 +// } +// function g() +// { +// let d:u256 +// } +// } diff --git a/test/libyul/yulOptimizerTests/mainFunction/nested_fun.yul b/test/libyul/yulOptimizerTests/mainFunction/nested_fun.yul new file mode 100644 index 00000000..309b97cc --- /dev/null +++ b/test/libyul/yulOptimizerTests/mainFunction/nested_fun.yul @@ -0,0 +1,26 @@ +// yul +{ + let a:u256 + function f() { + let b:u256 + function g() { let c:u256} + let d:u256 + } +} +// ---- +// mainFunction +// { +// function main() +// { +// let a:u256 +// } +// function f() +// { +// let b:u256 +// function g() +// { +// let c:u256 +// } +// let d:u256 +// } +// } diff --git a/test/libyul/yulOptimizerTests/mainFunction/sigle_fun.yul b/test/libyul/yulOptimizerTests/mainFunction/sigle_fun.yul new file mode 100644 index 00000000..fa9a8f41 --- /dev/null +++ b/test/libyul/yulOptimizerTests/mainFunction/sigle_fun.yul @@ -0,0 +1,16 @@ +// yul +{ + let a:u256 + function f() {} +} +// ---- +// mainFunction +// { +// function main() +// { +// let a:u256 +// } +// function f() +// { +// } +// } diff --git a/test/libyul/yulOptimizerTests/mainFunction/smoke.yul b/test/libyul/yulOptimizerTests/mainFunction/smoke.yul new file mode 100644 index 00000000..7be14746 --- /dev/null +++ b/test/libyul/yulOptimizerTests/mainFunction/smoke.yul @@ -0,0 +1,9 @@ +// yul +{} +// ---- +// mainFunction +// { +// function main() +// { +// } +// } diff --git a/test/libyul/yulOptimizerTests/redundantAssignEliminator/for.yul b/test/libyul/yulOptimizerTests/redundantAssignEliminator/for.yul new file mode 100644 index 00000000..d9bbd86d --- /dev/null +++ b/test/libyul/yulOptimizerTests/redundantAssignEliminator/for.yul @@ -0,0 +1,26 @@ +{ + for { + let a := 2 + // Should not be removed, even though you might think + // it goes out of scope + a := 3 + } a { a := add(a, 1) } + { + a := 7 + } +} +// ---- +// redundantAssignEliminator +// { +// for { +// let a := 2 +// a := 3 +// } +// a +// { +// a := add(a, 1) +// } +// { +// a := 7 +// } +// } diff --git a/test/libyul/yulOptimizerTests/redundantAssignEliminator/for_branch.yul b/test/libyul/yulOptimizerTests/redundantAssignEliminator/for_branch.yul new file mode 100644 index 00000000..7f5e97ce --- /dev/null +++ b/test/libyul/yulOptimizerTests/redundantAssignEliminator/for_branch.yul @@ -0,0 +1,31 @@ +{ + let x + let y + // Cannot be removed, because we might skip the loop + x := 1 + for { } calldataload(0) { } + { + // Cannot be removed + x := 2 + // Can be removed + y := 3 + } + y := 8 + mstore(x, 0) +} +// ---- +// redundantAssignEliminator +// { +// let x +// let y +// x := 1 +// for { +// } +// calldataload(0) +// { +// } +// { +// x := 2 +// } +// mstore(x, 0) +// } diff --git a/test/libyul/yulOptimizerTests/redundantAssignEliminator/for_rerun.yul b/test/libyul/yulOptimizerTests/redundantAssignEliminator/for_rerun.yul new file mode 100644 index 00000000..65eb2838 --- /dev/null +++ b/test/libyul/yulOptimizerTests/redundantAssignEliminator/for_rerun.yul @@ -0,0 +1,27 @@ +{ + let x + // Cannot be removed, because we might run the loop only once + x := 1 + for { } calldataload(0) { } + { + mstore(x, 2) + // Cannot be removed because of the line above + x := 2 + } + x := 3 +} +// ---- +// redundantAssignEliminator +// { +// let x +// x := 1 +// for { +// } +// calldataload(0) +// { +// } +// { +// mstore(x, 2) +// x := 2 +// } +// } diff --git a/test/libyul/yulOptimizerTests/redundantAssignEliminator/function.yul b/test/libyul/yulOptimizerTests/redundantAssignEliminator/function.yul new file mode 100644 index 00000000..5bb920ec --- /dev/null +++ b/test/libyul/yulOptimizerTests/redundantAssignEliminator/function.yul @@ -0,0 +1,23 @@ +{ + let r + r := 1 + function f(x, y) -> a, b { + // Can be removed, is param + x := 1 + y := 2 + // Cannot be removed, is return param + a := 3 + b := 4 + } + r := 2 +} +// ---- +// redundantAssignEliminator +// { +// let r +// function f(x, y) -> a, b +// { +// a := 3 +// b := 4 +// } +// } diff --git a/test/libyul/yulOptimizerTests/redundantAssignEliminator/if.yul b/test/libyul/yulOptimizerTests/redundantAssignEliminator/if.yul new file mode 100644 index 00000000..958bfc66 --- /dev/null +++ b/test/libyul/yulOptimizerTests/redundantAssignEliminator/if.yul @@ -0,0 +1,24 @@ +{ + let c + let d + c := calldataload(0) + d := 1 + if c { + d := 2 + } + // This enforces that none of the assignments above can be removed. + mstore(0, d) +} +// ---- +// redundantAssignEliminator +// { +// let c +// let d +// c := calldataload(0) +// d := 1 +// if c +// { +// d := 2 +// } +// mstore(0, d) +// } diff --git a/test/libyul/yulOptimizerTests/redundantAssignEliminator/if_overwrite_all_branches.yul b/test/libyul/yulOptimizerTests/redundantAssignEliminator/if_overwrite_all_branches.yul new file mode 100644 index 00000000..e47c31d1 --- /dev/null +++ b/test/libyul/yulOptimizerTests/redundantAssignEliminator/if_overwrite_all_branches.yul @@ -0,0 +1,24 @@ +{ + let c + let d + c := calldataload(0) + // This assignment will be overwritten in all branches and thus can be removed. + d := 1 + if c { + d := 2 + } + d := 3 + mstore(0, d) +} +// ---- +// redundantAssignEliminator +// { +// let c +// let d +// c := calldataload(0) +// if c +// { +// } +// d := 3 +// mstore(0, d) +// } diff --git a/test/libyul/yulOptimizerTests/redundantAssignEliminator/if_used_in_one_branch.yul b/test/libyul/yulOptimizerTests/redundantAssignEliminator/if_used_in_one_branch.yul new file mode 100644 index 00000000..00065ed2 --- /dev/null +++ b/test/libyul/yulOptimizerTests/redundantAssignEliminator/if_used_in_one_branch.yul @@ -0,0 +1,25 @@ +{ + let c + let d + c := calldataload(0) + d := 1 + if c { + // Uses the assignment above + d := d + } + d := 3 + mstore(0, d) +} +// ---- +// redundantAssignEliminator +// { +// let c +// let d +// c := calldataload(0) +// d := 1 +// if c +// { +// } +// d := 3 +// mstore(0, d) +// } diff --git a/test/libyul/yulOptimizerTests/redundantAssignEliminator/multi_assign.yul b/test/libyul/yulOptimizerTests/redundantAssignEliminator/multi_assign.yul new file mode 100644 index 00000000..26bcfc72 --- /dev/null +++ b/test/libyul/yulOptimizerTests/redundantAssignEliminator/multi_assign.yul @@ -0,0 +1,19 @@ +{ + function f() -> a, b {} + let x, y + x := 1 + x := 2 + // Will not be used, but is a multi-assign, so not removed. + x, y := f() + x := 3 + y := 4 +} +// ---- +// redundantAssignEliminator +// { +// function f() -> a, b +// { +// } +// let x, y +// x, y := f() +// } diff --git a/test/libyul/yulOptimizerTests/redundantAssignEliminator/multivar.yul b/test/libyul/yulOptimizerTests/redundantAssignEliminator/multivar.yul new file mode 100644 index 00000000..cf646126 --- /dev/null +++ b/test/libyul/yulOptimizerTests/redundantAssignEliminator/multivar.yul @@ -0,0 +1,15 @@ +{ + let a := 2 + a := 7 + let b := 8 + b := a + a := b +} +// ---- +// redundantAssignEliminator +// { +// let a := 2 +// a := 7 +// let b := 8 +// b := a +// } diff --git a/test/libyul/yulOptimizerTests/redundantAssignEliminator/non_movable.yul b/test/libyul/yulOptimizerTests/redundantAssignEliminator/non_movable.yul new file mode 100644 index 00000000..ae3e5226 --- /dev/null +++ b/test/libyul/yulOptimizerTests/redundantAssignEliminator/non_movable.yul @@ -0,0 +1,11 @@ +{ + let a + a := 0 + a := mload(0) +} +// ---- +// redundantAssignEliminator +// { +// let a +// a := mload(0) +// } diff --git a/test/libyul/yulOptimizerTests/redundantAssignEliminator/scopes.yul b/test/libyul/yulOptimizerTests/redundantAssignEliminator/scopes.yul new file mode 100644 index 00000000..702f854d --- /dev/null +++ b/test/libyul/yulOptimizerTests/redundantAssignEliminator/scopes.yul @@ -0,0 +1,16 @@ +{ + let a + { + let b + b := 2 + a := 2 + } +} +// ---- +// redundantAssignEliminator +// { +// let a +// { +// let b +// } +// } diff --git a/test/libyul/yulOptimizerTests/redundantAssignEliminator/simple.yul b/test/libyul/yulOptimizerTests/redundantAssignEliminator/simple.yul new file mode 100644 index 00000000..913a7694 --- /dev/null +++ b/test/libyul/yulOptimizerTests/redundantAssignEliminator/simple.yul @@ -0,0 +1,10 @@ +{ + let a + a := 1 + a := 2 +} +// ---- +// redundantAssignEliminator +// { +// let a +// } diff --git a/test/libyul/yulOptimizerTests/redundantAssignEliminator/switch_overwrite_in_all.yul b/test/libyul/yulOptimizerTests/redundantAssignEliminator/switch_overwrite_in_all.yul new file mode 100644 index 00000000..96265576 --- /dev/null +++ b/test/libyul/yulOptimizerTests/redundantAssignEliminator/switch_overwrite_in_all.yul @@ -0,0 +1,22 @@ +{ + let x + // Will be overwritten in all branches + x := 1 + switch calldataload(0) + case 0 { x := 2 } + default { x := 3 } + mstore(x, 0) +} +// ---- +// redundantAssignEliminator +// { +// let x +// switch calldataload(0) +// case 0 { +// x := 2 +// } +// default { +// x := 3 +// } +// mstore(x, 0) +// } diff --git a/test/libyul/yulOptimizerTests/redundantAssignEliminator/switch_overwrite_in_one.yul b/test/libyul/yulOptimizerTests/redundantAssignEliminator/switch_overwrite_in_one.yul new file mode 100644 index 00000000..cbe859ed --- /dev/null +++ b/test/libyul/yulOptimizerTests/redundantAssignEliminator/switch_overwrite_in_one.yul @@ -0,0 +1,19 @@ +{ + let x + // Will NOT be overwritten in all branches + x := 1 + switch calldataload(0) + case 0 { x := 2 } + mstore(x, 0) +} +// ---- +// redundantAssignEliminator +// { +// let x +// x := 1 +// switch calldataload(0) +// case 0 { +// x := 2 +// } +// mstore(x, 0) +// } diff --git a/test/libyul/yulOptimizerTests/redundantAssignEliminator/switch_overwrite_use_combination.yul b/test/libyul/yulOptimizerTests/redundantAssignEliminator/switch_overwrite_use_combination.yul new file mode 100644 index 00000000..1a3b26eb --- /dev/null +++ b/test/libyul/yulOptimizerTests/redundantAssignEliminator/switch_overwrite_use_combination.yul @@ -0,0 +1,23 @@ +{ + let x + // Will be used in some and overwritten in others + x := 1 + switch calldataload(0) + case 0 { x := 2 } + default { mstore(x, 1) } + mstore(x, 0) +} +// ---- +// redundantAssignEliminator +// { +// let x +// x := 1 +// switch calldataload(0) +// case 0 { +// x := 2 +// } +// default { +// mstore(x, 1) +// } +// mstore(x, 0) +// } diff --git a/test/libyul/yulOptimizerTests/redundantAssignEliminator/switch_unused.yul b/test/libyul/yulOptimizerTests/redundantAssignEliminator/switch_unused.yul new file mode 100644 index 00000000..cc78b74d --- /dev/null +++ b/test/libyul/yulOptimizerTests/redundantAssignEliminator/switch_unused.yul @@ -0,0 +1,16 @@ +{ + let x + // Not referenced anywhere. + x := 1 + switch calldataload(0) + case 0 { mstore(0, 1) } +} +// ---- +// redundantAssignEliminator +// { +// let x +// switch calldataload(0) +// case 0 { +// mstore(0, 1) +// } +// } diff --git a/test/libyul/yulOptimizerTests/rematerialiser/branches_for1.yul b/test/libyul/yulOptimizerTests/rematerialiser/branches_for1.yul new file mode 100644 index 00000000..dbd1ee63 --- /dev/null +++ b/test/libyul/yulOptimizerTests/rematerialiser/branches_for1.yul @@ -0,0 +1,21 @@ +{ + let a := 1 + for { pop(a) } a { pop(a) } { + pop(a) + } +} +// ---- +// rematerialiser +// { +// let a := 1 +// for { +// pop(1) +// } +// 1 +// { +// pop(1) +// } +// { +// pop(1) +// } +// } diff --git a/test/libyul/yulOptimizerTests/rematerialiser/branches_for2.yul b/test/libyul/yulOptimizerTests/rematerialiser/branches_for2.yul new file mode 100644 index 00000000..6a52e045 --- /dev/null +++ b/test/libyul/yulOptimizerTests/rematerialiser/branches_for2.yul @@ -0,0 +1,25 @@ +{ + let a := 1 + for { pop(a) } a { pop(a) } { + a := 7 + let c := a + } + let x := a +} +// ---- +// rematerialiser +// { +// let a := 1 +// for { +// pop(1) +// } +// a +// { +// pop(7) +// } +// { +// a := 7 +// let c := 7 +// } +// let x := a +// } diff --git a/test/libyul/yulOptimizerTests/rematerialiser/branches_for_declared_in_init1.yul b/test/libyul/yulOptimizerTests/rematerialiser/branches_for_declared_in_init1.yul new file mode 100644 index 00000000..fc816419 --- /dev/null +++ b/test/libyul/yulOptimizerTests/rematerialiser/branches_for_declared_in_init1.yul @@ -0,0 +1,23 @@ +{ + let b := 0 + for { let a := 1 pop(a) } a { pop(a) } { + b := 1 pop(a) + } +} +// ---- +// rematerialiser +// { +// let b := 0 +// for { +// let a := 1 +// pop(1) +// } +// 1 +// { +// pop(1) +// } +// { +// b := 1 +// pop(1) +// } +// } diff --git a/test/libyul/yulOptimizerTests/rematerialiser/branches_for_declared_in_init2.yul b/test/libyul/yulOptimizerTests/rematerialiser/branches_for_declared_in_init2.yul new file mode 100644 index 00000000..3d916890 --- /dev/null +++ b/test/libyul/yulOptimizerTests/rematerialiser/branches_for_declared_in_init2.yul @@ -0,0 +1,24 @@ +{ + let b := 0 + for { let a := 1 pop(a) } lt(a, 0) { pop(a) a := add(a, 3) } { + b := 1 pop(a) + } +} +// ---- +// rematerialiser +// { +// let b := 0 +// for { +// let a := 1 +// pop(1) +// } +// lt(a, 0) +// { +// pop(a) +// a := add(a, 3) +// } +// { +// b := 1 +// pop(a) +// } +// } diff --git a/test/libyul/yulOptimizerTests/rematerialiser/branches_if.yul b/test/libyul/yulOptimizerTests/rematerialiser/branches_if.yul new file mode 100644 index 00000000..c148c2f2 --- /dev/null +++ b/test/libyul/yulOptimizerTests/rematerialiser/branches_if.yul @@ -0,0 +1,18 @@ +{ + let a := 1 + let b := 2 + if b { pop(b) b := a } + let c := b +} +// ---- +// rematerialiser +// { +// let a := 1 +// let b := 2 +// if 2 +// { +// pop(2) +// b := 1 +// } +// let c := b +// } diff --git a/test/libyul/yulOptimizerTests/rematerialiser/branches_switch.yul b/test/libyul/yulOptimizerTests/rematerialiser/branches_switch.yul new file mode 100644 index 00000000..8f70a79d --- /dev/null +++ b/test/libyul/yulOptimizerTests/rematerialiser/branches_switch.yul @@ -0,0 +1,24 @@ +{ + let a := 1 + let b := 2 + switch number() + case 1 { b := a } + default { let x := a let y := b b := a } + pop(add(a, b)) +} +// ---- +// rematerialiser +// { +// let a := 1 +// let b := 2 +// switch number() +// case 1 { +// b := 1 +// } +// default { +// let x := 1 +// let y := b +// b := 1 +// } +// pop(add(1, b)) +// } diff --git a/test/libyul/yulOptimizerTests/rematerialiser/do_not_move_out_of_scope.yul b/test/libyul/yulOptimizerTests/rematerialiser/do_not_move_out_of_scope.yul new file mode 100644 index 00000000..891a5043 --- /dev/null +++ b/test/libyul/yulOptimizerTests/rematerialiser/do_not_move_out_of_scope.yul @@ -0,0 +1,19 @@ +// Cannot replace `let b := x` by `let b := a` since a is out of scope. +{ + let x + { + let a := sload(0) + x := a + } + let b := x +} +// ---- +// rematerialiser +// { +// let x +// { +// let a := sload(0) +// x := a +// } +// let b := x +// } diff --git a/test/libyul/yulOptimizerTests/rematerialiser/do_not_remat_large_amounts_of_code1.yul b/test/libyul/yulOptimizerTests/rematerialiser/do_not_remat_large_amounts_of_code1.yul new file mode 100644 index 00000000..016fa0d7 --- /dev/null +++ b/test/libyul/yulOptimizerTests/rematerialiser/do_not_remat_large_amounts_of_code1.yul @@ -0,0 +1,10 @@ +{ + let x := add(mul(calldataload(2), calldataload(4)), mul(2, calldatasize())) + let b := x +} +// ---- +// rematerialiser +// { +// let x := add(mul(calldataload(2), calldataload(4)), mul(2, calldatasize())) +// let b := x +// } diff --git a/test/libyul/yulOptimizerTests/rematerialiser/do_not_remat_large_amounts_of_code2.yul b/test/libyul/yulOptimizerTests/rematerialiser/do_not_remat_large_amounts_of_code2.yul new file mode 100644 index 00000000..d95dc1fc --- /dev/null +++ b/test/libyul/yulOptimizerTests/rematerialiser/do_not_remat_large_amounts_of_code2.yul @@ -0,0 +1,10 @@ +{ + let x := add(mul(calldataload(2), calldataload(4)), calldatasize()) + let b := x +} +// ---- +// rematerialiser +// { +// let x := add(mul(calldataload(2), calldataload(4)), calldatasize()) +// let b := add(mul(calldataload(2), calldataload(4)), calldatasize()) +// } diff --git a/test/libyul/yulOptimizerTests/rematerialiser/expression.yul b/test/libyul/yulOptimizerTests/rematerialiser/expression.yul new file mode 100644 index 00000000..a801677d --- /dev/null +++ b/test/libyul/yulOptimizerTests/rematerialiser/expression.yul @@ -0,0 +1,10 @@ +{ + let a := add(mul(calldatasize(), 2), number()) + let b := add(a, a) +} +// ---- +// rematerialiser +// { +// let a := add(mul(calldatasize(), 2), number()) +// let b := add(add(mul(calldatasize(), 2), number()), add(mul(calldatasize(), 2), number())) +// } diff --git a/test/libyul/yulOptimizerTests/rematerialiser/non_movable_function.yul b/test/libyul/yulOptimizerTests/rematerialiser/non_movable_function.yul new file mode 100644 index 00000000..9a041dfc --- /dev/null +++ b/test/libyul/yulOptimizerTests/rematerialiser/non_movable_function.yul @@ -0,0 +1,18 @@ +{ + function f(x) -> y {} + let a := 1 + let b := f(a) + let c := a + mstore(add(a, b), c) +} +// ---- +// rematerialiser +// { +// function f(x) -> y +// { +// } +// let a := 1 +// let b := f(1) +// let c := 1 +// mstore(add(1, b), 1) +// } diff --git a/test/libyul/yulOptimizerTests/rematerialiser/non_movable_instruction.yul b/test/libyul/yulOptimizerTests/rematerialiser/non_movable_instruction.yul new file mode 100644 index 00000000..8767abc9 --- /dev/null +++ b/test/libyul/yulOptimizerTests/rematerialiser/non_movable_instruction.yul @@ -0,0 +1,14 @@ +{ + let a := 1 + let b := mload(a) + let c := a + mstore(add(a, b), c) +} +// ---- +// rematerialiser +// { +// let a := 1 +// let b := mload(1) +// let c := 1 +// mstore(add(1, b), 1) +// } diff --git a/test/libyul/yulOptimizerTests/rematerialiser/reassign.yul b/test/libyul/yulOptimizerTests/rematerialiser/reassign.yul new file mode 100644 index 00000000..47124658 --- /dev/null +++ b/test/libyul/yulOptimizerTests/rematerialiser/reassign.yul @@ -0,0 +1,21 @@ +{ + let a := extcodesize(0) + let b := a + let c := b + a := 2 + let d := add(b, c) + pop(a) pop(b) pop(c) pop(d) +} +// ---- +// rematerialiser +// { +// let a := extcodesize(0) +// let b := a +// let c := a +// a := 2 +// let d := add(b, c) +// pop(2) +// pop(b) +// pop(c) +// pop(add(b, c)) +// } diff --git a/test/libyul/yulOptimizerTests/rematerialiser/reassignment.yul b/test/libyul/yulOptimizerTests/rematerialiser/reassignment.yul new file mode 100644 index 00000000..13238780 --- /dev/null +++ b/test/libyul/yulOptimizerTests/rematerialiser/reassignment.yul @@ -0,0 +1,19 @@ +{ + let a := 1 + pop(a) + if a { a := 2 } + let b := mload(a) + pop(b) +} +// ---- +// rematerialiser +// { +// let a := 1 +// pop(1) +// if 1 +// { +// a := 2 +// } +// let b := mload(a) +// pop(b) +// } diff --git a/test/libyul/yulOptimizerTests/rematerialiser/smoke.yul b/test/libyul/yulOptimizerTests/rematerialiser/smoke.yul new file mode 100644 index 00000000..2423db32 --- /dev/null +++ b/test/libyul/yulOptimizerTests/rematerialiser/smoke.yul @@ -0,0 +1,5 @@ +{} +// ---- +// rematerialiser +// { +// } diff --git a/test/libyul/yulOptimizerTests/rematerialiser/trivial.yul b/test/libyul/yulOptimizerTests/rematerialiser/trivial.yul new file mode 100644 index 00000000..d29ea98a --- /dev/null +++ b/test/libyul/yulOptimizerTests/rematerialiser/trivial.yul @@ -0,0 +1,12 @@ +{ + let a := 1 + let b := a + mstore(0, b) +} +// ---- +// rematerialiser +// { +// let a := 1 +// let b := 1 +// mstore(0, 1) +// } diff --git a/test/libyul/yulOptimizerTests/rematerialiser/update_asignment_remat.yul b/test/libyul/yulOptimizerTests/rematerialiser/update_asignment_remat.yul new file mode 100644 index 00000000..7d35fee0 --- /dev/null +++ b/test/libyul/yulOptimizerTests/rematerialiser/update_asignment_remat.yul @@ -0,0 +1,13 @@ +// We cannot substitute `a` in `let b := a` +{ + let a := extcodesize(0) + a := mul(a, 2) + let b := a +} +// ---- +// rematerialiser +// { +// let a := extcodesize(0) +// a := mul(a, 2) +// let b := a +// } diff --git a/test/libyul/yulOptimizerTests/splitJoin/control_flow.yul b/test/libyul/yulOptimizerTests/splitJoin/control_flow.yul new file mode 100644 index 00000000..ad609c74 --- /dev/null +++ b/test/libyul/yulOptimizerTests/splitJoin/control_flow.yul @@ -0,0 +1,26 @@ +{ + if mul(add(calldataload(0), 2), 3) { + for { let a := 2 } lt(a, mload(a)) { a := add(a, mul(a, 2)) } { + let b := mul(add(a, 2), 4) + sstore(b, mul(b, 2)) + } + } +} +// ---- +// splitJoin +// { +// if mul(add(calldataload(0), 2), 3) +// { +// for { +// let a := 2 +// } +// lt(a, mload(a)) +// { +// a := add(a, mul(a, 2)) +// } +// { +// let b := mul(add(a, 2), 4) +// sstore(b, mul(b, 2)) +// } +// } +// } diff --git a/test/libyul/yulOptimizerTests/splitJoin/functions.yul b/test/libyul/yulOptimizerTests/splitJoin/functions.yul new file mode 100644 index 00000000..549fc550 --- /dev/null +++ b/test/libyul/yulOptimizerTests/splitJoin/functions.yul @@ -0,0 +1,30 @@ +{ + let x := f(0) + function f(y) -> r { + r := mload(mul(6, add(y, 0x20))) + } + for { let a := 2 } lt(a, mload(a)) { a := add(a, mul(a, 2)) } { + let b := mul(add(a, f(a)), 4) + sstore(b, mul(b, 2)) + } +} +// ---- +// splitJoin +// { +// let x := f(0) +// function f(y) -> r +// { +// r := mload(mul(6, add(y, 0x20))) +// } +// for { +// let a := 2 +// } +// lt(a, mload(a)) +// { +// a := add(a, mul(a, 2)) +// } +// { +// let b := mul(add(a, f(a)), 4) +// sstore(b, mul(b, 2)) +// } +// } diff --git a/test/libyul/yulOptimizerTests/splitJoin/smoke.yul b/test/libyul/yulOptimizerTests/splitJoin/smoke.yul new file mode 100644 index 00000000..4b133029 --- /dev/null +++ b/test/libyul/yulOptimizerTests/splitJoin/smoke.yul @@ -0,0 +1,5 @@ +{} +// ---- +// splitJoin +// { +// } diff --git a/test/libyul/yulOptimizerTests/ssaPlusCleanup/control_structures.yul b/test/libyul/yulOptimizerTests/ssaPlusCleanup/control_structures.yul new file mode 100644 index 00000000..d2408343 --- /dev/null +++ b/test/libyul/yulOptimizerTests/ssaPlusCleanup/control_structures.yul @@ -0,0 +1,35 @@ +{ + function copy(from, to) -> length { + length := mload(from) + mstore(to, length) + from := add(from, 0x20) + to := add(to, 0x20) + for { let x := 1 } lt(x, length) { x := add(x, 0x20) } { + mstore(add(to, x), mload(add(from, x))) + } + } +} +// ---- +// ssaPlusCleanup +// { +// function copy(from, to) -> length +// { +// let length_1 := mload(from) +// length := length_1 +// mstore(to, length_1) +// let from_2 := add(from, 0x20) +// let to_3 := add(to, 0x20) +// for { +// let x_4 := 1 +// let x := x_4 +// } +// lt(x, length_1) +// { +// let x_5 := add(x, 0x20) +// x := x_5 +// } +// { +// mstore(add(to_3, x), mload(add(from_2, x))) +// } +// } +// } diff --git a/test/libyul/yulOptimizerTests/ssaPlusCleanup/multi_reassign.yul b/test/libyul/yulOptimizerTests/ssaPlusCleanup/multi_reassign.yul new file mode 100644 index 00000000..ddb33aa0 --- /dev/null +++ b/test/libyul/yulOptimizerTests/ssaPlusCleanup/multi_reassign.yul @@ -0,0 +1,17 @@ +{ + let a := 1 + a := 2 + a := 3 + a := 4 + mstore(0, a) +} +// ---- +// ssaPlusCleanup +// { +// let a_1 := 1 +// let a := a_1 +// let a_2 := 2 +// let a_3 := 3 +// let a_4 := 4 +// mstore(0, a_4) +// } diff --git a/test/libyul/yulOptimizerTests/ssaPlusCleanup/multi_reassign_with_use.yul b/test/libyul/yulOptimizerTests/ssaPlusCleanup/multi_reassign_with_use.yul new file mode 100644 index 00000000..67a6c5d3 --- /dev/null +++ b/test/libyul/yulOptimizerTests/ssaPlusCleanup/multi_reassign_with_use.yul @@ -0,0 +1,17 @@ +{ + let a := 1 + a := add(a, 2) + a := add(a, 3) + a := mload(add(a, 4)) + mstore(0, a) +} +// ---- +// ssaPlusCleanup +// { +// let a_1 := 1 +// let a := a_1 +// let a_2 := add(a_1, 2) +// let a_3 := add(a_2, 3) +// let a_4 := mload(add(a_3, 4)) +// mstore(0, a_4) +// } diff --git a/test/libyul/yulOptimizerTests/ssaTransform/branches.yul b/test/libyul/yulOptimizerTests/ssaTransform/branches.yul new file mode 100644 index 00000000..c089fe70 --- /dev/null +++ b/test/libyul/yulOptimizerTests/ssaTransform/branches.yul @@ -0,0 +1,25 @@ +{ + let a := 1 + a := add(a, 1) + if a { + a := add(a, 1) + } + a := add(a, 1) + mstore(a, 1) +} +// ---- +// ssaTransform +// { +// let a_1 := 1 +// let a := a_1 +// let a_2 := add(a_1, 1) +// a := a_2 +// if a_2 +// { +// let a_3 := add(a_2, 1) +// a := a_3 +// } +// let a_4 := add(a, 1) +// a := a_4 +// mstore(a_4, 1) +// } diff --git a/test/libyul/yulOptimizerTests/ssaTransform/for_reassign_body.yul b/test/libyul/yulOptimizerTests/ssaTransform/for_reassign_body.yul new file mode 100644 index 00000000..41640346 --- /dev/null +++ b/test/libyul/yulOptimizerTests/ssaTransform/for_reassign_body.yul @@ -0,0 +1,26 @@ +{ + let a := mload(0) + for { mstore(0, a) } a { mstore(0, a) } + { + a := add(a, 3) + } + mstore(0, a) +} +// ---- +// ssaTransform +// { +// let a_1 := mload(0) +// let a := a_1 +// for { +// mstore(0, a_1) +// } +// a +// { +// mstore(0, a) +// } +// { +// let a_2 := add(a, 3) +// a := a_2 +// } +// mstore(0, a) +// } diff --git a/test/libyul/yulOptimizerTests/ssaTransform/for_reassign_init.yul b/test/libyul/yulOptimizerTests/ssaTransform/for_reassign_init.yul new file mode 100644 index 00000000..821a5b2a --- /dev/null +++ b/test/libyul/yulOptimizerTests/ssaTransform/for_reassign_init.yul @@ -0,0 +1,26 @@ +{ + let a := mload(0) + for { a := add(a, 3) } a { mstore(0, a) } + { + mstore(0, a) + } + mstore(0, a) +} +// ---- +// ssaTransform +// { +// let a_1 := mload(0) +// let a := a_1 +// for { +// let a_2 := add(a_1, 3) +// a := a_2 +// } +// a +// { +// mstore(0, a) +// } +// { +// mstore(0, a) +// } +// mstore(0, a) +// } diff --git a/test/libyul/yulOptimizerTests/ssaTransform/for_reassign_post.yul b/test/libyul/yulOptimizerTests/ssaTransform/for_reassign_post.yul new file mode 100644 index 00000000..1fc075bc --- /dev/null +++ b/test/libyul/yulOptimizerTests/ssaTransform/for_reassign_post.yul @@ -0,0 +1,26 @@ +{ + let a := mload(0) + for { mstore(0, a) } a { a := add(a, 3) } + { + mstore(0, a) + } + mstore(0, a) +} +// ---- +// ssaTransform +// { +// let a_1 := mload(0) +// let a := a_1 +// for { +// mstore(0, a_1) +// } +// a +// { +// let a_2 := add(a, 3) +// a := a_2 +// } +// { +// mstore(0, a) +// } +// mstore(0, a) +// } diff --git a/test/libyul/yulOptimizerTests/ssaTransform/for_simple.yul b/test/libyul/yulOptimizerTests/ssaTransform/for_simple.yul new file mode 100644 index 00000000..273d3811 --- /dev/null +++ b/test/libyul/yulOptimizerTests/ssaTransform/for_simple.yul @@ -0,0 +1,47 @@ +{ + let a := mload(0) + a := add(a, 1) + if a { + a := add(a, 2) + } + { + a := add(a, 4) + } + for { a := add(a, 3) } a { a := add(a, 6) } + { + a := add(a, 12) + } + a := add(a, 8) +} +// ---- +// ssaTransform +// { +// let a_1 := mload(0) +// let a := a_1 +// let a_2 := add(a_1, 1) +// a := a_2 +// if a_2 +// { +// let a_3 := add(a_2, 2) +// a := a_3 +// } +// { +// let a_4 := add(a, 4) +// a := a_4 +// } +// for { +// let a_5 := add(a, 3) +// a := a_5 +// } +// a +// { +// let a_7 := add(a, 6) +// a := a_7 +// } +// { +// let a_6 := add(a, 12) +// a := a_6 +// } +// let a_8 := add(a, 8) +// a := a_8 +// } diff --git a/test/libyul/yulOptimizerTests/ssaTransform/function.yul b/test/libyul/yulOptimizerTests/ssaTransform/function.yul new file mode 100644 index 00000000..995d16cc --- /dev/null +++ b/test/libyul/yulOptimizerTests/ssaTransform/function.yul @@ -0,0 +1,23 @@ +{ + function f(a, b) -> c, d { + b := add(b, a) + c := add(c, b) + d := add(d, c) + a := add(a, d) + } +} +// ---- +// ssaTransform +// { +// function f(a, b) -> c, d +// { +// let b_1 := add(b, a) +// b := b_1 +// let c_2 := add(c, b_1) +// c := c_2 +// let d_3 := add(d, c_2) +// d := d_3 +// let a_4 := add(a, d_3) +// a := a_4 +// } +// } diff --git a/test/libyul/yulOptimizerTests/ssaTransform/nested.yul b/test/libyul/yulOptimizerTests/ssaTransform/nested.yul new file mode 100644 index 00000000..49a76953 --- /dev/null +++ b/test/libyul/yulOptimizerTests/ssaTransform/nested.yul @@ -0,0 +1,32 @@ +{ + let a := 1 + a := 2 + let b := 3 + b := 4 + { + // b is not reassigned here + a := 3 + a := 4 + } + a := add(b, a) +} +// ---- +// ssaTransform +// { +// let a_1 := 1 +// let a := a_1 +// let a_2 := 2 +// a := a_2 +// let b_3 := 3 +// let b := b_3 +// let b_4 := 4 +// b := b_4 +// { +// let a_5 := 3 +// a := a_5 +// let a_6 := 4 +// a := a_6 +// } +// let a_7 := add(b_4, a) +// a := a_7 +// } diff --git a/test/libyul/yulOptimizerTests/ssaTransform/notransform.yul b/test/libyul/yulOptimizerTests/ssaTransform/notransform.yul new file mode 100644 index 00000000..297905c6 --- /dev/null +++ b/test/libyul/yulOptimizerTests/ssaTransform/notransform.yul @@ -0,0 +1,19 @@ +{ + let a := 1 + // this should not be transformed + let b := add(a, 2) + let c + mstore(c, 0) + c := add(a, b) +} +// ---- +// ssaTransform +// { +// let a := 1 +// let b := add(a, 2) +// let c_1 +// let c := c_1 +// mstore(c_1, 0) +// let c_2 := add(a, b) +// c := c_2 +// } diff --git a/test/libyul/yulOptimizerTests/ssaTransform/simple.yul b/test/libyul/yulOptimizerTests/ssaTransform/simple.yul new file mode 100644 index 00000000..6dbce729 --- /dev/null +++ b/test/libyul/yulOptimizerTests/ssaTransform/simple.yul @@ -0,0 +1,18 @@ +{ + let a := 1 + a := 2 + a := 3 + a := 4 +} +// ---- +// ssaTransform +// { +// let a_1 := 1 +// let a := a_1 +// let a_2 := 2 +// a := a_2 +// let a_3 := 3 +// a := a_3 +// let a_4 := 4 +// a := a_4 +// } diff --git a/test/libyul/yulOptimizerTests/ssaTransform/switch.yul b/test/libyul/yulOptimizerTests/ssaTransform/switch.yul new file mode 100644 index 00000000..bc9b55bb --- /dev/null +++ b/test/libyul/yulOptimizerTests/ssaTransform/switch.yul @@ -0,0 +1,26 @@ +{ + let a := mload(0) + // This could be more efficient: + // all cases could use the value of the variable from just before + // the switch and not just the first + switch a + case 0 { a := add(a, 4) } + default { a := add(a, 8) } + mstore(0, a) +} +// ---- +// ssaTransform +// { +// let a_1 := mload(0) +// let a := a_1 +// switch a_1 +// case 0 { +// let a_2 := add(a_1, 4) +// a := a_2 +// } +// default { +// let a_3 := add(a, 8) +// a := a_3 +// } +// mstore(0, a) +// } diff --git a/test/libyul/yulOptimizerTests/ssaTransform/used.yul b/test/libyul/yulOptimizerTests/ssaTransform/used.yul new file mode 100644 index 00000000..ad686ca1 --- /dev/null +++ b/test/libyul/yulOptimizerTests/ssaTransform/used.yul @@ -0,0 +1,39 @@ +{ + let a := 1 + mstore(a, 0) + a := 2 + mstore(a, 0) + { + mstore(a, 0) + a := 3 + mstore(a, 0) + a := 4 + mstore(a, 0) + } + mstore(a, 0) + a := 4 + mstore(a, 0) +} +// ---- +// ssaTransform +// { +// let a_1 := 1 +// let a := a_1 +// mstore(a_1, 0) +// let a_2 := 2 +// a := a_2 +// mstore(a_2, 0) +// { +// mstore(a_2, 0) +// let a_3 := 3 +// a := a_3 +// mstore(a_3, 0) +// let a_4 := 4 +// a := a_4 +// mstore(a_4, 0) +// } +// mstore(a, 0) +// let a_5 := 4 +// a := a_5 +// mstore(a_5, 0) +// } diff --git a/test/libyul/yulOptimizerTests/unusedPruner/functions.yul b/test/libyul/yulOptimizerTests/unusedPruner/functions.yul new file mode 100644 index 00000000..ec9cdda8 --- /dev/null +++ b/test/libyul/yulOptimizerTests/unusedPruner/functions.yul @@ -0,0 +1,8 @@ +{ + function f() { let a := 1 } + function g() { f() } +} +// ---- +// unusedPruner +// { +// } diff --git a/test/libyul/yulOptimizerTests/unusedPruner/intermediate_assignment.yul b/test/libyul/yulOptimizerTests/unusedPruner/intermediate_assignment.yul new file mode 100644 index 00000000..4ed6dd2c --- /dev/null +++ b/test/libyul/yulOptimizerTests/unusedPruner/intermediate_assignment.yul @@ -0,0 +1,11 @@ +{ + let a := 1 + a := 4 + let b := 1 +} +// ---- +// unusedPruner +// { +// let a := 1 +// a := 4 +// } diff --git a/test/libyul/yulOptimizerTests/unusedPruner/intermediate_multi_assignment.yul b/test/libyul/yulOptimizerTests/unusedPruner/intermediate_multi_assignment.yul new file mode 100644 index 00000000..94d101e9 --- /dev/null +++ b/test/libyul/yulOptimizerTests/unusedPruner/intermediate_multi_assignment.yul @@ -0,0 +1,16 @@ +{ + let a, b + function f() -> x { } + a := f() + b := 1 +} +// ---- +// unusedPruner +// { +// let a, b +// function f() -> x +// { +// } +// a := f() +// b := 1 +// } diff --git a/test/libyul/yulOptimizerTests/unusedPruner/multi_assign.yul b/test/libyul/yulOptimizerTests/unusedPruner/multi_assign.yul new file mode 100644 index 00000000..a14dc28c --- /dev/null +++ b/test/libyul/yulOptimizerTests/unusedPruner/multi_assign.yul @@ -0,0 +1,16 @@ +{ + let a + let b + function f() -> x, y { } + a, b := f() +} +// ---- +// unusedPruner +// { +// let a +// let b +// function f() -> x, y +// { +// } +// a, b := f() +// } diff --git a/test/libyul/yulOptimizerTests/unusedPruner/multi_assignments.yul b/test/libyul/yulOptimizerTests/unusedPruner/multi_assignments.yul new file mode 100644 index 00000000..fe94edb8 --- /dev/null +++ b/test/libyul/yulOptimizerTests/unusedPruner/multi_assignments.yul @@ -0,0 +1,12 @@ +{ + let x, y + x := 1 + y := 2 +} +// ---- +// unusedPruner +// { +// let x, y +// x := 1 +// y := 2 +// } diff --git a/test/libyul/yulOptimizerTests/unusedPruner/multi_declarations.yul b/test/libyul/yulOptimizerTests/unusedPruner/multi_declarations.yul new file mode 100644 index 00000000..3cf35007 --- /dev/null +++ b/test/libyul/yulOptimizerTests/unusedPruner/multi_declarations.yul @@ -0,0 +1,7 @@ +{ + let x, y +} +// ---- +// unusedPruner +// { +// } diff --git a/test/libyul/yulOptimizerTests/unusedPruner/multi_declare.yul b/test/libyul/yulOptimizerTests/unusedPruner/multi_declare.yul new file mode 100644 index 00000000..adabac87 --- /dev/null +++ b/test/libyul/yulOptimizerTests/unusedPruner/multi_declare.yul @@ -0,0 +1,12 @@ +{ + function f() -> x, y { } + let a, b := f() +} +// ---- +// unusedPruner +// { +// function f() -> x, y +// { +// } +// let a, b := f() +// } diff --git a/test/libyul/yulOptimizerTests/unusedPruner/multi_partial_assignments.yul b/test/libyul/yulOptimizerTests/unusedPruner/multi_partial_assignments.yul new file mode 100644 index 00000000..5db0ade9 --- /dev/null +++ b/test/libyul/yulOptimizerTests/unusedPruner/multi_partial_assignments.yul @@ -0,0 +1,10 @@ +{ + let x, y + x := 1 +} +// ---- +// unusedPruner +// { +// let x, y +// x := 1 +// } diff --git a/test/libyul/yulOptimizerTests/unusedPruner/pop.yul b/test/libyul/yulOptimizerTests/unusedPruner/pop.yul new file mode 100644 index 00000000..542070f9 --- /dev/null +++ b/test/libyul/yulOptimizerTests/unusedPruner/pop.yul @@ -0,0 +1,8 @@ +{ + let a := 1 + pop(a) +} +// ---- +// unusedPruner +// { +// } diff --git a/test/libyul/yulOptimizerTests/unusedPruner/smoke.yul b/test/libyul/yulOptimizerTests/unusedPruner/smoke.yul new file mode 100644 index 00000000..ca2ed942 --- /dev/null +++ b/test/libyul/yulOptimizerTests/unusedPruner/smoke.yul @@ -0,0 +1,5 @@ +{ } +// ---- +// unusedPruner +// { +// } diff --git a/test/libyul/yulOptimizerTests/unusedPruner/trivial.yul b/test/libyul/yulOptimizerTests/unusedPruner/trivial.yul new file mode 100644 index 00000000..9b4cf9fd --- /dev/null +++ b/test/libyul/yulOptimizerTests/unusedPruner/trivial.yul @@ -0,0 +1,10 @@ +{ + let a := 1 + let b := 1 + mstore(0, 1) +} +// ---- +// unusedPruner +// { +// mstore(0, 1) +// } diff --git a/test/libyul/yulOptimizerTests/varDeclPropagator/init_assignment_inside_if.yul b/test/libyul/yulOptimizerTests/varDeclPropagator/init_assignment_inside_if.yul new file mode 100644 index 00000000..54fea2fb --- /dev/null +++ b/test/libyul/yulOptimizerTests/varDeclPropagator/init_assignment_inside_if.yul @@ -0,0 +1,17 @@ +{ + let a := 4 + let x + if a { + x := 2 + } +} +// ---- +// varDeclPropagator +// { +// let a := 4 +// let x +// if a +// { +// x := 2 +// } +// } diff --git a/test/libyul/yulOptimizerTests/varDeclPropagator/multi_assignment_vardecl.yul b/test/libyul/yulOptimizerTests/varDeclPropagator/multi_assignment_vardecl.yul new file mode 100644 index 00000000..ed8d33b4 --- /dev/null +++ b/test/libyul/yulOptimizerTests/varDeclPropagator/multi_assignment_vardecl.yul @@ -0,0 +1,13 @@ +{ + function f() -> a, b, c {} + let x, y, z + z, x, y := f() +} +// ---- +// varDeclPropagator +// { +// function f() -> a, b, c +// { +// } +// let z, x, y := f() +// } diff --git a/test/libyul/yulOptimizerTests/varDeclPropagator/overwrite.yul b/test/libyul/yulOptimizerTests/varDeclPropagator/overwrite.yul new file mode 100644 index 00000000..ca921500 --- /dev/null +++ b/test/libyul/yulOptimizerTests/varDeclPropagator/overwrite.yul @@ -0,0 +1,11 @@ +{ + let a + a := 4 + a := 5 +} +// ---- +// varDeclPropagator +// { +// let a := 4 +// a := 5 +// } diff --git a/test/libyul/yulOptimizerTests/varDeclPropagator/rewrite_removes_unused_var.yul b/test/libyul/yulOptimizerTests/varDeclPropagator/rewrite_removes_unused_var.yul new file mode 100644 index 00000000..3affcac6 --- /dev/null +++ b/test/libyul/yulOptimizerTests/varDeclPropagator/rewrite_removes_unused_var.yul @@ -0,0 +1,10 @@ +{ + let a, b + a := mload(0) +} +// ---- +// varDeclPropagator +// { +// let b +// let a := mload(0) +// } diff --git a/test/libyul/yulOptimizerTests/varDeclPropagator/simple1.yul b/test/libyul/yulOptimizerTests/varDeclPropagator/simple1.yul new file mode 100644 index 00000000..d8959040 --- /dev/null +++ b/test/libyul/yulOptimizerTests/varDeclPropagator/simple1.yul @@ -0,0 +1,9 @@ +{ + let f + f := mload(0) +} +// ---- +// varDeclPropagator +// { +// let f := mload(0) +// } diff --git a/test/libyul/yulOptimizerTests/varDeclPropagator/split_assign_splits_vardecl.yul b/test/libyul/yulOptimizerTests/varDeclPropagator/split_assign_splits_vardecl.yul new file mode 100644 index 00000000..e8c91e10 --- /dev/null +++ b/test/libyul/yulOptimizerTests/varDeclPropagator/split_assign_splits_vardecl.yul @@ -0,0 +1,11 @@ +{ + let a, b + a := mload(0) + b := mload(1) +} +// ---- +// varDeclPropagator +// { +// let a := mload(0) +// let b := mload(1) +// } diff --git a/test/libyul/yulOptimizerTests/varDeclPropagator/use_before_init.yul b/test/libyul/yulOptimizerTests/varDeclPropagator/use_before_init.yul new file mode 100644 index 00000000..5312112a --- /dev/null +++ b/test/libyul/yulOptimizerTests/varDeclPropagator/use_before_init.yul @@ -0,0 +1,12 @@ +{ + let b + let a := b + b := 1 +} +// ---- +// varDeclPropagator +// { +// let b +// let a := b +// b := 1 +// } diff --git a/test/libyul/yulOptimizerTests/varDeclPropagator/use_doesnt_rewrite.yul b/test/libyul/yulOptimizerTests/varDeclPropagator/use_doesnt_rewrite.yul new file mode 100644 index 00000000..e27785dd --- /dev/null +++ b/test/libyul/yulOptimizerTests/varDeclPropagator/use_doesnt_rewrite.yul @@ -0,0 +1,16 @@ +{ + function f(x) {} + let a + f(a) + a := 4 +} +// ---- +// varDeclPropagator +// { +// function f(x) +// { +// } +// let a +// f(a) +// a := 4 +// } diff --git a/test/solcjsTests.sh b/test/solcjsTests.sh index 27797cb4..e0bbc5df 100755 --- a/test/solcjsTests.sh +++ b/test/solcjsTests.sh @@ -53,6 +53,11 @@ DIR=$(mktemp -d) rm -f soljson.js cp "$SOLJSON" soljson.js + # ensure to use always 0.5.0 sources + # FIXME: should be removed once the version bump in this repo is done + rm -rf test/DAO040 + cp -R test/DAO test/DAO040 + # Update version (needed for some tests) echo "Updating package.json to version $VERSION" npm version --no-git-tag-version $VERSION diff --git a/test/tools/CMakeLists.txt b/test/tools/CMakeLists.txt index 11714017..19a1d958 100644 --- a/test/tools/CMakeLists.txt +++ b/test/tools/CMakeLists.txt @@ -1,5 +1,10 @@ add_executable(solfuzzer fuzzer.cpp) target_link_libraries(solfuzzer PRIVATE libsolc evmasm ${Boost_PROGRAM_OPTIONS_LIBRARIES} ${Boost_SYSTEM_LIBRARIES}) -add_executable(isoltest isoltest.cpp ../Options.cpp ../libsolidity/SyntaxTest.cpp ../libsolidity/AnalysisFramework.cpp) +add_executable(yulopti yulopti.cpp) +target_link_libraries(yulopti PRIVATE solidity ${Boost_PROGRAM_OPTIONS_LIBRARIES} ${Boost_SYSTEM_LIBRARIES}) + +add_executable(isoltest isoltest.cpp ../Options.cpp ../Common.cpp ../libsolidity/TestCase.cpp ../libsolidity/SyntaxTest.cpp + ../libsolidity/AnalysisFramework.cpp ../libsolidity/SolidityExecutionFramework.cpp ../ExecutionFramework.cpp + ../RPCSession.cpp ../libsolidity/ASTJSONTest.cpp ../libyul/YulOptimizerTest.cpp) target_link_libraries(isoltest PRIVATE libsolc solidity evmasm ${Boost_PROGRAM_OPTIONS_LIBRARIES} ${Boost_UNIT_TEST_FRAMEWORK_LIBRARIES}) diff --git a/test/tools/fuzzer.cpp b/test/tools/fuzzer.cpp index 71f38b67..8633454c 100644 --- a/test/tools/fuzzer.cpp +++ b/test/tools/fuzzer.cpp @@ -28,6 +28,7 @@ #include <boost/program_options.hpp> #include <string> +#include <sstream> #include <iostream> using namespace std; @@ -48,15 +49,17 @@ string contains(string const& _haystack, vector<string> const& _needles) return ""; } -void testConstantOptimizer() +void testConstantOptimizer(string const& input) { if (!quiet) cout << "Testing constant optimizer" << endl; vector<u256> numbers; - while (!cin.eof()) + stringstream sin(input); + + while (!sin.eof()) { h256 data; - cin.read(reinterpret_cast<char*>(data.data()), 32); + sin.read(reinterpret_cast<char*>(data.data()), 32); numbers.push_back(u256(data)); } if (!quiet) @@ -84,13 +87,9 @@ void testConstantOptimizer() } } -void testStandardCompiler() +void runCompiler(string input) { - if (!quiet) - cout << "Testing compiler via JSON interface." << endl; - string input = readStandardInput(); - - string outputString(compileStandard(input.c_str(), NULL)); + string outputString(solidity_compile(input.c_str(), nullptr)); Json::Value output; if (!jsonParseStrict(outputString, output)) { @@ -112,48 +111,35 @@ void testStandardCompiler() } } -void testCompiler(bool optimize) +void testStandardCompiler(string const& input) +{ + if (!quiet) + cout << "Testing compiler via JSON interface." << endl; + + runCompiler(input); +} + +void testCompiler(string const& input, bool optimize) { if (!quiet) cout << "Testing compiler " << (optimize ? "with" : "without") << " optimizer." << endl; - string input = readStandardInput(); - string outputString(compileJSON(input.c_str(), optimize)); - Json::Value outputJson; - if (!jsonParseStrict(outputString, outputJson)) - { - cout << "Compiler produced invalid JSON output." << endl; - abort(); - } - if (outputJson.isMember("errors")) - { - if (!outputJson["errors"].isArray()) - { - cout << "Output JSON has \"errors\" but it is not an array." << endl; - abort(); - } - for (Json::Value const& error: outputJson["errors"]) - { - string invalid = contains(error.asString(), vector<string>{ - "Internal compiler error", - "Exception during compilation", - "Unknown exception during compilation", - "Unknown exception while generating contract data output", - "Unknown exception while generating source name output", - "Unknown error while generating JSON" - }); - if (!invalid.empty()) - { - cout << "Invalid error: \"" << error.asString() << "\"" << endl; - abort(); - } - } - } - else if (!outputJson.isMember("contracts")) - { - cout << "Output JSON has neither \"errors\" nor \"contracts\"." << endl; - abort(); - } + Json::Value config = Json::objectValue; + config["language"] = "Solidity"; + config["sources"] = Json::objectValue; + config["sources"][""] = Json::objectValue; + config["sources"][""]["content"] = input; + config["settings"] = Json::objectValue; + config["settings"]["optimizer"] = Json::objectValue; + config["settings"]["optimizer"]["enabled"] = optimize; + config["settings"]["optimizer"]["runs"] = 200; + + // Enable all SourceUnit-level outputs. + config["settings"]["outputSelection"]["*"][""][0] = "*"; + // Enable all Contract-level outputs. + config["settings"]["outputSelection"]["*"]["*"][0] = "*"; + + runCompiler(jsonCompactPrint(config)); } } @@ -183,15 +169,24 @@ Allowed options)", "Expects a binary string of up to 32 bytes on stdin." ) ( + "input-file", + po::value<string>(), + "input file" + ) + ( "without-optimizer", "Run without optimizations. Cannot be used together with standard-json." ); + // All positional options should be interpreted as input files + po::positional_options_description filesPositions; + filesPositions.add("input-file", 1); + po::variables_map arguments; try { po::command_line_parser cmdLineParser(argc, argv); - cmdLineParser.options(options); + cmdLineParser.options(options).positional(filesPositions); po::store(cmdLineParser.run(), arguments); } catch (po::error const& _exception) @@ -200,17 +195,23 @@ Allowed options)", return 1; } + string input; + if (arguments.count("input-file")) + input = readFileAsString(arguments["input-file"].as<string>()); + else + input = readStandardInput(); + if (arguments.count("quiet")) quiet = true; if (arguments.count("help")) cout << options; else if (arguments.count("const-opt")) - testConstantOptimizer(); + testConstantOptimizer(input); else if (arguments.count("standard-json")) - testStandardCompiler(); + testStandardCompiler(input); else - testCompiler(!arguments.count("without-optimizer")); + testCompiler(input, !arguments.count("without-optimizer")); return 0; } diff --git a/test/tools/isoltest.cpp b/test/tools/isoltest.cpp index 7a147bd0..1b6fd54a 100644 --- a/test/tools/isoltest.cpp +++ b/test/tools/isoltest.cpp @@ -16,8 +16,12 @@ */ #include <libdevcore/CommonIO.h> + +#include <test/Common.h> #include <test/libsolidity/AnalysisFramework.h> #include <test/libsolidity/SyntaxTest.h> +#include <test/libsolidity/ASTJSONTest.h> +#include <test/libyul/YulOptimizerTest.h> #include <boost/algorithm/string.hpp> #include <boost/algorithm/string/replace.hpp> @@ -29,6 +33,10 @@ #include <fstream> #include <queue> +#if defined(_WIN32) +#include <windows.h> +#endif + using namespace dev; using namespace dev::solidity; using namespace dev::solidity::test; @@ -37,18 +45,28 @@ using namespace std; namespace po = boost::program_options; namespace fs = boost::filesystem; -struct SyntaxTestStats +struct TestStats { int successCount; - int runCount; - operator bool() const { return successCount == runCount; } + int testCount; + operator bool() const { return successCount == testCount; } + TestStats& operator+=(TestStats const& _other) noexcept + { + successCount += _other.successCount; + testCount += _other.testCount; + return *this; + } }; -class SyntaxTestTool +class TestTool { public: - SyntaxTestTool(string const& _name, fs::path const& _path, bool _formatted): - m_formatted(_formatted), m_name(_name), m_path(_path) + TestTool( + TestCase::TestCaseCreator _testCaseCreator, + string const& _name, + fs::path const& _path, + bool _formatted + ): m_testCaseCreator(_testCaseCreator), m_formatted(_formatted), m_name(_name), m_path(_path) {} enum class Result @@ -60,7 +78,8 @@ public: Result process(); - static SyntaxTestStats processPath( + static TestStats processPath( + TestCase::TestCaseCreator _testCaseCreator, fs::path const& _basepath, fs::path const& _path, bool const _formatted @@ -77,68 +96,18 @@ private: Request handleResponse(bool const _exception); - void printContract() const; - - bool const m_formatted; + TestCase::TestCaseCreator m_testCaseCreator; + bool const m_formatted = false; string const m_name; fs::path const m_path; - unique_ptr<SyntaxTest> m_test; + unique_ptr<TestCase> m_test; + static bool m_exitRequested; }; -string SyntaxTestTool::editor; +string TestTool::editor; +bool TestTool::m_exitRequested = false; -void SyntaxTestTool::printContract() const -{ - if (m_formatted) - { - string const& source = m_test->source(); - if (source.empty()) - return; - - std::vector<char const*> sourceFormatting(source.length(), formatting::RESET); - for (auto const& error: m_test->errorList()) - if (error.locationStart >= 0 && error.locationEnd >= 0) - { - assert(static_cast<size_t>(error.locationStart) < source.length()); - assert(static_cast<size_t>(error.locationEnd) < source.length()); - bool isWarning = error.type == "Warning"; - for (int i = error.locationStart; i < error.locationEnd; i++) - if (isWarning) - { - if (sourceFormatting[i] == formatting::RESET) - sourceFormatting[i] = formatting::ORANGE_BACKGROUND; - } - else - sourceFormatting[i] = formatting::RED_BACKGROUND; - } - - cout << " " << sourceFormatting.front() << source.front(); - for (size_t i = 1; i < source.length(); i++) - { - if (sourceFormatting[i] != sourceFormatting[i - 1]) - cout << sourceFormatting[i]; - if (source[i] != '\n') - cout << source[i]; - else - { - cout << formatting::RESET << endl; - if (i + 1 < source.length()) - cout << " " << sourceFormatting[i]; - } - } - cout << formatting::RESET << endl; - } - else - { - stringstream stream(m_test->source()); - string line; - while (getline(stream, line)) - cout << " " << line << endl; - cout << endl; - } -} - -SyntaxTestTool::Result SyntaxTestTool::process() +TestTool::Result TestTool::process() { bool success; std::stringstream outputMessages; @@ -147,42 +116,25 @@ SyntaxTestTool::Result SyntaxTestTool::process() try { - m_test = unique_ptr<SyntaxTest>(new SyntaxTest(m_path.string())); + m_test = m_testCaseCreator(m_path.string()); success = m_test->run(outputMessages, " ", m_formatted); } - catch(CompilerError const& _e) - { - FormattedScope(cout, m_formatted, {BOLD, RED}) << - "Exception: " << SyntaxTest::errorMessage(_e) << endl; - return Result::Exception; - } - catch(InternalCompilerError const& _e) - { - FormattedScope(cout, m_formatted, {BOLD, RED}) << - "InternalCompilerError: " << SyntaxTest::errorMessage(_e) << endl; - return Result::Exception; - } - catch(FatalError const& _e) + catch(boost::exception const& _e) { FormattedScope(cout, m_formatted, {BOLD, RED}) << - "FatalError: " << SyntaxTest::errorMessage(_e) << endl; - return Result::Exception; - } - catch(UnimplementedFeatureError const& _e) - { - FormattedScope(cout, m_formatted, {BOLD, RED}) << - "UnimplementedFeatureError: " << SyntaxTest::errorMessage(_e) << endl; + "Exception during syntax test: " << boost::diagnostic_information(_e) << endl; return Result::Exception; } catch (std::exception const& _e) { - FormattedScope(cout, m_formatted, {BOLD, RED}) << "Exception: " << _e.what() << endl; + FormattedScope(cout, m_formatted, {BOLD, RED}) << + "Exception during syntax test: " << _e.what() << endl; return Result::Exception; } - catch(...) + catch (...) { FormattedScope(cout, m_formatted, {BOLD, RED}) << - "Unknown Exception" << endl; + "Unknown exception during syntax test." << endl; return Result::Exception; } @@ -196,14 +148,14 @@ SyntaxTestTool::Result SyntaxTestTool::process() FormattedScope(cout, m_formatted, {BOLD, RED}) << "FAIL" << endl; FormattedScope(cout, m_formatted, {BOLD, CYAN}) << " Contract:" << endl; - printContract(); + m_test->printSource(cout, " ", m_formatted); - cout << outputMessages.str() << endl; + cout << endl << outputMessages.str() << endl; return Result::Failure; } } -SyntaxTestTool::Request SyntaxTestTool::handleResponse(bool const _exception) +TestTool::Request TestTool::handleResponse(bool const _exception) { if (_exception) cout << "(e)dit/(s)kip/(q)uit? "; @@ -225,15 +177,14 @@ SyntaxTestTool::Request SyntaxTestTool::handleResponse(bool const _exception) { cout << endl; ofstream file(m_path.string(), ios::trunc); - file << m_test->source(); + m_test->printSource(file); file << "// ----" << endl; - if (!m_test->errorList().empty()) - m_test->printErrorList(file, m_test->errorList(), "// ", false); + m_test->printUpdatedExpectations(file, "// "); return Request::Rerun; } case 'e': cout << endl << endl; - if (system((editor + " \"" + m_path.string() + "\"").c_str())) + if (system((TestTool::editor + " \"" + m_path.string() + "\"").c_str())) cerr << "Error running editor command." << endl << endl; return Request::Rerun; case 'q': @@ -245,8 +196,8 @@ SyntaxTestTool::Request SyntaxTestTool::handleResponse(bool const _exception) } } - -SyntaxTestStats SyntaxTestTool::processPath( +TestStats TestTool::processPath( + TestCase::TestCaseCreator _testCaseCreator, fs::path const& _basepath, fs::path const& _path, bool const _formatted @@ -255,7 +206,7 @@ SyntaxTestStats SyntaxTestTool::processPath( std::queue<fs::path> paths; paths.push(_path); int successCount = 0; - int runCount = 0; + int testCount = 0; while (!paths.empty()) { @@ -269,13 +220,18 @@ SyntaxTestStats SyntaxTestTool::processPath( fs::directory_iterator(fullpath), fs::directory_iterator() )) - if (fs::is_directory(entry.path()) || SyntaxTest::isTestFilename(entry.path().filename())) + if (fs::is_directory(entry.path()) || TestCase::isTestFilename(entry.path().filename())) paths.push(currentPath / entry.path().filename()); } + else if (m_exitRequested) + { + ++testCount; + paths.pop(); + } else { - SyntaxTestTool testTool(currentPath.string(), fullpath, _formatted); - ++runCount; + ++testCount; + TestTool testTool(_testCaseCreator, currentPath.string(), fullpath, _formatted); auto result = testTool.process(); switch(result) @@ -285,10 +241,12 @@ SyntaxTestStats SyntaxTestTool::processPath( switch(testTool.handleResponse(result == Result::Exception)) { case Request::Quit: - return { successCount, runCount }; + paths.pop(); + m_exitRequested = true; + break; case Request::Rerun: cout << "Re-running test case..." << endl; - --runCount; + --testCount; break; case Request::Skip: paths.pop(); @@ -303,18 +261,74 @@ SyntaxTestStats SyntaxTestTool::processPath( } } - return { successCount, runCount }; + return { successCount, testCount }; + +} + +namespace +{ + +void setupTerminal() +{ +#if defined(_WIN32) && defined(ENABLE_VIRTUAL_TERMINAL_PROCESSING) + // Set output mode to handle virtual terminal (ANSI escape sequences) + // ignore any error, as this is just a "nice-to-have" + // only windows needs to be taken care of, as other platforms (Linux/OSX) support them natively. + HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE); + if (hOut == INVALID_HANDLE_VALUE) + return; + + DWORD dwMode = 0; + if (!GetConsoleMode(hOut, &dwMode)) + return; + + dwMode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING; + if (!SetConsoleMode(hOut, dwMode)) + return; +#endif +} + +boost::optional<TestStats> runTestSuite( + string const& _name, + fs::path const& _basePath, + fs::path const& _subdirectory, + TestCase::TestCaseCreator _testCaseCreator, + bool _formatted +) +{ + fs::path testPath = _basePath / _subdirectory; + + if (!fs::exists(testPath) || !fs::is_directory(testPath)) + { + cerr << _name << " tests not found. Use the --testpath argument." << endl; + return {}; + } + + TestStats stats = TestTool::processPath(_testCaseCreator, _basePath, _subdirectory, _formatted); + + cout << endl << _name << " Test Summary: "; + FormattedScope(cout, _formatted, {BOLD, stats ? GREEN : RED}) << + stats.successCount << + "/" << + stats.testCount; + cout << " tests successful." << endl << endl; + + return stats; +} } int main(int argc, char *argv[]) { + setupTerminal(); + if (getenv("EDITOR")) - SyntaxTestTool::editor = getenv("EDITOR"); + TestTool::editor = getenv("EDITOR"); else if (fs::exists("/usr/bin/editor")) - SyntaxTestTool::editor = "/usr/bin/editor"; + TestTool::editor = "/usr/bin/editor"; fs::path testPath; + bool disableSMT = false; bool formatted = true; po::options_description options( R"(isoltest, tool for interactively managing test contracts. @@ -327,8 +341,9 @@ Allowed options)", options.add_options() ("help", "Show this help screen.") ("testpath", po::value<fs::path>(&testPath), "path to test files") + ("no-smt", "disable SMT checker") ("no-color", "don't use colors") - ("editor", po::value<string>(&SyntaxTestTool::editor), "editor for opening contracts"); + ("editor", po::value<string>(&TestTool::editor), "editor for opening contracts"); po::variables_map arguments; try @@ -347,50 +362,62 @@ Allowed options)", formatted = false; po::notify(arguments); + + if (arguments.count("no-smt")) + disableSMT = true; } - catch (po::error const& _exception) + catch (std::exception const& _exception) { cerr << _exception.what() << endl; return 1; } if (testPath.empty()) - { - auto const searchPath = - { - fs::current_path() / ".." / ".." / ".." / "test", - fs::current_path() / ".." / ".." / "test", - fs::current_path() / ".." / "test", - fs::current_path() / "test", - fs::current_path() - }; - for (auto const& basePath : searchPath) - { - fs::path syntaxTestPath = basePath / "libsolidity" / "syntaxTests"; - if (fs::exists(syntaxTestPath) && fs::is_directory(syntaxTestPath)) - { - testPath = basePath; - break; - } - } - } + testPath = dev::test::discoverTestPath(); - fs::path syntaxTestPath = testPath / "libsolidity" / "syntaxTests"; + TestStats global_stats{0, 0}; - if (fs::exists(syntaxTestPath) && fs::is_directory(syntaxTestPath)) - { - auto stats = SyntaxTestTool::processPath(testPath / "libsolidity", "syntaxTests", formatted); + // Actually run the tests. + // If you add new tests here, you also have to add them in boostTest.cpp + if (auto stats = runTestSuite("Syntax", testPath / "libsolidity", "syntaxTests", SyntaxTest::create, formatted)) + global_stats += *stats; + else + return 1; - cout << endl << "Summary: "; - FormattedScope(cout, formatted, {BOLD, stats ? GREEN : RED}) << - stats.successCount << "/" << stats.runCount; - cout << " tests successful." << endl; + if (auto stats = runTestSuite("JSON AST", testPath / "libsolidity", "ASTJSON", ASTJSONTest::create, formatted)) + global_stats += *stats; + else + return 1; - return stats ? 0 : 1; - } + if (auto stats = runTestSuite( + "Yul Optimizer", + testPath / "libyul", + "yulOptimizerTests", + yul::test::YulOptimizerTest::create, + formatted + )) + global_stats += *stats; else - { - cerr << "Test path not found. Use the --testpath argument." << endl; return 1; + + if (!disableSMT) + { + if (auto stats = runTestSuite( + "SMT Checker", + testPath / "libsolidity", + "smtCheckerTests", + SyntaxTest::create, + formatted + )) + global_stats += *stats; + else + return 1; } + + cout << endl << "Summary: "; + FormattedScope(cout, formatted, {BOLD, global_stats ? GREEN : RED}) << + global_stats.successCount << "/" << global_stats.testCount; + cout << " tests successful." << endl; + + return global_stats ? 0 : 1; } diff --git a/test/tools/yulopti.cpp b/test/tools/yulopti.cpp new file mode 100644 index 00000000..348c5f4a --- /dev/null +++ b/test/tools/yulopti.cpp @@ -0,0 +1,233 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see <http://www.gnu.org/licenses/>. +*/ +/** + * Interactive yul optimizer + */ + +#include <libdevcore/CommonIO.h> +#include <libsolidity/inlineasm/AsmAnalysis.h> +#include <libsolidity/inlineasm/AsmAnalysisInfo.h> +#include <libsolidity/parsing/Scanner.h> +#include <libsolidity/parsing/Parser.h> +#include <libsolidity/inlineasm/AsmData.h> +#include <libsolidity/inlineasm/AsmParser.h> +#include <libsolidity/inlineasm/AsmPrinter.h> +#include <libsolidity/interface/SourceReferenceFormatter.h> +#include <libsolidity/interface/ErrorReporter.h> + +#include <libyul/optimiser/BlockFlattener.h> +#include <libyul/optimiser/Disambiguator.h> +#include <libyul/optimiser/CommonSubexpressionEliminator.h> +#include <libyul/optimiser/NameCollector.h> +#include <libyul/optimiser/ExpressionSplitter.h> +#include <libyul/optimiser/FunctionGrouper.h> +#include <libyul/optimiser/FunctionHoister.h> +#include <libyul/optimiser/ExpressionInliner.h> +#include <libyul/optimiser/FullInliner.h> +#include <libyul/optimiser/MainFunction.h> +#include <libyul/optimiser/Rematerialiser.h> +#include <libyul/optimiser/ExpressionSimplifier.h> +#include <libyul/optimiser/UnusedPruner.h> +#include <libyul/optimiser/ExpressionJoiner.h> +#include <libyul/optimiser/RedundantAssignEliminator.h> +#include <libyul/optimiser/SSATransform.h> +#include <libyul/optimiser/VarDeclPropagator.h> + +#include <libdevcore/JSON.h> + +#include <boost/program_options.hpp> + +#include <string> +#include <sstream> +#include <iostream> + +using namespace std; +using namespace dev; +using namespace dev::solidity; +using namespace dev::solidity::assembly; +using namespace dev::yul; + +namespace po = boost::program_options; + +class YulOpti +{ +public: + void printErrors(Scanner const& _scanner) + { + SourceReferenceFormatter formatter(cout, [&](string const&) -> Scanner const& { return _scanner; }); + + for (auto const& error: m_errors) + formatter.printExceptionInformation( + *error, + (error->type() == Error::Type::Warning) ? "Warning" : "Error" + ); + } + + bool parse(string const& _input) + { + ErrorReporter errorReporter(m_errors); + shared_ptr<Scanner> scanner = make_shared<Scanner>(CharStream(_input), ""); + m_ast = assembly::Parser(errorReporter, assembly::AsmFlavour::Strict).parse(scanner, false); + if (!m_ast || !errorReporter.errors().empty()) + { + cout << "Error parsing source." << endl; + printErrors(*scanner); + return false; + } + m_analysisInfo = make_shared<assembly::AsmAnalysisInfo>(); + AsmAnalyzer analyzer( + *m_analysisInfo, + errorReporter, + EVMVersion::byzantium(), + boost::none, + AsmFlavour::Strict + ); + if (!analyzer.analyze(*m_ast) || !errorReporter.errors().empty()) + { + cout << "Error analyzing source." << endl; + printErrors(*scanner); + return false; + } + return true; + } + + void runInteractive(string source) + { + bool disambiguated = false; + while (true) + { + cout << "----------------------" << endl; + cout << source << endl; + if (!parse(source)) + return; + if (!disambiguated) + { + *m_ast = boost::get<assembly::Block>(Disambiguator(*m_analysisInfo)(*m_ast)); + m_analysisInfo.reset(); + m_nameDispenser = make_shared<NameDispenser>(*m_ast); + disambiguated = true; + } + cout << "(q)quit/(f)flatten/(c)se/propagate var(d)ecls/(x)plit/(j)oin/(g)rouper/(h)oister/" << endl; + cout << " (e)xpr inline/(i)nline/(s)implify/(u)nusedprune/ss(a) transform/" << endl; + cout << " (r)edundant assign elim./re(m)aterializer? "; + cout.flush(); + int option = readStandardInputChar(); + cout << ' ' << char(option) << endl; + switch (option) + { + case 'q': + return; + case 'f': + BlockFlattener{}(*m_ast); + break; + case 'c': + (CommonSubexpressionEliminator{})(*m_ast); + break; + case 'd': + (VarDeclPropagator{})(*m_ast); + break; + case 'x': + ExpressionSplitter{*m_nameDispenser}(*m_ast); + break; + case 'j': + ExpressionJoiner::run(*m_ast); + break; + case 'g': + (FunctionGrouper{})(*m_ast); + break; + case 'h': + (FunctionHoister{})(*m_ast); + break; + case 'e': + ExpressionInliner{*m_ast}.run(); + break; + case 'i': + FullInliner(*m_ast, *m_nameDispenser).run(); + break; + case 's': + ExpressionSimplifier::run(*m_ast); + break; + case 'u': + UnusedPruner::runUntilStabilised(*m_ast); + break; + case 'a': + SSATransform::run(*m_ast, *m_nameDispenser); + break; + case 'r': + RedundantAssignEliminator::run(*m_ast); + break; + case 'm': + Rematerialiser{}(*m_ast); + break; + default: + cout << "Unknown option." << endl; + } + source = AsmPrinter{}(*m_ast); + } + } + +private: + ErrorList m_errors; + shared_ptr<assembly::Block> m_ast; + shared_ptr<AsmAnalysisInfo> m_analysisInfo; + shared_ptr<NameDispenser> m_nameDispenser; +}; + +int main(int argc, char** argv) +{ + po::options_description options( + R"(yulopti, yul optimizer exploration tool. +Usage: yulopti [Options] <file> +Reads <file> as yul code and applies optimizer steps to it, +interactively read from stdin. + +Allowed options)", + po::options_description::m_default_line_length, + po::options_description::m_default_line_length - 23); + options.add_options() + ( + "input-file", + po::value<string>(), + "input file" + ) + ("help", "Show this help screen."); + + // All positional options should be interpreted as input files + po::positional_options_description filesPositions; + filesPositions.add("input-file", 1); + + po::variables_map arguments; + try + { + po::command_line_parser cmdLineParser(argc, argv); + cmdLineParser.options(options).positional(filesPositions); + po::store(cmdLineParser.run(), arguments); + } + catch (po::error const& _exception) + { + cerr << _exception.what() << endl; + return 1; + } + + string input; + if (arguments.count("input-file")) + YulOpti{}.runInteractive(readFileAsString(arguments["input-file"].as<string>())); + else + cout << options; + + return 0; +} |