From 8899812ff2f5c0323001c792e94b98788ca4c6e6 Mon Sep 17 00:00:00 2001 From: chriseth Date: Tue, 27 Feb 2018 16:38:16 +0100 Subject: [PATCH 001/197] Disable tests for travis on non-release and non-tag branches. --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index 0b05f6610..ed4584ef5 100644 --- a/.travis.yml +++ b/.travis.yml @@ -159,6 +159,7 @@ cache: install: - test $SOLC_INSTALL_DEPS_TRAVIS != On || (scripts/install_deps.sh) - test "$TRAVIS_OS_NAME" != "linux" || (scripts/install_cmake.sh) + - if [ "$TRAVIS_BRANCH" != release -a -z "$TRAVIS_TAG" ]; then SOLC_TESTS=Off; fi - if [ "$TRAVIS_BRANCH" = release -o -n "$TRAVIS_TAG" ]; then echo -n > prerelease.txt; else date -u +"nightly.%Y.%-m.%-d" > prerelease.txt; fi - echo -n "$TRAVIS_COMMIT" > commit_hash.txt From 6055bc250fb393cf621f6c7e63a2d89b880fe3a6 Mon Sep 17 00:00:00 2001 From: chriseth Date: Tue, 6 Mar 2018 10:32:58 +0100 Subject: [PATCH 002/197] Use coding style. --- CODING_STYLE.md | 254 ++++++++++++++++++++++++++++++++++++++++++ docs/contributing.rst | 4 +- 2 files changed, 256 insertions(+), 2 deletions(-) create mode 100644 CODING_STYLE.md diff --git a/CODING_STYLE.md b/CODING_STYLE.md new file mode 100644 index 000000000..ee924e726 --- /dev/null +++ b/CODING_STYLE.md @@ -0,0 +1,254 @@ +0. Formatting + +GOLDEN RULE: Follow the style of the existing code when you make changes. + +a. Use tabs for leading indentation +- tab stops are every 4 characters. +- One indentation level -> exactly one byte (i.e. a tab character) in the source file. +- If you have run-on lines, indent as you would for a block. +b. Line widths: +- Don't worry about having lines of code > 80-char wide. +- Lines of comments should be formatted according to ease of viewing, but simplicity is to be preferred over beauty. +c. Don't use braces for condition-body one-liners. +d. Never place condition bodies on same line as condition. +e. Space between first paren and keyword, but *not* following first paren or preceding final paren. +f. No spaces when fewer than intra-expression three parens together; when three or more, space according to clarity. +g. No spaces for subscripting or unary operators. +h. No space before ':' but one after it, except in the ternary operator: one on both sides. +i. Space all other operators. +j. Braces, when used, always have their own lines and are at same indentation level as "parent" scope. + +(WRONG) +if( a==b[ i ] ) { printf ("Hello\n"); } +foo->bar(someLongVariableName, + anotherLongVariableName, + anotherLongVariableName, + anotherLongVariableName, + anotherLongVariableName); + +(RIGHT) +if (a == b[i]) + printf("Hello\n"); // NOTE spaces used instead of tab here for clarity - first byte should be '\t'. +foo->bar( + someLongVariableName, + anotherLongVariableName, + anotherLongVariableName, + anotherLongVariableName, + anotherLongVariableName +); + + + +1. Namespaces; + +a. No "using namespace" declarations in header files. +b. All symbols should be declared in a namespace except for final applications. +c. Preprocessor symbols should be prefixed with the namespace in all-caps and an underscore. + +(WRONG) +#include +using namespace std; +tuple meanAndSigma(vector const& _v); + +(CORRECT) +#include +std::tuple meanAndSigma(std::vector const& _v); + + + +2. Preprocessor; + +a. File comment is always at top, and includes: +- Copyright. +- License (e.g. see COPYING). +b. Never use #ifdef/#define/#endif file guards. Prefer #pragma once as first line below file comment. +c. Prefer static const variable to value macros. +d. Prefer inline constexpr functions to function macros. +e. Split complex macro on multiple lines with '\'. + + + +3. Capitalization; + +GOLDEN RULE: Preprocessor: ALL_CAPS; C++: camelCase. + +a. Use camelCase for splitting words in names, except where obviously extending STL/boost functionality in which case follow those naming conventions. +b. The following entities' first alpha is upper case: +- Type names. +- Template parameters. +- Enum members. +- static const variables that form an external API. +c. All preprocessor symbols (macros, macro arguments) in full uppercase with underscore word separation. + + +All other entities' first alpha is lower case. + + + +4. Variable prefixes: + +a. Leading underscore "_" to parameter names. +- Exception: "o_parameterName" when it is used exclusively for output. See 6(f). +- Exception: "io_parameterName" when it is used for both input and output. See 6(f). +b. Leading "c_" to const variables (unless part of an external API). +c. Leading "g_" to global (non-const) variables. +d. Leading "s_" to static (non-const, non-global) variables. + + + +5. Error reporting: + +- Prefer exception to bool/int return type. + + + +6. Declarations: + +a. {Typename} + {qualifiers} + {name}. +b. Only one per line. +c. Associate */& with type, not variable (at ends with parser, but more readable, and safe if in conjunction with (b)). +d. Favour declarations close to use; don't habitually declare at top of scope ala C. +e. Always pass non-trivial parameters with a const& suffix. +f. If a function returns multiple values, use std::tuple (std::pair acceptable). Prefer not using */& arguments, except where efficiency requires. +g. Never use a macro where adequate non-preprocessor C++ can be written. +h. Make use of auto whenever type is clear or unimportant: +- Always avoid doubly-stating the type. +- Use to avoid vast and unimportant type declarations. +- However, avoid using auto where type is not immediately obvious from the context, and especially not for arithmetic expressions. +i. Don't pass bools: prefer enumerations instead. +j. Prefer enum class to straight enum. + + +(WRONG) +const double d = 0; +int i, j; +char *s; +float meanAndSigma(std::vector _v, float* _sigma, bool _approximate); +Derived* x(dynamic_cast(base)); +for (map::iterator i = l.begin(); i != l.end(); ++l) {} + + +(CORRECT) +enum class Accuracy +{ + Approximate, + Exact +}; +double const d = 0; +int i; +int j; +char* s; +std::tuple meanAndSigma(std::vector const& _v, Accuracy _a); +auto x = dynamic_cast(base); +for (auto i = x.begin(); i != x.end(); ++i) {} + + +7. Structs & classes + +a. Structs to be used when all members public and no virtual functions. +- In this case, members should be named naturally and not prefixed with 'm_' +b. Classes to be used in all other circumstances. + + + +8. Members: + +a. One member per line only. +b. Private, non-static, non-const fields prefixed with m_. +c. Avoid public fields, except in structs. +d. Use override, final and const as much as possible. +e. No implementations with the class declaration, except: +- template or force-inline method (though prefer implementation at bottom of header file). +- one-line implementation (in which case include it in same line as declaration). +f. For a property 'foo' +- Member: m_foo; +- Getter: foo() [ also: for booleans, isFoo() ]; +- Setter: setFoo(); + + + +9. Naming + +a. Collection conventions: +- -s means std::vector e.g. using MyTypes = std::vector +- -Set means std::set e.g. using MyTypeSet = std::set +- -Hash means std::unordered_set e.g. using MyTypeHash = std::unordered_set +b. Class conventions: +- -Face means the interface of some shared concept. (e.g. FooFace might be a pure virtual class.) +c. Avoid unpronouncable names; +- If you need to shorten a name favour a pronouncable slice of the original to a scattered set of consonants. +- e.g. Manager shortens to Man rather than Mgr. +d. Avoid prefixes of initials (e.g. DON'T use IMyInterface, CMyImplementation) +e. Find short, memorable & (at least semi-) descriptive names for commonly used classes or name-fragments. +- A dictionary and thesaurus are your friends. +- Spell correctly. +- Think carefully about the class's purpose. +- Imagine it as an isolated component to try to decontextualise it when considering its name. +- Don't be trapped into naming it (purely) in terms of its implementation. + + + +10. Type-definitions + +a. Prefer 'using' to 'typedef'. e.g. using ints = std::vector; rather than typedef std::vector ints; +b. Generally avoid shortening a standard form that already includes all important information: +- e.g. stick to shared_ptr rather than shortening to ptr. +c. 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; ///< Guard is used throughout the codebase since it's clear in meaning and used commonly. +d. In general expressions should be roughly as important/semantically meaningful as the space they occupy. + + + +11. Commenting + +a. Comments should be doxygen-compilable, using @notation rather than \notation. +b. Document the interface, not the implementation. +- Documentation should be able to remain completely unchanged, even if the method is reimplemented. +- Comment in terms of the method properties and intended alteration to class state (or what aspects of the state it reports). +- Be careful to scrutinise documentation that extends only to intended purpose and usage. +- Reject documentation that is simply an English transaction of the implementation. + + + +12. Include Headers + +Includes should go in increasing order of generality (libethereum -> libethcore -> libdevcrypto -> libdevcore -> boost -> STL). For example: + +#include +#include +#include +#include +#include +#include +#include +#include + +See http://stackoverflow.com/questions/614302/c-header-order/614333#614333 for the reason: this makes it easier to find missing includes in header files. + + + +13. Logging + +Logging should be performed at appropriate verbosities depending on the logging message. +The more likely a message is to repeat (and thus cause noise) the higher in verbosity it should be. +Some rules to keep in mind: + + - Verbosity == 0 -> Reserved for important stuff that users must see and can understand. + - Verbosity == 1 -> Reserved for stuff that users don't need to see but can understand. + - Verbosity >= 2 -> Anything that is or might be displayed more than once every minute + - Verbosity >= 3 -> Anything that only a developer would understand + - Verbosity >= 4 -> Anything that is low-level (e.g. peer disconnects, timers being cancelled) + + +14. Recommended reading + +Herb Sutter and Bjarne Stroustrup +- "C++ Core Guidelines" (https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md) + +Herb Sutter and Andrei Alexandrescu +- "C++ Coding Standards: 101 Rules, Guidelines, and Best Practices" + +Scott Meyers +- "Effective C++: 55 Specific Ways to Improve Your Programs and Designs (3rd Edition)" +- "More Effective C++: 35 New Ways to Improve Your Programs and Designs" +- "Effective Modern C++: 42 Specific Ways to Improve Your Use of C++11 and C++14" diff --git a/docs/contributing.rst b/docs/contributing.rst index a5efba8bf..8c190a266 100644 --- a/docs/contributing.rst +++ b/docs/contributing.rst @@ -55,8 +55,8 @@ However, if you are making a larger change, please consult with the `Solidity De focused on compiler and language development instead of language use) first. -Finally, please make sure you respect the `coding standards -`_ +Finally, please make sure you respect the `coding style +`_ for this project. Also, even though we do CI testing, please test your code and ensure that it builds locally before submitting a pull request. From 16780ea3eda597d0f20975eca88f5fc82febaa89 Mon Sep 17 00:00:00 2001 From: chriseth Date: Wed, 7 Mar 2018 20:26:22 +0100 Subject: [PATCH 003/197] Set version to 0.4.22. --- CMakeLists.txt | 2 +- Changelog.md | 9 +++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 5d4ff1614..208e405d9 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.21") +set(PROJECT_VERSION "0.4.22") project(solidity VERSION ${PROJECT_VERSION}) option(SOLC_LINK_STATIC "Link solc executable statically on supported platforms" OFF) diff --git a/Changelog.md b/Changelog.md index 98528893d..c9bffa64a 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,3 +1,12 @@ +### 0.4.22 (unreleased) + +Features: + + +Bugfixes: + + + ### 0.4.21 (2018-03-07) Features: From 7ebd580954b355605025a9ef46602382145a2cdc Mon Sep 17 00:00:00 2001 From: Grzegorz Hasse Date: Wed, 7 Mar 2018 21:27:24 -0800 Subject: [PATCH 004/197] Fix a typo. --- docs/abi-spec.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/abi-spec.rst b/docs/abi-spec.rst index 4a61d91f0..4d84a7dae 100644 --- a/docs/abi-spec.rst +++ b/docs/abi-spec.rst @@ -58,7 +58,7 @@ The following elementary types exist: - ``bytes``: binary type of ``M`` bytes, ``0 < M <= 32``. -- ``function``: an address (20 bytes) folled by a function selector (4 bytes). Encoded identical to ``bytes24``. +- ``function``: an address (20 bytes) followed by a function selector (4 bytes). Encoded identical to ``bytes24``. The following (fixed-size) array type exists: From 0649f900cacdbe43c127d543c47e834b80210949 Mon Sep 17 00:00:00 2001 From: chriseth Date: Fri, 9 Mar 2018 14:06:54 +0100 Subject: [PATCH 005/197] Properly skip cleanup if only enlarging storage array. --- Changelog.md | 1 + libsolidity/codegen/ArrayUtils.cpp | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/Changelog.md b/Changelog.md index c9bffa64a..6a25569fa 100644 --- a/Changelog.md +++ b/Changelog.md @@ -4,6 +4,7 @@ Features: Bugfixes: + * Code Generator: Properly skip unneeded storgae array cleanup when not reducing length. diff --git a/libsolidity/codegen/ArrayUtils.cpp b/libsolidity/codegen/ArrayUtils.cpp index ce8cbb5f2..4703fc1f6 100644 --- a/libsolidity/codegen/ArrayUtils.cpp +++ b/libsolidity/codegen/ArrayUtils.cpp @@ -741,10 +741,10 @@ void ArrayUtils::resizeDynamicArray(ArrayType const& _typeIn) const if (_type.isByteArray()) // For a "long" byte array, store length as 2*length+1 _context << Instruction::DUP1 << Instruction::ADD << u256(1) << Instruction::ADD; - _context<< Instruction::DUP4 << Instruction::SSTORE; + _context << Instruction::DUP4 << Instruction::SSTORE; // skip if size is not reduced _context << Instruction::DUP2 << Instruction::DUP2 - << Instruction::ISZERO << Instruction::GT; + << Instruction::GT << Instruction::ISZERO; _context.appendConditionalJumpTo(resizeEnd); // size reduced, clear the end of the array From a0907e90c6bd33f4788cb2409f233ed054d782c6 Mon Sep 17 00:00:00 2001 From: dongsamb Date: Sat, 10 Mar 2018 11:27:23 +0900 Subject: [PATCH 006/197] Add Korean to Translation --- docs/index.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/index.rst b/docs/index.rst index 184d0e692..8c18c96e4 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -37,6 +37,7 @@ This documentation is translated into several languages by community volunteers, * `Simplified Chinese `_ (in progress) * `Spanish `_ * `Russian `_ (rather outdated) +* `Korean `_ (in progress) Useful links From 413053d6cbb30b5c1e4526c9df4f9889bc678324 Mon Sep 17 00:00:00 2001 From: chriseth Date: Mon, 12 Mar 2018 11:37:44 +0100 Subject: [PATCH 007/197] New release step. --- ReleaseChecklist.md | 1 + 1 file changed, 1 insertion(+) diff --git a/ReleaseChecklist.md b/ReleaseChecklist.md index b5df9fdac..82b1308c7 100644 --- a/ReleaseChecklist.md +++ b/ReleaseChecklist.md @@ -11,6 +11,7 @@ Checklist for making a release: - [ ] 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). - [ ] Update the homebrew realease in https://github.com/ethereum/homebrew-ethereum/blob/master/solidity.rb (version and hash) + - [ ] Update the default version on readthedocs. - [ ] Make a release of ``solc-js``: Increment the version number, create a pull request for that, merge it after tests succeeded. - [ ] Run ``npm publish`` in the updated ``solc-js`` repository. - [ ] Create a commit to increase the version number on ``develop`` in ``CMakeLists.txt`` and add a new skeleton changelog entry. From 7d206ba64ad4590b42aa9ea8d89145aff39c70e0 Mon Sep 17 00:00:00 2001 From: wbt Date: Mon, 12 Mar 2018 10:02:24 -0400 Subject: [PATCH 008/197] Noted `suicide` is deprecated (#3692) According to the [changelog](https://github.com/ethereum/solidity/blob/b5e804b8caba0cc84514898323df91a025705177/Changelog.md), `suicide` was deprecated before 0.4.3 (after 0.2.0) and warning by 0.4.17. --- docs/miscellaneous.rst | 2 +- docs/units-and-global-variables.rst | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/miscellaneous.rst b/docs/miscellaneous.rst index 075b6be02..a7d5c4456 100644 --- a/docs/miscellaneous.rst +++ b/docs/miscellaneous.rst @@ -341,7 +341,7 @@ Global Variables - ``this`` (current contract's type): the current contract, explicitly convertible to ``address`` - ``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)``: an alias to ``selfdestruct`` +- ``suicide(address recipient)``: a deprecated alias to ``selfdestruct`` - ``
.balance`` (``uint256``): balance of the :ref:`address` in Wei - ``
.send(uint256 amount) returns (bool)``: send given amount of Wei to :ref:`address`, returns ``false`` on failure - ``
.transfer(uint256 amount)``: send given amount of Wei to :ref:`address`, throws on failure diff --git a/docs/units-and-global-variables.rst b/docs/units-and-global-variables.rst index 1b58b1e86..d789e87f3 100644 --- a/docs/units-and-global-variables.rst +++ b/docs/units-and-global-variables.rst @@ -183,7 +183,7 @@ Contract Related destroy the current contract, sending its funds to the given :ref:`address` ``suicide(address recipient)``: - alias to ``selfdestruct`` + deprecated alias to ``selfdestruct`` Furthermore, all functions of the current contract are callable directly including the current function. From a59d6d2e5303e714a6e66a5d11bd6a9c2e904e4e Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Mon, 12 Mar 2018 18:11:08 +0100 Subject: [PATCH 009/197] Support constantinople in evm-version --- Changelog.md | 5 ++--- libsolidity/interface/EVMVersion.h | 2 +- test/libsolidity/StandardCompiler.cpp | 2 ++ 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/Changelog.md b/Changelog.md index 6a25569fa..648af66c6 100644 --- a/Changelog.md +++ b/Changelog.md @@ -2,11 +2,10 @@ Features: - Bugfixes: * Code Generator: Properly skip unneeded storgae array cleanup when not reducing length. - - + * Commandline interface: Support ``--evm-version constantinople`` properly. + * Standard JSON: Support ``constantinople`` as ``evmVersion`` properly. ### 0.4.21 (2018-03-07) diff --git a/libsolidity/interface/EVMVersion.h b/libsolidity/interface/EVMVersion.h index 13c4ec94d..b68e1f4eb 100644 --- a/libsolidity/interface/EVMVersion.h +++ b/libsolidity/interface/EVMVersion.h @@ -49,7 +49,7 @@ public: static boost::optional fromString(std::string const& _version) { - for (auto const& v: {homestead(), tangerineWhistle(), spuriousDragon(), byzantium()}) + for (auto const& v: {homestead(), tangerineWhistle(), spuriousDragon(), byzantium(), constantinople()}) if (_version == v.name()) return v; return {}; diff --git a/test/libsolidity/StandardCompiler.cpp b/test/libsolidity/StandardCompiler.cpp index 4c8918be8..dd6eb7c40 100644 --- a/test/libsolidity/StandardCompiler.cpp +++ b/test/libsolidity/StandardCompiler.cpp @@ -752,6 +752,8 @@ BOOST_AUTO_TEST_CASE(evm_version) BOOST_CHECK(result["contracts"]["fileA"]["A"]["metadata"].asString().find("\"evmVersion\":\"spuriousDragon\"") != string::npos); result = compile(inputForVersion("\"evmVersion\": \"byzantium\",")); BOOST_CHECK(result["contracts"]["fileA"]["A"]["metadata"].asString().find("\"evmVersion\":\"byzantium\"") != string::npos); + result = compile(inputForVersion("\"evmVersion\": \"constantinople\",")); + BOOST_CHECK(result["contracts"]["fileA"]["A"]["metadata"].asString().find("\"evmVersion\":\"constantinople\"") != string::npos); // test default result = compile(inputForVersion("")); BOOST_CHECK(result["contracts"]["fileA"]["A"]["metadata"].asString().find("\"evmVersion\":\"byzantium\"") != string::npos); From 121f87b043d7c3f01c760589edf1bf342d67c634 Mon Sep 17 00:00:00 2001 From: Daniel Kirchner Date: Mon, 12 Mar 2018 14:13:19 +0100 Subject: [PATCH 010/197] Move test tools to the subdirectory test/tools and adjust CMakeLists.txt. --- circle.yml | 2 +- scripts/uniqueErrors.sh | 2 +- test/CMakeLists.txt | 30 ++++++++++++++++++++++++------ test/cmdlineTests.sh | 4 ++-- test/tools/CMakeLists.txt | 2 ++ test/{ => tools}/fuzzer.cpp | 0 6 files changed, 30 insertions(+), 10 deletions(-) create mode 100644 test/tools/CMakeLists.txt rename test/{ => tools}/fuzzer.cpp (100%) diff --git a/circle.yml b/circle.yml index 263cb700e..49c73ce4d 100644 --- a/circle.yml +++ b/circle.yml @@ -126,7 +126,7 @@ jobs: paths: - solc/solc - test/soltest - - test/solfuzzer + - test/tools/solfuzzer test_x86: docker: diff --git a/scripts/uniqueErrors.sh b/scripts/uniqueErrors.sh index eee1df903..fa2c7b4c0 100755 --- a/scripts/uniqueErrors.sh +++ b/scripts/uniqueErrors.sh @@ -9,6 +9,6 @@ do echo -n $x " # " # This subshell is a workaround to prevent the shell from printing # "Aborted" - ("$REPO"/build/test/solfuzzer < "$x" || true) 2>&1 | head -n 1 + ("$REPO"/build/test/tools/solfuzzer < "$x" || true) 2>&1 | head -n 1 done ) | sort -u -t'#' -k 2 diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index f36ad4c53..522856cc7 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -1,9 +1,27 @@ -file(GLOB_RECURSE sources "*.cpp") -list(REMOVE_ITEM sources "${CMAKE_CURRENT_SOURCE_DIR}/fuzzer.cpp") -file(GLOB_RECURSE headers "*.h") +file(GLOB sources "*.cpp") +file(GLOB headers "*.h") -add_executable(soltest ${sources} ${headers}) +file(GLOB contracts_sources "contracts/*.cpp") +file(GLOB contracts_headers "contracts/*.h") +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 liblll_sources "liblll/*.cpp") +file(GLOB liblll_headers "liblll/*.h") +file(GLOB libsolidity_sources "libsolidity/*.cpp") +file(GLOB libsolidity_headers "libsolidity/*.h") + +add_executable(soltest ${sources} ${headers} + ${contracts_sources} ${contracts_headers} + ${libdevcore_sources} ${libdevcore_headers} + ${libevmasm_sources} ${libevmasm_headers} + ${libjulia_sources} ${libjulia_headers} + ${liblll_sources} ${liblll_headers} + ${libsolidity_sources} ${libsolidity_headers} +) target_link_libraries(soltest PRIVATE libsolc solidity lll evmasm devcore ${Boost_UNIT_TEST_FRAMEWORK_LIBRARIES}) -add_executable(solfuzzer fuzzer.cpp) -target_link_libraries(solfuzzer PRIVATE libsolc evmasm ${Boost_PROGRAM_OPTIONS_LIBRARIES}) +add_subdirectory(tools) diff --git a/test/cmdlineTests.sh b/test/cmdlineTests.sh index e86e0ad48..92f9569a1 100755 --- a/test/cmdlineTests.sh +++ b/test/cmdlineTests.sh @@ -170,14 +170,14 @@ TMPDIR=$(mktemp -d) for f in *.sol do set +e - "$REPO_ROOT"/build/test/solfuzzer --quiet < "$f" + "$REPO_ROOT"/build/test/tools/solfuzzer --quiet < "$f" if [ $? -ne 0 ]; then printError "Fuzzer failed on:" cat "$f" exit 1 fi - "$REPO_ROOT"/build/test/solfuzzer --without-optimizer --quiet < "$f" + "$REPO_ROOT"/build/test/tools/solfuzzer --without-optimizer --quiet < "$f" if [ $? -ne 0 ]; then printError "Fuzzer (without optimizer) failed on:" cat "$f" diff --git a/test/tools/CMakeLists.txt b/test/tools/CMakeLists.txt new file mode 100644 index 000000000..a693ebab4 --- /dev/null +++ b/test/tools/CMakeLists.txt @@ -0,0 +1,2 @@ +add_executable(solfuzzer fuzzer.cpp) +target_link_libraries(solfuzzer PRIVATE libsolc evmasm ${Boost_PROGRAM_OPTIONS_LIBRARIES}) diff --git a/test/fuzzer.cpp b/test/tools/fuzzer.cpp similarity index 100% rename from test/fuzzer.cpp rename to test/tools/fuzzer.cpp From 6a940f0a99e941c48e5deb695e89ac52784c4f3c Mon Sep 17 00:00:00 2001 From: Leonardo Alt Date: Sun, 21 Jan 2018 13:58:56 +0100 Subject: [PATCH 011/197] [SMTChecker] Support to Bool variables --- libsolidity/formal/SMTChecker.cpp | 2 +- libsolidity/formal/SSAVariable.cpp | 6 +- libsolidity/formal/SSAVariable.h | 2 +- libsolidity/formal/SolverInterface.h | 15 ++-- libsolidity/formal/SymbolicBoolVariable.cpp | 43 +++++++++ libsolidity/formal/SymbolicBoolVariable.h | 47 ++++++++++ test/libsolidity/SMTChecker.cpp | 98 +++++++++++++++++++++ 7 files changed, 205 insertions(+), 8 deletions(-) create mode 100644 libsolidity/formal/SymbolicBoolVariable.cpp create mode 100644 libsolidity/formal/SymbolicBoolVariable.h diff --git a/libsolidity/formal/SMTChecker.cpp b/libsolidity/formal/SMTChecker.cpp index 1da5b291e..83784ead5 100644 --- a/libsolidity/formal/SMTChecker.cpp +++ b/libsolidity/formal/SMTChecker.cpp @@ -444,7 +444,7 @@ void SMTChecker::arithmeticOperation(BinaryOperation const& _op) void SMTChecker::compareOperation(BinaryOperation const& _op) { solAssert(_op.annotation().commonType, ""); - if (_op.annotation().commonType->category() == Type::Category::Integer) + if (SSAVariable::supportedType(_op.annotation().commonType.get())) { smt::Expression left(expr(_op.leftExpression())); smt::Expression right(expr(_op.rightExpression())); diff --git a/libsolidity/formal/SSAVariable.cpp b/libsolidity/formal/SSAVariable.cpp index 4e6bcbcb5..4f8080ab3 100644 --- a/libsolidity/formal/SSAVariable.cpp +++ b/libsolidity/formal/SSAVariable.cpp @@ -17,6 +17,7 @@ #include +#include #include #include @@ -34,6 +35,8 @@ SSAVariable::SSAVariable( if (dynamic_cast(_decl->type().get())) m_symbolicVar = make_shared(_decl, _interface); + else if (dynamic_cast(_decl->type().get())) + m_symbolicVar = make_shared(_decl, _interface); else { solAssert(false, ""); @@ -42,7 +45,8 @@ SSAVariable::SSAVariable( bool SSAVariable::supportedType(Type const* _decl) { - return dynamic_cast(_decl); + return dynamic_cast(_decl) || + dynamic_cast(_decl); } void SSAVariable::resetIndex() diff --git a/libsolidity/formal/SSAVariable.h b/libsolidity/formal/SSAVariable.h index 275e85900..d283ad9ea 100644 --- a/libsolidity/formal/SSAVariable.h +++ b/libsolidity/formal/SSAVariable.h @@ -68,7 +68,7 @@ public: void setZeroValue(); void setUnknownValue(); - /// So far Int is supported. + /// So far Int and Bool are supported. static bool supportedType(Type const* _decl); private: diff --git a/libsolidity/formal/SolverInterface.h b/libsolidity/formal/SolverInterface.h index 884873100..38d3236eb 100644 --- a/libsolidity/formal/SolverInterface.h +++ b/libsolidity/formal/SolverInterface.h @@ -46,7 +46,8 @@ enum class Sort { Int, Bool, - IntIntFun // Function of one Int returning a single Int + IntIntFun, // Function of one Int returning a single Int + IntBoolFun // Function of one Int returning a single Bool }; /// C++ representation of an SMTLIB2 expression. @@ -132,10 +133,12 @@ public: Expression operator()(Expression _a) const { solAssert( - sort == Sort::IntIntFun && arguments.empty(), + (sort == Sort::IntIntFun || sort == Sort::IntBoolFun) && arguments.empty(), "Attempted function application to non-function." ); - return Expression(name, _a, Sort::Int); + if (sort == Sort::IntIntFun) + return Expression(name, _a, Sort::Int); + return Expression(name, _a, Sort::Bool); } std::string const name; @@ -167,9 +170,11 @@ public: virtual Expression newFunction(std::string _name, Sort _domain, Sort _codomain) { - solAssert(_domain == Sort::Int && _codomain == Sort::Int, "Function sort not supported."); + solAssert(_domain == Sort::Int && (_codomain == Sort::Int || _codomain == Sort::Bool), "Function sort not supported."); // Subclasses should do something here - return Expression(std::move(_name), {}, Sort::IntIntFun); + if (_codomain == Sort::Int) + return Expression(std::move(_name), {}, Sort::IntIntFun); + return Expression(std::move(_name), {}, Sort::IntBoolFun); } virtual Expression newInteger(std::string _name) { diff --git a/libsolidity/formal/SymbolicBoolVariable.cpp b/libsolidity/formal/SymbolicBoolVariable.cpp new file mode 100644 index 000000000..e2a5687db --- /dev/null +++ b/libsolidity/formal/SymbolicBoolVariable.cpp @@ -0,0 +1,43 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ + +#include + +#include + +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, ""); + m_expression = make_shared(m_interface.newFunction(uniqueSymbol(), smt::Sort::Int, smt::Sort::Bool)); +} + +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 new file mode 100644 index 000000000..bff56ad0a --- /dev/null +++ b/libsolidity/formal/SymbolicBoolVariable.h @@ -0,0 +1,47 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ + +#pragma once + +#include + +#include + +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); +}; + +} +} diff --git a/test/libsolidity/SMTChecker.cpp b/test/libsolidity/SMTChecker.cpp index 12b5f4396..b97fc80e7 100644 --- a/test/libsolidity/SMTChecker.cpp +++ b/test/libsolidity/SMTChecker.cpp @@ -329,6 +329,104 @@ BOOST_AUTO_TEST_CASE(ways_to_merge_variables) CHECK_WARNING(text, "Assertion violation happens here"); } +BOOST_AUTO_TEST_CASE(bool_simple) +{ + string text = R"( + contract C { + function f(bool x) public pure { + assert(x); + } + } + )"; + CHECK_WARNING(text, "Assertion violation happens here"); + text = R"( + contract C { + function f(bool x, bool y) public pure { + assert(x == y); + } + } + )"; + CHECK_WARNING(text, "Assertion violation happens here"); + text = R"( + contract C { + function f(bool x, bool y) public pure { + bool z = x || y; + assert(!(x && y) || z); + } + } + )"; + CHECK_SUCCESS_NO_WARNINGS(text); + text = R"( + contract C { + function f(bool x) public pure { + if(x) { + assert(x); + } else { + assert(!x); + } + } + } + )"; + CHECK_SUCCESS_NO_WARNINGS(text); + text = R"( + contract C { + function f(bool x) public pure { + bool y = x; + assert(x == y); + } + } + )"; + CHECK_SUCCESS_NO_WARNINGS(text); +} + +BOOST_AUTO_TEST_CASE(bool_int_mixed) +{ + string text = R"( + contract C { + function f(bool x) public pure { + uint a; + if(x) + a = 1; + assert(!x || a > 0); + } + } + )"; + CHECK_SUCCESS_NO_WARNINGS(text); + text = R"( + contract C { + function f(bool x, uint a) public pure { + require(!x || a > 0); + uint b = a; + assert(!x || b > 0); + } + } + )"; + CHECK_SUCCESS_NO_WARNINGS(text); + text = R"( + contract C { + function f(bool x, bool y) public pure { + uint a; + if (x) { + if (y) { + a = 0; + } else { + a = 1; + } + } else { + if (y) { + a = 1; + } else { + a = 0; + } + } + bool xor_x_y = (x && !y) || (!x && y); + assert(!xor_x_y || a > 0); + } + } + )"; + CHECK_SUCCESS_NO_WARNINGS(text); +} + BOOST_AUTO_TEST_CASE(while_loop_simple) { // Check that variables are cleared From c2d26eb6a20a21f5fe4b5d78a39f8c23f7f3f5cb Mon Sep 17 00:00:00 2001 From: Leonardo Alt Date: Fri, 9 Mar 2018 16:19:03 +0100 Subject: [PATCH 012/197] [SMTChecker_Bool] Fix PR comments; Add support to gt, ge, lt, le. and tests. --- libsolidity/formal/SMTChecker.cpp | 48 ++++++++++++++------- libsolidity/formal/SSAVariable.cpp | 21 ++++++--- libsolidity/formal/SSAVariable.h | 6 ++- libsolidity/formal/SolverInterface.h | 29 ++++++++++--- libsolidity/formal/SymbolicBoolVariable.cpp | 4 +- libsolidity/formal/SymbolicBoolVariable.h | 2 +- libsolidity/formal/SymbolicIntVariable.cpp | 6 +-- libsolidity/formal/SymbolicIntVariable.h | 2 +- libsolidity/formal/SymbolicVariable.cpp | 4 +- libsolidity/formal/SymbolicVariable.h | 4 +- test/libsolidity/SMTChecker.cpp | 40 +++++++++++++++++ 11 files changed, 125 insertions(+), 41 deletions(-) diff --git a/libsolidity/formal/SMTChecker.cpp b/libsolidity/formal/SMTChecker.cpp index 83784ead5..d4f94f16c 100644 --- a/libsolidity/formal/SMTChecker.cpp +++ b/libsolidity/formal/SMTChecker.cpp @@ -205,7 +205,7 @@ void SMTChecker::endVisit(Assignment const& _assignment) _assignment.location(), "Assertion checker does not yet implement compound assignment." ); - else if (_assignment.annotation().type->category() != Type::Category::Integer) + else if (!SSAVariable::supportedType(_assignment.annotation().type->category())) m_errorReporter.warning( _assignment.location(), "Assertion checker does not yet implement type " + _assignment.annotation().type->toString() @@ -266,14 +266,15 @@ void SMTChecker::endVisit(UnaryOperation const& _op) { case Token::Not: // ! { - solAssert(_op.annotation().type->category() == Type::Category::Bool, ""); + solAssert(SSAVariable::typeBool(_op.annotation().type->category()), ""); defineExpr(_op, !expr(_op.subExpression())); break; } case Token::Inc: // ++ (pre- or postfix) case Token::Dec: // -- (pre- or postfix) { - solAssert(_op.annotation().type->category() == Type::Category::Integer, ""); + + solAssert(SSAVariable::typeInteger(_op.annotation().type->category()), ""); solAssert(_op.subExpression().annotation().lValueRequested, ""); if (Identifier const* identifier = dynamic_cast(&_op.subExpression())) { @@ -370,7 +371,7 @@ void SMTChecker::endVisit(Identifier const& _identifier) { // Will be translated as part of the node that requested the lvalue. } - else if (SSAVariable::supportedType(_identifier.annotation().type.get())) + else if (SSAVariable::supportedType(_identifier.annotation().type->category())) defineExpr(_identifier, currentValue(*decl)); else if (FunctionType const* fun = dynamic_cast(_identifier.annotation().type.get())) { @@ -444,21 +445,36 @@ void SMTChecker::arithmeticOperation(BinaryOperation const& _op) void SMTChecker::compareOperation(BinaryOperation const& _op) { solAssert(_op.annotation().commonType, ""); - if (SSAVariable::supportedType(_op.annotation().commonType.get())) + if (SSAVariable::supportedType(_op.annotation().commonType->category())) { smt::Expression left(expr(_op.leftExpression())); smt::Expression right(expr(_op.rightExpression())); Token::Value op = _op.getOperator(); - smt::Expression value = ( - 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) - ); + shared_ptr value; + if (SSAVariable::typeInteger(_op.annotation().commonType->category())) + { + value = make_shared( + 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) + ); + } + else // Bool + { + value = make_shared( + 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) + ); + } // TODO: check that other values for op are not possible. - defineExpr(_op, value); + defineExpr(_op, *value); } else m_errorReporter.warning( @@ -728,10 +744,10 @@ void SMTChecker::mergeVariables(vector const& _variables, sm bool SMTChecker::createVariable(VariableDeclaration const& _varDecl) { - if (SSAVariable::supportedType(_varDecl.type().get())) + if (SSAVariable::supportedType(_varDecl.type()->category())) { solAssert(m_variables.count(&_varDecl) == 0, ""); - m_variables.emplace(&_varDecl, SSAVariable(&_varDecl, *m_interface)); + m_variables.emplace(&_varDecl, SSAVariable(_varDecl, *m_interface)); return true; } else diff --git a/libsolidity/formal/SSAVariable.cpp b/libsolidity/formal/SSAVariable.cpp index 4f8080ab3..3f2a61f1d 100644 --- a/libsolidity/formal/SSAVariable.cpp +++ b/libsolidity/formal/SSAVariable.cpp @@ -27,15 +27,15 @@ using namespace dev; using namespace dev::solidity; SSAVariable::SSAVariable( - Declaration const* _decl, + Declaration const& _decl, smt::SolverInterface& _interface ) { resetIndex(); - if (dynamic_cast(_decl->type().get())) + if (typeInteger(_decl.type()->category())) m_symbolicVar = make_shared(_decl, _interface); - else if (dynamic_cast(_decl->type().get())) + else if (typeBool(_decl.type()->category())) m_symbolicVar = make_shared(_decl, _interface); else { @@ -43,10 +43,19 @@ SSAVariable::SSAVariable( } } -bool SSAVariable::supportedType(Type const* _decl) +bool SSAVariable::supportedType(Type::Category _category) { - return dynamic_cast(_decl) || - dynamic_cast(_decl); + return typeInteger(_category) || typeBool(_category); +} + +bool SSAVariable::typeInteger(Type::Category _category) +{ + return _category == Type::Category::Integer; +} + +bool SSAVariable::typeBool(Type::Category _category) +{ + return _category == Type::Category::Bool; } void SSAVariable::resetIndex() diff --git a/libsolidity/formal/SSAVariable.h b/libsolidity/formal/SSAVariable.h index d283ad9ea..7e2ebc8c4 100644 --- a/libsolidity/formal/SSAVariable.h +++ b/libsolidity/formal/SSAVariable.h @@ -37,7 +37,7 @@ 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, + Declaration const& _decl, smt::SolverInterface& _interface ); @@ -69,7 +69,9 @@ public: void setUnknownValue(); /// So far Int and Bool are supported. - static bool supportedType(Type const* _decl); + static bool supportedType(Type::Category _category); + static bool typeInteger(Type::Category _category); + static bool typeBool(Type::Category _category); private: smt::Expression valueAtSequence(int _seq) const diff --git a/libsolidity/formal/SolverInterface.h b/libsolidity/formal/SolverInterface.h index 38d3236eb..0bdebb6c7 100644 --- a/libsolidity/formal/SolverInterface.h +++ b/libsolidity/formal/SolverInterface.h @@ -133,12 +133,22 @@ public: Expression operator()(Expression _a) const { solAssert( - (sort == Sort::IntIntFun || sort == Sort::IntBoolFun) && arguments.empty(), + arguments.empty(), "Attempted function application to non-function." ); - if (sort == Sort::IntIntFun) + switch (sort) + { + case Sort::IntIntFun: return Expression(name, _a, Sort::Int); - return Expression(name, _a, Sort::Bool); + case Sort::IntBoolFun: + return Expression(name, _a, Sort::Bool); + default: + solAssert( + false, + "Attempted function application to invalid type." + ); + break; + } } std::string const name; @@ -170,11 +180,18 @@ public: virtual Expression newFunction(std::string _name, Sort _domain, Sort _codomain) { - solAssert(_domain == Sort::Int && (_codomain == Sort::Int || _codomain == Sort::Bool), "Function sort not supported."); + solAssert(_domain == Sort::Int, "Function sort not supported."); // Subclasses should do something here - if (_codomain == Sort::Int) + switch (_codomain) + { + case Sort::Int: return Expression(std::move(_name), {}, Sort::IntIntFun); - return Expression(std::move(_name), {}, Sort::IntBoolFun); + case Sort::Bool: + return Expression(std::move(_name), {}, Sort::IntBoolFun); + default: + solAssert(false, "Function sort not supported."); + break; + } } virtual Expression newInteger(std::string _name) { diff --git a/libsolidity/formal/SymbolicBoolVariable.cpp b/libsolidity/formal/SymbolicBoolVariable.cpp index e2a5687db..e5c56e461 100644 --- a/libsolidity/formal/SymbolicBoolVariable.cpp +++ b/libsolidity/formal/SymbolicBoolVariable.cpp @@ -24,12 +24,12 @@ using namespace dev; using namespace dev::solidity; SymbolicBoolVariable::SymbolicBoolVariable( - Declaration const* _decl, + Declaration const& _decl, smt::SolverInterface&_interface ): SymbolicVariable(_decl, _interface) { - solAssert(m_declaration->type()->category() == Type::Category::Bool, ""); + solAssert(m_declaration.type()->category() == Type::Category::Bool, ""); m_expression = make_shared(m_interface.newFunction(uniqueSymbol(), smt::Sort::Int, smt::Sort::Bool)); } diff --git a/libsolidity/formal/SymbolicBoolVariable.h b/libsolidity/formal/SymbolicBoolVariable.h index bff56ad0a..3510b7703 100644 --- a/libsolidity/formal/SymbolicBoolVariable.h +++ b/libsolidity/formal/SymbolicBoolVariable.h @@ -33,7 +33,7 @@ class SymbolicBoolVariable: public SymbolicVariable { public: SymbolicBoolVariable( - Declaration const* _decl, + Declaration const& _decl, smt::SolverInterface& _interface ); diff --git a/libsolidity/formal/SymbolicIntVariable.cpp b/libsolidity/formal/SymbolicIntVariable.cpp index d08dc1553..eb7b1c175 100644 --- a/libsolidity/formal/SymbolicIntVariable.cpp +++ b/libsolidity/formal/SymbolicIntVariable.cpp @@ -24,12 +24,12 @@ using namespace dev; using namespace dev::solidity; SymbolicIntVariable::SymbolicIntVariable( - Declaration const* _decl, + Declaration const& _decl, smt::SolverInterface& _interface ): SymbolicVariable(_decl, _interface) { - solAssert(m_declaration->type()->category() == Type::Category::Integer, ""); + solAssert(m_declaration.type()->category() == Type::Category::Integer, ""); m_expression = make_shared(m_interface.newFunction(uniqueSymbol(), smt::Sort::Int, smt::Sort::Int)); } @@ -40,7 +40,7 @@ void SymbolicIntVariable::setZeroValue(int _seq) void SymbolicIntVariable::setUnknownValue(int _seq) { - auto const& intType = dynamic_cast(*m_declaration->type()); + auto const& intType = dynamic_cast(*m_declaration.type()); m_interface.addAssertion(valueAtSequence(_seq) >= minValue(intType)); m_interface.addAssertion(valueAtSequence(_seq) <= maxValue(intType)); } diff --git a/libsolidity/formal/SymbolicIntVariable.h b/libsolidity/formal/SymbolicIntVariable.h index afa25f1b9..eb36b8996 100644 --- a/libsolidity/formal/SymbolicIntVariable.h +++ b/libsolidity/formal/SymbolicIntVariable.h @@ -33,7 +33,7 @@ class SymbolicIntVariable: public SymbolicVariable { public: SymbolicIntVariable( - Declaration const* _decl, + Declaration const& _decl, smt::SolverInterface& _interface ); diff --git a/libsolidity/formal/SymbolicVariable.cpp b/libsolidity/formal/SymbolicVariable.cpp index 629049ea5..d59b55b10 100644 --- a/libsolidity/formal/SymbolicVariable.cpp +++ b/libsolidity/formal/SymbolicVariable.cpp @@ -24,7 +24,7 @@ using namespace dev; using namespace dev::solidity; SymbolicVariable::SymbolicVariable( - Declaration const* _decl, + Declaration const& _decl, smt::SolverInterface& _interface ): m_declaration(_decl), @@ -34,7 +34,7 @@ SymbolicVariable::SymbolicVariable( string SymbolicVariable::uniqueSymbol() const { - return m_declaration->name() + "_" + to_string(m_declaration->id()); + return m_declaration.name() + "_" + to_string(m_declaration.id()); } diff --git a/libsolidity/formal/SymbolicVariable.h b/libsolidity/formal/SymbolicVariable.h index 932582503..75eb9fa5c 100644 --- a/libsolidity/formal/SymbolicVariable.h +++ b/libsolidity/formal/SymbolicVariable.h @@ -37,7 +37,7 @@ class SymbolicVariable { public: SymbolicVariable( - Declaration const* _decl, + Declaration const& _decl, smt::SolverInterface& _interface ); @@ -60,7 +60,7 @@ protected: return (*m_expression)(_seq); } - Declaration const* m_declaration; + Declaration const& m_declaration; std::shared_ptr m_expression = nullptr; smt::SolverInterface& m_interface; }; diff --git a/test/libsolidity/SMTChecker.cpp b/test/libsolidity/SMTChecker.cpp index b97fc80e7..beb933a4e 100644 --- a/test/libsolidity/SMTChecker.cpp +++ b/test/libsolidity/SMTChecker.cpp @@ -377,6 +377,46 @@ BOOST_AUTO_TEST_CASE(bool_simple) } )"; CHECK_SUCCESS_NO_WARNINGS(text); + text = R"( + contract C { + function f(bool x) public pure { + require(x); + bool y; + y = false; + assert(x || y); + } + } + )"; + 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) From 9b64dc501da5c743b948e4dca844a6cd67766be1 Mon Sep 17 00:00:00 2001 From: Leonardo Alt Date: Mon, 12 Mar 2018 20:15:27 +0100 Subject: [PATCH 013/197] [SMTChecker_Bool] Fix PR review comments: method renaming and solAssert --- libsolidity/formal/SMTChecker.cpp | 15 ++++++++------- libsolidity/formal/SSAVariable.cpp | 12 ++++++------ libsolidity/formal/SSAVariable.h | 6 +++--- 3 files changed, 17 insertions(+), 16 deletions(-) diff --git a/libsolidity/formal/SMTChecker.cpp b/libsolidity/formal/SMTChecker.cpp index d4f94f16c..8f4abdc27 100644 --- a/libsolidity/formal/SMTChecker.cpp +++ b/libsolidity/formal/SMTChecker.cpp @@ -205,7 +205,7 @@ void SMTChecker::endVisit(Assignment const& _assignment) _assignment.location(), "Assertion checker does not yet implement compound assignment." ); - else if (!SSAVariable::supportedType(_assignment.annotation().type->category())) + else if (!SSAVariable::isSupportedType(_assignment.annotation().type->category())) m_errorReporter.warning( _assignment.location(), "Assertion checker does not yet implement type " + _assignment.annotation().type->toString() @@ -266,7 +266,7 @@ void SMTChecker::endVisit(UnaryOperation const& _op) { case Token::Not: // ! { - solAssert(SSAVariable::typeBool(_op.annotation().type->category()), ""); + solAssert(SSAVariable::isBool(_op.annotation().type->category()), ""); defineExpr(_op, !expr(_op.subExpression())); break; } @@ -274,7 +274,7 @@ void SMTChecker::endVisit(UnaryOperation const& _op) case Token::Dec: // -- (pre- or postfix) { - solAssert(SSAVariable::typeInteger(_op.annotation().type->category()), ""); + solAssert(SSAVariable::isInteger(_op.annotation().type->category()), ""); solAssert(_op.subExpression().annotation().lValueRequested, ""); if (Identifier const* identifier = dynamic_cast(&_op.subExpression())) { @@ -371,7 +371,7 @@ void SMTChecker::endVisit(Identifier const& _identifier) { // Will be translated as part of the node that requested the lvalue. } - else if (SSAVariable::supportedType(_identifier.annotation().type->category())) + else if (SSAVariable::isSupportedType(_identifier.annotation().type->category())) defineExpr(_identifier, currentValue(*decl)); else if (FunctionType const* fun = dynamic_cast(_identifier.annotation().type.get())) { @@ -445,13 +445,13 @@ void SMTChecker::arithmeticOperation(BinaryOperation const& _op) void SMTChecker::compareOperation(BinaryOperation const& _op) { solAssert(_op.annotation().commonType, ""); - if (SSAVariable::supportedType(_op.annotation().commonType->category())) + if (SSAVariable::isSupportedType(_op.annotation().commonType->category())) { smt::Expression left(expr(_op.leftExpression())); smt::Expression right(expr(_op.rightExpression())); Token::Value op = _op.getOperator(); shared_ptr value; - if (SSAVariable::typeInteger(_op.annotation().commonType->category())) + if (SSAVariable::isInteger(_op.annotation().commonType->category())) { value = make_shared( op == Token::Equal ? (left == right) : @@ -464,6 +464,7 @@ void SMTChecker::compareOperation(BinaryOperation const& _op) } else // Bool { + solAssert(SSAVariable::isBool(_op.annotation().commonType->category()), ""); value = make_shared( op == Token::Equal ? (left == right) : op == Token::NotEqual ? (left != right) : @@ -744,7 +745,7 @@ void SMTChecker::mergeVariables(vector const& _variables, sm bool SMTChecker::createVariable(VariableDeclaration const& _varDecl) { - if (SSAVariable::supportedType(_varDecl.type()->category())) + if (SSAVariable::isSupportedType(_varDecl.type()->category())) { solAssert(m_variables.count(&_varDecl) == 0, ""); m_variables.emplace(&_varDecl, SSAVariable(_varDecl, *m_interface)); diff --git a/libsolidity/formal/SSAVariable.cpp b/libsolidity/formal/SSAVariable.cpp index 3f2a61f1d..f3213e03c 100644 --- a/libsolidity/formal/SSAVariable.cpp +++ b/libsolidity/formal/SSAVariable.cpp @@ -33,9 +33,9 @@ SSAVariable::SSAVariable( { resetIndex(); - if (typeInteger(_decl.type()->category())) + if (isInteger(_decl.type()->category())) m_symbolicVar = make_shared(_decl, _interface); - else if (typeBool(_decl.type()->category())) + else if (isBool(_decl.type()->category())) m_symbolicVar = make_shared(_decl, _interface); else { @@ -43,17 +43,17 @@ SSAVariable::SSAVariable( } } -bool SSAVariable::supportedType(Type::Category _category) +bool SSAVariable::isSupportedType(Type::Category _category) { - return typeInteger(_category) || typeBool(_category); + return isInteger(_category) || isBool(_category); } -bool SSAVariable::typeInteger(Type::Category _category) +bool SSAVariable::isInteger(Type::Category _category) { return _category == Type::Category::Integer; } -bool SSAVariable::typeBool(Type::Category _category) +bool SSAVariable::isBool(Type::Category _category) { return _category == Type::Category::Bool; } diff --git a/libsolidity/formal/SSAVariable.h b/libsolidity/formal/SSAVariable.h index 7e2ebc8c4..bf5dae3b5 100644 --- a/libsolidity/formal/SSAVariable.h +++ b/libsolidity/formal/SSAVariable.h @@ -69,9 +69,9 @@ public: void setUnknownValue(); /// So far Int and Bool are supported. - static bool supportedType(Type::Category _category); - static bool typeInteger(Type::Category _category); - static bool typeBool(Type::Category _category); + 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 From 49eaf7c3fd98a2983ac7c9f0994fbca0b73a33c1 Mon Sep 17 00:00:00 2001 From: Daniel Kirchner Date: Tue, 6 Mar 2018 20:45:34 +0100 Subject: [PATCH 014/197] Infrastructure for extracting syntax tests in separate test files. --- appveyor.yml | 2 +- scripts/isolate_tests.py | 7 +- scripts/tests.sh | 2 +- test/TestHelper.cpp | 9 ++ test/TestHelper.h | 1 + test/boostTest.cpp | 2 + .../SolidityNameAndTypeResolution.cpp | 52 ------- test/libsolidity/SyntaxTestParser.cpp | 97 +++++++++++++ test/libsolidity/SyntaxTestParser.h | 57 ++++++++ test/libsolidity/SyntaxTester.cpp | 134 ++++++++++++++++++ test/libsolidity/SyntaxTester.h | 48 +++++++ .../double_stateVariable_declaration.sol | 6 + .../double_variable_declaration.sol | 8 ++ .../double_variable_declaration_050.sol | 11 ++ test/libsolidity/syntaxTests/smoke_test.sol | 6 + 15 files changed, 387 insertions(+), 55 deletions(-) create mode 100644 test/libsolidity/SyntaxTestParser.cpp create mode 100644 test/libsolidity/SyntaxTestParser.h create mode 100644 test/libsolidity/SyntaxTester.cpp create mode 100644 test/libsolidity/SyntaxTester.h create mode 100644 test/libsolidity/syntaxTests/double_stateVariable_declaration.sol create mode 100644 test/libsolidity/syntaxTests/double_variable_declaration.sol create mode 100644 test/libsolidity/syntaxTests/double_variable_declaration_050.sol create mode 100644 test/libsolidity/syntaxTests/smoke_test.sol diff --git a/appveyor.yml b/appveyor.yml index ef5f69074..5fd85482f 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -71,7 +71,7 @@ build_script: test_script: - cd %APPVEYOR_BUILD_FOLDER%\build\test\%CONFIGURATION% - - soltest.exe --show-progress -- --no-ipc --no-smt + - soltest.exe --show-progress -- --testpath %APPVEYOR_BUILD_FOLDER%\test --no-ipc --no-smt # Skip bytecode compare if private key is not available - cd %APPVEYOR_BUILD_FOLDER% - ps: if ($env:priv_key) { diff --git a/scripts/isolate_tests.py b/scripts/isolate_tests.py index cfaef2106..5bf577d3b 100755 --- a/scripts/isolate_tests.py +++ b/scripts/isolate_tests.py @@ -86,10 +86,15 @@ if __name__ == '__main__': 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: - cases = extract_test_cases(path) + if f.endswith(".sol"): + cases = [open(path, "r").read()] + else: + cases = extract_test_cases(path) write_cases(cases) diff --git a/scripts/tests.sh b/scripts/tests.sh index bf4ee3d9d..37ffafc28 100755 --- a/scripts/tests.sh +++ b/scripts/tests.sh @@ -116,7 +116,7 @@ do log=--logger=JUNIT,test_suite,$log_directory/noopt_$vm.xml $testargs_no_opt fi fi - "$REPO_ROOT"/build/test/soltest $progress $log -- "$optimize" --evm-version "$vm" --ipcpath /tmp/test/geth.ipc + "$REPO_ROOT"/build/test/soltest $progress $log -- --testpath "$REPO_ROOT"/test "$optimize" --evm-version "$vm" --ipcpath /tmp/test/geth.ipc done done diff --git a/test/TestHelper.cpp b/test/TestHelper.cpp index e0d4423dc..77fa204fb 100644 --- a/test/TestHelper.cpp +++ b/test/TestHelper.cpp @@ -43,6 +43,11 @@ Options::Options() ipcPath = suite.argv[i + 1]; i++; } + else if (string(suite.argv[i]) == "--testpath" && i + 1 < suite.argc) + { + testPath = suite.argv[i + 1]; + i++; + } else if (string(suite.argv[i]) == "--optimize") optimize = true; else if (string(suite.argv[i]) == "--evm-version") @@ -60,6 +65,10 @@ Options::Options() if (!disableIPC && ipcPath.empty()) if (auto path = getenv("ETH_TEST_IPC")) ipcPath = path; + + if (testPath.empty()) + if (auto path = getenv("ETH_TEST_PATH")) + testPath = path; } dev::solidity::EVMVersion Options::evmVersion() const diff --git a/test/TestHelper.h b/test/TestHelper.h index 8c2eec362..9c61be3b9 100644 --- a/test/TestHelper.h +++ b/test/TestHelper.h @@ -35,6 +35,7 @@ namespace test struct Options: boost::noncopyable { std::string ipcPath; + std::string testPath; bool showMessages = false; bool optimize = false; bool disableIPC = false; diff --git a/test/boostTest.cpp b/test/boostTest.cpp index a3cc51c5b..fa9df3fd6 100644 --- a/test/boostTest.cpp +++ b/test/boostTest.cpp @@ -36,6 +36,7 @@ #pragma GCC diagnostic pop #include +#include using namespace boost::unit_test; @@ -54,6 +55,7 @@ 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::solidity::test::SyntaxTester::registerTests(); if (dev::test::Options::get().disableIPC) { for (auto suite: { diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index 997b610ea..1f76c01b5 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -43,27 +43,6 @@ namespace test BOOST_FIXTURE_TEST_SUITE(SolidityNameAndTypeResolution, AnalysisFramework) -BOOST_AUTO_TEST_CASE(smoke_test) -{ - char const* text = R"( - contract test { - uint256 stateVariable1; - function fun(uint256 arg1) public { uint256 y; y = arg1; } - } - )"; - CHECK_SUCCESS(text); -} - -BOOST_AUTO_TEST_CASE(double_stateVariable_declaration) -{ - char const* text = R"( - contract test { - uint256 variable; - uint128 variable; - } - )"; - CHECK_ERROR(text, DeclarationError, "Identifier already declared."); -} BOOST_AUTO_TEST_CASE(double_function_declaration) { @@ -76,37 +55,6 @@ BOOST_AUTO_TEST_CASE(double_function_declaration) CHECK_ERROR(text, DeclarationError, "Function with same name and arguments defined twice."); } -BOOST_AUTO_TEST_CASE(double_variable_declaration) -{ - string text = R"( - contract test { - function f() pure public { - uint256 x; - if (true) { uint256 x; } - } - } - )"; - CHECK_ERROR(text, DeclarationError, "Identifier already declared"); -} - -BOOST_AUTO_TEST_CASE(double_variable_declaration_050) -{ - string text = R"( - pragma experimental "v0.5.0"; - contract test { - function f() pure public { - uint256 x; - if (true) { uint256 x; } - } - } - )"; - CHECK_WARNING_ALLOW_MULTI(text, (vector{ - "This declaration shadows an existing declaration.", - "Unused local variable", - "Unused local variable" - })); -} - BOOST_AUTO_TEST_CASE(double_variable_declaration_disjoint_scope) { string text = R"( diff --git a/test/libsolidity/SyntaxTestParser.cpp b/test/libsolidity/SyntaxTestParser.cpp new file mode 100644 index 000000000..c37caca53 --- /dev/null +++ b/test/libsolidity/SyntaxTestParser.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 . +*/ + +#include +#include +#include +#include +#include +#include + +using namespace dev; +using namespace solidity; +using namespace dev::solidity::test; +using namespace std; + +template +void skipWhitespace(IteratorType& it, IteratorType end) +{ + while (it != end && isspace(*it)) + ++it; +} + +template +void skipSlashes(IteratorType& it, IteratorType end) +{ + while (it != end && *it == '/') + ++it; +} + +std::string SyntaxTestParser::parseSource(std::istream& _stream) +{ + std::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; +} + +std::vector SyntaxTestParser::parseExpectations(std::istream& _stream) +{ + std::vector expectations; + std::string line; + while (getline(_stream, line)) + { + auto it = line.begin(); + + skipSlashes(it, line.end()); + skipWhitespace(it, line.end()); + + if (it == line.end()) continue; + + auto typeBegin = it; + while (it != line.end() && *it != ':') + ++it; + string errorType(typeBegin, it); + + // skip colon + if (it != line.end()) it++; + + skipWhitespace(it, line.end()); + + string errorMessage(it, line.end()); + expectations.emplace_back(SyntaxTestExpectation{move(errorType), move(errorMessage)}); + } + return expectations; +} + +SyntaxTest SyntaxTestParser::parse(string const& _filename) +{ + ifstream file(_filename); + if (!file) + BOOST_THROW_EXCEPTION(runtime_error("cannot open test contract: \"" + _filename + "\"")); + file.exceptions(ios::badbit); + + SyntaxTest result; + result.source = parseSource(file); + result.expectations = parseExpectations(file); + return result; +} diff --git a/test/libsolidity/SyntaxTestParser.h b/test/libsolidity/SyntaxTestParser.h new file mode 100644 index 000000000..9e295a0b1 --- /dev/null +++ b/test/libsolidity/SyntaxTestParser.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 . +*/ + +#pragma once + +#include +#include +#include +#include + +namespace dev +{ +namespace solidity +{ +namespace test +{ + +struct SyntaxTestExpectation +{ + std::string type; + std::string message; +}; + +struct SyntaxTest +{ + std::string source; + std::vector expectations; +}; + +class SyntaxTestParser +{ +public: + SyntaxTestParser() = default; + + SyntaxTest parse(std::string const& _filename); +private: + std::string parseSource(std::istream& _stream); + std::vector parseExpectations(std::istream& _stream); +}; + +} +} +} diff --git a/test/libsolidity/SyntaxTester.cpp b/test/libsolidity/SyntaxTester.cpp new file mode 100644 index 000000000..4e5457604 --- /dev/null +++ b/test/libsolidity/SyntaxTester.cpp @@ -0,0 +1,134 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ + +#include +#include +#include +#include + +using namespace dev; +using namespace solidity; +using namespace dev::solidity::test; +using namespace std; +using namespace boost::unit_test; +namespace fs = boost::filesystem; + +void SyntaxTester::runTest(SyntaxTest const& _test) +{ + vector unexpectedErrors; + auto expectations = _test.expectations; + auto errorList = parseAnalyseAndReturnError(_test.source, true, true, true).second; + + bool errorsMatch = true; + + if (errorList.size() != expectations.size()) + errorsMatch = false; + else + { + for (size_t i = 0; i < errorList.size(); i++) + { + if ( + !(errorList[i]->typeName() == expectations[i].type) || + !(errorMessage(*errorList[i]) == expectations[i].message) + ) + { + errorsMatch = false; + break; + } + } + } + + if (!errorsMatch) + { + string msg = "Test expectation mismatch.\nExpected result:\n"; + if (expectations.empty()) + msg += "\tSuccess\n"; + else + for (auto const& expectation: expectations) + msg += "\t" + expectation.type + ": " + expectation.message + "\n"; + msg += "Obtained result:\n"; + if (errorList.empty()) + msg += "\tSuccess\n"; + else + for (auto const& error: errorList) + msg += "\t" + error->typeName() + ": " + errorMessage(*error) + "\n"; + BOOST_ERROR(msg); + } +} + +std::string SyntaxTester::errorMessage(Error const& _e) +{ + if (_e.comment()) + return boost::replace_all_copy(*_e.comment(), "\n", "\\n"); + else + return "NONE"; +} + +int SyntaxTester::registerTests( + test_suite& _suite, + fs::path const& _basepath, + fs::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(fullpath), + fs::directory_iterator() + )) + numTestsAdded += registerTests(*sub_suite, _basepath, _path / entry.path().filename()); + _suite.add(sub_suite); + } + else + { + _suite.add(make_test_case( + [fullpath] { SyntaxTester().runTest(SyntaxTestParser().parse(fullpath.string())); }, + _path.stem().string(), + _path.string(), + 0 + )); + numTestsAdded = 1; + } + return numTestsAdded; +} + +void SyntaxTester::registerTests() +{ + if(dev::test::Options::get().testPath.empty()) + throw runtime_error( + "No path to the test files was specified. " + "Use the --testpath command line option or " + "the ETH_TEST_PATH environment variable." + ); + auto testPath = fs::path(dev::test::Options::get().testPath); + + if (fs::exists(testPath) && fs::is_directory(testPath)) + { + int numTestsAdded = registerTests( + framework::master_test_suite(), + testPath / "libsolidity", + "syntaxTests" + ); + solAssert(numTestsAdded > 0, "no syntax tests found in libsolidity/syntaxTests"); + } + else + solAssert(false, "libsolidity/syntaxTests directory not found"); +} diff --git a/test/libsolidity/SyntaxTester.h b/test/libsolidity/SyntaxTester.h new file mode 100644 index 000000000..d61668aa2 --- /dev/null +++ b/test/libsolidity/SyntaxTester.h @@ -0,0 +1,48 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ + +#pragma once + +#include +#include +#include +#include + +namespace dev +{ +namespace solidity +{ +namespace test +{ + +class SyntaxTester: public AnalysisFramework +{ +public: + static void registerTests(); +private: + static int registerTests( + boost::unit_test::test_suite& _suite, + boost::filesystem::path const& _basepath, + boost::filesystem::path const& _path + ); + static std::string errorMessage(Error const& _e); + void runTest(SyntaxTest const& _test); +}; + +} +} +} diff --git a/test/libsolidity/syntaxTests/double_stateVariable_declaration.sol b/test/libsolidity/syntaxTests/double_stateVariable_declaration.sol new file mode 100644 index 000000000..c5507b649 --- /dev/null +++ b/test/libsolidity/syntaxTests/double_stateVariable_declaration.sol @@ -0,0 +1,6 @@ +contract test { + uint256 variable; + uint128 variable; +} +// ---- +// DeclarationError: Identifier already declared. diff --git a/test/libsolidity/syntaxTests/double_variable_declaration.sol b/test/libsolidity/syntaxTests/double_variable_declaration.sol new file mode 100644 index 000000000..3349cfece --- /dev/null +++ b/test/libsolidity/syntaxTests/double_variable_declaration.sol @@ -0,0 +1,8 @@ +contract test { + function f() pure public { + uint256 x; + if (true) { uint256 x; } + } +} +// ---- +// DeclarationError: Identifier already declared. diff --git a/test/libsolidity/syntaxTests/double_variable_declaration_050.sol b/test/libsolidity/syntaxTests/double_variable_declaration_050.sol new file mode 100644 index 000000000..9c2d40d52 --- /dev/null +++ b/test/libsolidity/syntaxTests/double_variable_declaration_050.sol @@ -0,0 +1,11 @@ +pragma experimental "v0.5.0"; +contract test { + function f() pure public { + uint256 x; + if (true) { uint256 x; } + } +} +// ---- +// Warning: This declaration shadows an existing declaration. +// Warning: Unused local variable. +// Warning: Unused local variable. diff --git a/test/libsolidity/syntaxTests/smoke_test.sol b/test/libsolidity/syntaxTests/smoke_test.sol new file mode 100644 index 000000000..2d48098a1 --- /dev/null +++ b/test/libsolidity/syntaxTests/smoke_test.sol @@ -0,0 +1,6 @@ +contract test { + uint256 stateVariable1; + function fun(uint256 arg1) public { uint256 y; y = arg1; } +} +// ---- +// Warning: Function state mutability can be restricted to pure From 317c1f7fa36a24259a3c678fad255406df9da64d Mon Sep 17 00:00:00 2001 From: Daniel Kirchner Date: Fri, 9 Mar 2018 11:03:16 +0100 Subject: [PATCH 015/197] Workaround for boost < 1.59.0 --- test/libsolidity/SyntaxTester.cpp | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/test/libsolidity/SyntaxTester.cpp b/test/libsolidity/SyntaxTester.cpp index 4e5457604..886bc3bfe 100644 --- a/test/libsolidity/SyntaxTester.cpp +++ b/test/libsolidity/SyntaxTester.cpp @@ -27,6 +27,19 @@ using namespace std; using namespace boost::unit_test; namespace fs = boost::filesystem; +#if BOOST_VERSION < 105900 +test_case *make_test_case( + function const& _fn, + string const& _name, + string const&, // _filename + size_t // _line +) +{ + return make_test_case(_fn, _name); +} +#endif + + void SyntaxTester::runTest(SyntaxTest const& _test) { vector unexpectedErrors; From 3232561d979954f0625102d33cf042fc5eda7211 Mon Sep 17 00:00:00 2001 From: Daniel Kirchner Date: Mon, 12 Mar 2018 13:33:37 +0100 Subject: [PATCH 016/197] Refactoring; fuse SyntaxTestParser and SyntaxTester to SyntaxTest. --- docs/contributing.rst | 15 +- test/TestHelper.h | 2 +- test/boostTest.cpp | 8 +- test/libsolidity/SyntaxTest.cpp | 205 ++++++++++++++++++ .../{SyntaxTester.h => SyntaxTest.h} | 41 +++- test/libsolidity/SyntaxTestParser.cpp | 97 --------- test/libsolidity/SyntaxTestParser.h | 57 ----- test/libsolidity/SyntaxTester.cpp | 147 ------------- 8 files changed, 258 insertions(+), 314 deletions(-) create mode 100644 test/libsolidity/SyntaxTest.cpp rename test/libsolidity/{SyntaxTester.h => SyntaxTest.h} (54%) delete mode 100644 test/libsolidity/SyntaxTestParser.cpp delete mode 100644 test/libsolidity/SyntaxTestParser.h delete mode 100644 test/libsolidity/SyntaxTester.cpp diff --git a/docs/contributing.rst b/docs/contributing.rst index a5efba8bf..8b4695e40 100644 --- a/docs/contributing.rst +++ b/docs/contributing.rst @@ -69,15 +69,22 @@ 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, some others require ``libz3`` to be installed. -To disable the z3 tests, use ``./build/test/soltest -- --no-smt`` and -to run a subset of the tests that do not require ``cpp-ethereum``, use ``./build/test/soltest -- --no-ipc``. +``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 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``. For all other tests, you need to install `cpp-ethereum `_ and run it in testing mode: ``eth --test -d /tmp/testeth``. -Then you run the actual tests: ``./build/test/soltest -- --ipcpath /tmp/testeth/geth.ipc``. +Then you run the actual tests: ``./build/test/soltest -- --ipcpath /tmp/testeth/geth.ipc --testpath ./test``. To run a subset of tests, filters can be used: -``soltest -t TestSuite/TestName -- --ipcpath /tmp/testeth/geth.ipc``, where ``TestName`` can be a wildcard ``*``. +``soltest -t TestSuite/TestName -- --ipcpath /tmp/testeth/geth.ipc --testpath ./test``, +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). diff --git a/test/TestHelper.h b/test/TestHelper.h index 9c61be3b9..f7b1d94c8 100644 --- a/test/TestHelper.h +++ b/test/TestHelper.h @@ -35,7 +35,7 @@ namespace test struct Options: boost::noncopyable { std::string ipcPath; - std::string testPath; + boost::filesystem::path testPath; bool showMessages = false; bool optimize = false; bool disableIPC = false; diff --git a/test/boostTest.cpp b/test/boostTest.cpp index fa9df3fd6..e557ff95a 100644 --- a/test/boostTest.cpp +++ b/test/boostTest.cpp @@ -36,7 +36,7 @@ #pragma GCC diagnostic pop #include -#include +#include using namespace boost::unit_test; @@ -55,7 +55,11 @@ 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::solidity::test::SyntaxTester::registerTests(); + solAssert(dev::solidity::test::SyntaxTest::registerTests( + master, + dev::test::Options::get().testPath / "libsolidity", + "syntaxTests" + ) > 0, "no syntax tests found"); if (dev::test::Options::get().disableIPC) { for (auto suite: { diff --git a/test/libsolidity/SyntaxTest.cpp b/test/libsolidity/SyntaxTest.cpp new file mode 100644 index 000000000..8cbe2f36c --- /dev/null +++ b/test/libsolidity/SyntaxTest.cpp @@ -0,0 +1,205 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ + +#include +#include +#include +#include +#include +#include +#include + +using namespace dev; +using namespace solidity; +using namespace dev::solidity::test; +using namespace std; +namespace fs = boost::filesystem; +using namespace boost::unit_test; + +template +void skipWhitespace(IteratorType& it, IteratorType end) +{ + while (it != end && isspace(*it)) + ++it; +} + +template +void skipSlashes(IteratorType& it, IteratorType end) +{ + while (it != end && *it == '/') + ++it; +} + +SyntaxTest::SyntaxTest(string const& _filename) +{ + ifstream file(_filename); + if (!file) + BOOST_THROW_EXCEPTION(runtime_error("cannot open test contract: \"" + _filename + "\"")); + file.exceptions(ios::badbit); + + m_source = parseSource(file); + m_expectations = parseExpectations(file); +} + +bool SyntaxTest::run(ostream& _stream, string const& _indent) +{ + m_errorList = parseAnalyseAndReturnError(m_source, true, true, true).second; + if (!matchesExpectations(m_errorList)) + { + std::string nextIndentLevel = _indent.empty() ? "\t" : _indent + _indent; + _stream << _indent << "Expected result:" << endl; + printExpected(_stream, nextIndentLevel); + _stream << _indent << "Obtained result:\n"; + printErrorList(_stream, m_errorList, nextIndentLevel); + return false; + } + return true; +} + +void SyntaxTest::printExpected(ostream& _stream, string const& _indent) const +{ + if (m_expectations.empty()) + _stream << _indent << "Success" << endl; + else + for (auto const& expectation: m_expectations) + _stream << _indent << expectation.type << ": " << expectation.message << endl; +} + +void SyntaxTest::printErrorList( + ostream& _stream, + ErrorList const& _errorList, + string const& _indent +) const +{ + if (_errorList.empty()) + _stream << _indent << "Success" << endl; + else + for (auto const& error: _errorList) + _stream << _indent << error->typeName() << ": " << errorMessage(*error) << endl; +} + +bool SyntaxTest::matchesExpectations(ErrorList const& _errorList) const +{ + if (_errorList.size() != m_expectations.size()) + return false; + else + for (size_t i = 0; i < _errorList.size(); i++) + if ( + !(_errorList[i]->typeName() == m_expectations[i].type) || + !(errorMessage(*_errorList[i]) == m_expectations[i].message) + ) + return false; + return true; +} + +string SyntaxTest::errorMessage(Error const& _e) +{ + if (_e.comment()) + return boost::replace_all_copy(*_e.comment(), "\n", "\\n"); + else + 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 SyntaxTest::parseExpectations(istream& _stream) +{ + vector expectations; + string line; + while (getline(_stream, line)) + { + auto it = line.begin(); + + skipSlashes(it, line.end()); + skipWhitespace(it, line.end()); + + if (it == line.end()) continue; + + auto typeBegin = it; + while (it != line.end() && *it != ':') + ++it; + string errorType(typeBegin, it); + + // skip colon + if (it != line.end()) it++; + + skipWhitespace(it, line.end()); + + string errorMessage(it, line.end()); + expectations.emplace_back(SyntaxTestExpectation{move(errorType), move(errorMessage)}); + } + return expectations; +} + +#if BOOST_VERSION < 105900 +test_case *make_test_case( + function const& _fn, + string const& _name, + string const& /* _filename */, + size_t /* _line */ +) +{ + return make_test_case(_fn, _name); +} +#endif + +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(fullpath), + fs::directory_iterator() + )) + numTestsAdded += registerTests(*sub_suite, _basepath, _path / entry.path().filename()); + _suite.add(sub_suite); + } + else + { + _suite.add(make_test_case( + [fullpath] + { + std::stringstream errorStream; + if (!SyntaxTest(fullpath.string()).run(errorStream, "")) + BOOST_ERROR("Test expectation mismatch.\n" + errorStream.str()); + }, + _path.stem().string(), + _path.string(), + 0 + )); + numTestsAdded = 1; + } + return numTestsAdded; +} diff --git a/test/libsolidity/SyntaxTester.h b/test/libsolidity/SyntaxTest.h similarity index 54% rename from test/libsolidity/SyntaxTester.h rename to test/libsolidity/SyntaxTest.h index d61668aa2..4379c77bc 100644 --- a/test/libsolidity/SyntaxTester.h +++ b/test/libsolidity/SyntaxTest.h @@ -18,10 +18,16 @@ #pragma once #include -#include -#include +#include + +#include #include +#include +#include +#include +#include + namespace dev { namespace solidity @@ -29,18 +35,41 @@ namespace solidity namespace test { -class SyntaxTester: public AnalysisFramework +struct SyntaxTestExpectation +{ + std::string type; + std::string message; +}; + + +class SyntaxTest: AnalysisFramework { public: - static void registerTests(); -private: + SyntaxTest(std::string const& _filename); + + bool run(std::ostream& _stream, std::string const& _indent); + + void printExpected(std::ostream& _stream, std::string const& _indent) const; + void printErrorList( + std::ostream& _stream, + ErrorList const& _errors, + std::string const& _indent + ) const; + static int registerTests( boost::unit_test::test_suite& _suite, boost::filesystem::path const& _basepath, boost::filesystem::path const& _path ); +private: + bool matchesExpectations(ErrorList const& _errors) const; static std::string errorMessage(Error const& _e); - void runTest(SyntaxTest const& _test); + static std::string parseSource(std::istream& _stream); + static std::vector parseExpectations(std::istream& _stream); + + std::string m_source; + std::vector m_expectations; + ErrorList m_errorList; }; } diff --git a/test/libsolidity/SyntaxTestParser.cpp b/test/libsolidity/SyntaxTestParser.cpp deleted file mode 100644 index c37caca53..000000000 --- a/test/libsolidity/SyntaxTestParser.cpp +++ /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 . -*/ - -#include -#include -#include -#include -#include -#include - -using namespace dev; -using namespace solidity; -using namespace dev::solidity::test; -using namespace std; - -template -void skipWhitespace(IteratorType& it, IteratorType end) -{ - while (it != end && isspace(*it)) - ++it; -} - -template -void skipSlashes(IteratorType& it, IteratorType end) -{ - while (it != end && *it == '/') - ++it; -} - -std::string SyntaxTestParser::parseSource(std::istream& _stream) -{ - std::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; -} - -std::vector SyntaxTestParser::parseExpectations(std::istream& _stream) -{ - std::vector expectations; - std::string line; - while (getline(_stream, line)) - { - auto it = line.begin(); - - skipSlashes(it, line.end()); - skipWhitespace(it, line.end()); - - if (it == line.end()) continue; - - auto typeBegin = it; - while (it != line.end() && *it != ':') - ++it; - string errorType(typeBegin, it); - - // skip colon - if (it != line.end()) it++; - - skipWhitespace(it, line.end()); - - string errorMessage(it, line.end()); - expectations.emplace_back(SyntaxTestExpectation{move(errorType), move(errorMessage)}); - } - return expectations; -} - -SyntaxTest SyntaxTestParser::parse(string const& _filename) -{ - ifstream file(_filename); - if (!file) - BOOST_THROW_EXCEPTION(runtime_error("cannot open test contract: \"" + _filename + "\"")); - file.exceptions(ios::badbit); - - SyntaxTest result; - result.source = parseSource(file); - result.expectations = parseExpectations(file); - return result; -} diff --git a/test/libsolidity/SyntaxTestParser.h b/test/libsolidity/SyntaxTestParser.h deleted file mode 100644 index 9e295a0b1..000000000 --- a/test/libsolidity/SyntaxTestParser.h +++ /dev/null @@ -1,57 +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 . -*/ - -#pragma once - -#include -#include -#include -#include - -namespace dev -{ -namespace solidity -{ -namespace test -{ - -struct SyntaxTestExpectation -{ - std::string type; - std::string message; -}; - -struct SyntaxTest -{ - std::string source; - std::vector expectations; -}; - -class SyntaxTestParser -{ -public: - SyntaxTestParser() = default; - - SyntaxTest parse(std::string const& _filename); -private: - std::string parseSource(std::istream& _stream); - std::vector parseExpectations(std::istream& _stream); -}; - -} -} -} diff --git a/test/libsolidity/SyntaxTester.cpp b/test/libsolidity/SyntaxTester.cpp deleted file mode 100644 index 886bc3bfe..000000000 --- a/test/libsolidity/SyntaxTester.cpp +++ /dev/null @@ -1,147 +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 . -*/ - -#include -#include -#include -#include - -using namespace dev; -using namespace solidity; -using namespace dev::solidity::test; -using namespace std; -using namespace boost::unit_test; -namespace fs = boost::filesystem; - -#if BOOST_VERSION < 105900 -test_case *make_test_case( - function const& _fn, - string const& _name, - string const&, // _filename - size_t // _line -) -{ - return make_test_case(_fn, _name); -} -#endif - - -void SyntaxTester::runTest(SyntaxTest const& _test) -{ - vector unexpectedErrors; - auto expectations = _test.expectations; - auto errorList = parseAnalyseAndReturnError(_test.source, true, true, true).second; - - bool errorsMatch = true; - - if (errorList.size() != expectations.size()) - errorsMatch = false; - else - { - for (size_t i = 0; i < errorList.size(); i++) - { - if ( - !(errorList[i]->typeName() == expectations[i].type) || - !(errorMessage(*errorList[i]) == expectations[i].message) - ) - { - errorsMatch = false; - break; - } - } - } - - if (!errorsMatch) - { - string msg = "Test expectation mismatch.\nExpected result:\n"; - if (expectations.empty()) - msg += "\tSuccess\n"; - else - for (auto const& expectation: expectations) - msg += "\t" + expectation.type + ": " + expectation.message + "\n"; - msg += "Obtained result:\n"; - if (errorList.empty()) - msg += "\tSuccess\n"; - else - for (auto const& error: errorList) - msg += "\t" + error->typeName() + ": " + errorMessage(*error) + "\n"; - BOOST_ERROR(msg); - } -} - -std::string SyntaxTester::errorMessage(Error const& _e) -{ - if (_e.comment()) - return boost::replace_all_copy(*_e.comment(), "\n", "\\n"); - else - return "NONE"; -} - -int SyntaxTester::registerTests( - test_suite& _suite, - fs::path const& _basepath, - fs::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(fullpath), - fs::directory_iterator() - )) - numTestsAdded += registerTests(*sub_suite, _basepath, _path / entry.path().filename()); - _suite.add(sub_suite); - } - else - { - _suite.add(make_test_case( - [fullpath] { SyntaxTester().runTest(SyntaxTestParser().parse(fullpath.string())); }, - _path.stem().string(), - _path.string(), - 0 - )); - numTestsAdded = 1; - } - return numTestsAdded; -} - -void SyntaxTester::registerTests() -{ - if(dev::test::Options::get().testPath.empty()) - throw runtime_error( - "No path to the test files was specified. " - "Use the --testpath command line option or " - "the ETH_TEST_PATH environment variable." - ); - auto testPath = fs::path(dev::test::Options::get().testPath); - - if (fs::exists(testPath) && fs::is_directory(testPath)) - { - int numTestsAdded = registerTests( - framework::master_test_suite(), - testPath / "libsolidity", - "syntaxTests" - ); - solAssert(numTestsAdded > 0, "no syntax tests found in libsolidity/syntaxTests"); - } - else - solAssert(false, "libsolidity/syntaxTests directory not found"); -} From 7091b6c8b5a91a13ff02255cc0bca08266527e4f Mon Sep 17 00:00:00 2001 From: Daniel Kirchner Date: Tue, 13 Mar 2018 12:30:56 +0100 Subject: [PATCH 017/197] Minor adjustments. --- test/libsolidity/SyntaxTest.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/libsolidity/SyntaxTest.cpp b/test/libsolidity/SyntaxTest.cpp index 8cbe2f36c..f1c60458a 100644 --- a/test/libsolidity/SyntaxTest.cpp +++ b/test/libsolidity/SyntaxTest.cpp @@ -48,7 +48,7 @@ SyntaxTest::SyntaxTest(string const& _filename) { ifstream file(_filename); if (!file) - BOOST_THROW_EXCEPTION(runtime_error("cannot open test contract: \"" + _filename + "\"")); + BOOST_THROW_EXCEPTION(runtime_error("Cannot open test contract: \"" + _filename + "\".")); file.exceptions(ios::badbit); m_source = parseSource(file); @@ -60,7 +60,7 @@ bool SyntaxTest::run(ostream& _stream, string const& _indent) m_errorList = parseAnalyseAndReturnError(m_source, true, true, true).second; if (!matchesExpectations(m_errorList)) { - std::string nextIndentLevel = _indent.empty() ? "\t" : _indent + _indent; + std::string nextIndentLevel = _indent + "\t"; _stream << _indent << "Expected result:" << endl; printExpected(_stream, nextIndentLevel); _stream << _indent << "Obtained result:\n"; @@ -99,8 +99,8 @@ bool SyntaxTest::matchesExpectations(ErrorList const& _errorList) const else for (size_t i = 0; i < _errorList.size(); i++) if ( - !(_errorList[i]->typeName() == m_expectations[i].type) || - !(errorMessage(*_errorList[i]) == m_expectations[i].message) + (_errorList[i]->typeName() != m_expectations[i].type) || + (errorMessage(*_errorList[i]) != m_expectations[i].message) ) return false; return true; From f56afa21c4c30be51541d99027b96058227da6c4 Mon Sep 17 00:00:00 2001 From: chriseth Date: Tue, 13 Mar 2018 12:42:21 +0100 Subject: [PATCH 018/197] Update CODING_STYLE.md --- CODING_STYLE.md | 138 +++++++++++++++++++++++++----------------------- 1 file changed, 73 insertions(+), 65 deletions(-) diff --git a/CODING_STYLE.md b/CODING_STYLE.md index ee924e726..2cc9ac709 100644 --- a/CODING_STYLE.md +++ b/CODING_STYLE.md @@ -3,20 +3,22 @@ GOLDEN RULE: Follow the style of the existing code when you make changes. a. Use tabs for leading indentation -- tab stops are every 4 characters. +- tab stops are every 4 characters (only relevant for line length). - One indentation level -> exactly one byte (i.e. a tab character) in the source file. -- If you have run-on lines, indent as you would for a block. b. Line widths: -- Don't worry about having lines of code > 80-char wide. +- Lines should be at most 99 characters wide to make diff views readable and reduce merge conflicts. - Lines of comments should be formatted according to ease of viewing, but simplicity is to be preferred over beauty. -c. Don't use braces for condition-body one-liners. +c. Single-statement blocks should not have braces, unless required for clarity. d. Never place condition bodies on same line as condition. -e. Space between first paren and keyword, but *not* following first paren or preceding final paren. -f. No spaces when fewer than intra-expression three parens together; when three or more, space according to clarity. -g. No spaces for subscripting or unary operators. -h. No space before ':' but one after it, except in the ternary operator: one on both sides. -i. Space all other operators. -j. Braces, when used, always have their own lines and are at same indentation level as "parent" scope. +e. Space between keyword and opening parenthesis, but not following opening parenthesis or before final parenthesis. +f. No spaces for unary operators, `->` or `.`. +g. No space before ':' but one after it, except in the ternary operator: one on both sides. +h. Add spaces around all other operators. +i. Braces, when used, always have their own lines and are at same indentation level as "parent" scope. +j. If lines are broken, a list of elements enclosed with parentheses (of any kind) and separated by a + separator (of any kind) are formatted such that there is exactly one element per line, followed by + the separator, the opening parenthesis is on the first line, followed by a line break and the closing + parenthesis is on a line of its own (unindented). See example below. (WRONG) if( a==b[ i ] ) { printf ("Hello\n"); } @@ -25,6 +27,7 @@ foo->bar(someLongVariableName, anotherLongVariableName, anotherLongVariableName, anotherLongVariableName); +cout << "some very long string that contains completely irrelevant text that talks about this and that and contains the words \"lorem\" and \"ipsum\"" << endl; (RIGHT) if (a == b[i]) @@ -36,6 +39,11 @@ foo->bar( anotherLongVariableName, anotherLongVariableName ); +cout << + "some very long string that contains completely irrelevant " << + "text that talks about this and that and contains the words " << + "\"lorem\" and \"ipsum\"" << + endl; @@ -43,7 +51,8 @@ foo->bar( a. No "using namespace" declarations in header files. b. All symbols should be declared in a namespace except for final applications. -c. Preprocessor symbols should be prefixed with the namespace in all-caps and an underscore. +c. Use anonymous namespaces for helpers whose scope is a cpp file only. +d. Preprocessor symbols should be prefixed with the namespace in all-caps and an underscore. (WRONG) #include @@ -90,16 +99,16 @@ All other entities' first alpha is lower case. a. Leading underscore "_" to parameter names. - Exception: "o_parameterName" when it is used exclusively for output. See 6(f). - Exception: "io_parameterName" when it is used for both input and output. See 6(f). -b. Leading "c_" to const variables (unless part of an external API). -c. Leading "g_" to global (non-const) variables. -d. Leading "s_" to static (non-const, non-global) variables. +b. Leading "g_" to global (non-const) variables. +c. Leading "s_" to static (non-const, non-global) variables. -5. Error reporting: - -- Prefer exception to bool/int return type. +5. Assertions: +- use `solAssert` and `solUnimplementedAssert` generously to check assumptions + that span across different parts of the code base, for example before dereferencing + a pointer. 6. Declarations: @@ -108,15 +117,14 @@ a. {Typename} + {qualifiers} + {name}. b. Only one per line. c. Associate */& with type, not variable (at ends with parser, but more readable, and safe if in conjunction with (b)). d. Favour declarations close to use; don't habitually declare at top of scope ala C. -e. Always pass non-trivial parameters with a const& suffix. -f. If a function returns multiple values, use std::tuple (std::pair acceptable). Prefer not using */& arguments, except where efficiency requires. -g. Never use a macro where adequate non-preprocessor C++ can be written. -h. Make use of auto whenever type is clear or unimportant: -- Always avoid doubly-stating the type. -- Use to avoid vast and unimportant type declarations. -- However, avoid using auto where type is not immediately obvious from the context, and especially not for arithmetic expressions. -i. Don't pass bools: prefer enumerations instead. -j. Prefer enum class to straight enum. +e. Pass non-trivial parameters as const reference, unless the data is to be copied into the function, then either pass by const reference or by value and use std::move. +f. If a function returns multiple values, use std::tuple (std::pair acceptable) or better introduce a struct type. Do not use */& arguments. +g. Use parameters of pointer type only if ``nullptr`` is a valid argument, use references otherwise. Often, ``boost::optional`` is better suited than a raw pointer. +h. Never use a macro where adequate non-preprocessor C++ can be written. +i. Only use ``auto`` if the type is very long and rather irrelevant. +j. Do not pass bools: prefer enumerations instead. +k. Prefer enum class to straight enum. +l. Always initialize POD variables, even if their value is overwritten later. (WRONG) @@ -134,13 +142,18 @@ enum class Accuracy Approximate, Exact }; +struct MeanSigma +{ + float mean; + float standardDeviation; +}; double const d = 0; int i; int j; char* s; -std::tuple meanAndSigma(std::vector const& _v, Accuracy _a); -auto x = dynamic_cast(base); -for (auto i = x.begin(); i != x.end(); ++i) {} +MeanAndSigma ms meanAndSigma(std::vector const& _v, Accuracy _a); +Derived* x = dynamic_cast(base); +for (auto i = x->begin(); i != x->end(); ++i) {} 7. Structs & classes @@ -169,17 +182,10 @@ f. For a property 'foo' 9. Naming -a. Collection conventions: -- -s means std::vector e.g. using MyTypes = std::vector -- -Set means std::set e.g. using MyTypeSet = std::set -- -Hash means std::unordered_set e.g. using MyTypeHash = std::unordered_set -b. Class conventions: -- -Face means the interface of some shared concept. (e.g. FooFace might be a pure virtual class.) -c. Avoid unpronouncable names; -- If you need to shorten a name favour a pronouncable slice of the original to a scattered set of consonants. -- e.g. Manager shortens to Man rather than Mgr. -d. Avoid prefixes of initials (e.g. DON'T use IMyInterface, CMyImplementation) -e. Find short, memorable & (at least semi-) descriptive names for commonly used classes or name-fragments. +a. Avoid unpronouncable names +b. Names should be shortened only if they are extremely common, but shortening should be generally avoided +c. Avoid prefixes of initials (e.g. do not use IMyInterface, CMyImplementation) +c. Find short, memorable & (at least semi-) descriptive names for commonly used classes or name-fragments. - A dictionary and thesaurus are your friends. - Spell correctly. - Think carefully about the class's purpose. @@ -194,8 +200,9 @@ a. Prefer 'using' to 'typedef'. e.g. using ints = std::vector; rather than b. Generally avoid shortening a standard form that already includes all important information: - e.g. stick to shared_ptr rather than shortening to ptr. c. 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; ///< Guard is used throughout the codebase since it's clear in meaning and used commonly. +- e.g. using Guard = std::lock_guard; ///< Guard is used throughout the codebase since it is clear in meaning and used commonly. d. In general expressions should be roughly as important/semantically meaningful as the space they occupy. +e. 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. @@ -207,40 +214,41 @@ b. Document the interface, not the implementation. - Comment in terms of the method properties and intended alteration to class state (or what aspects of the state it reports). - Be careful to scrutinise documentation that extends only to intended purpose and usage. - Reject documentation that is simply an English transaction of the implementation. - +c. Avoid in-code comments. Instead, try to extract blocks of functionality into functions. This often already eliminates the need for an in-code comment. 12. Include Headers -Includes should go in increasing order of generality (libethereum -> libethcore -> libdevcrypto -> libdevcore -> boost -> STL). For example: +Includes should go in increasing order of generality (libsolidity -> libevmasm -> libdevcore -> boost -> STL). +The corresponding .h file should be the first include in the respective .cpp file. +Insert empty lines between blocks of include files. + +Example: + +``` +#include + +#include +#include +#include +#include + +#include -#include -#include -#include -#include -#include #include -#include -#include +#include + +#include +#include + +#include +#include +``` See http://stackoverflow.com/questions/614302/c-header-order/614333#614333 for the reason: this makes it easier to find missing includes in header files. - -13. Logging - -Logging should be performed at appropriate verbosities depending on the logging message. -The more likely a message is to repeat (and thus cause noise) the higher in verbosity it should be. -Some rules to keep in mind: - - - Verbosity == 0 -> Reserved for important stuff that users must see and can understand. - - Verbosity == 1 -> Reserved for stuff that users don't need to see but can understand. - - Verbosity >= 2 -> Anything that is or might be displayed more than once every minute - - Verbosity >= 3 -> Anything that only a developer would understand - - Verbosity >= 4 -> Anything that is low-level (e.g. peer disconnects, timers being cancelled) - - -14. Recommended reading +13. Recommended reading Herb Sutter and Bjarne Stroustrup - "C++ Core Guidelines" (https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md) From 255d2ce95dcdd61e49e1ab458d3e7293a114c6c9 Mon Sep 17 00:00:00 2001 From: Yosyp Schwab Date: Tue, 13 Mar 2018 10:07:10 -0400 Subject: [PATCH 019/197] Updated link for "try Solidity in your browser" Previous link leads to a page with a model warning: _The Remix IDE has moved to http://remix.ethereum.org. This instance of Remix you are visiting WILL NOT BE UPDATED. Please make a backup of your contracts and start using http://remix.ethereum.org_ Updated link to point to http://remix.ethereum.org --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index cb7437296..42c392e22 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ ## 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. -You can start using [Solidity in your browser](https://ethereum.github.io/browser-solidity/) with no need to download or compile anything. +You can start using [Solidity in your browser](http://remix.ethereum.org) with no need to download or compile anything. The changelog for this project can be found [here](https://github.com/ethereum/solidity/blob/develop/Changelog.md). From 069b150e42d12f3f3736dd4af2d82881db244b66 Mon Sep 17 00:00:00 2001 From: chriseth Date: Tue, 13 Mar 2018 16:50:44 +0100 Subject: [PATCH 020/197] Bugfix in virtual lookup for modifiers in libraries. --- libsolidity/codegen/CompilerContext.cpp | 14 +++++++++++--- libsolidity/codegen/CompilerContext.h | 2 +- libsolidity/codegen/ContractCompiler.cpp | 5 ++++- 3 files changed, 16 insertions(+), 5 deletions(-) diff --git a/libsolidity/codegen/CompilerContext.cpp b/libsolidity/codegen/CompilerContext.cpp index 0bf88267f..473330466 100644 --- a/libsolidity/codegen/CompilerContext.cpp +++ b/libsolidity/codegen/CompilerContext.cpp @@ -193,14 +193,22 @@ Declaration const* CompilerContext::nextFunctionToCompile() const return m_functionCompilationQueue.nextFunctionToCompile(); } -ModifierDefinition const& CompilerContext::functionModifier(string const& _name) const +ModifierDefinition const& CompilerContext::resolveVirtualFunctionModifier( + ModifierDefinition const& _modifier +) const { + // Libraries do not allow inheritance and their functions can be inlined, so we should not + // search the inheritance hierarchy (which will be the wrong one in case the function + // is inlined). + if (auto scope = dynamic_cast(_modifier.scope())) + if (scope->isLibrary()) + return _modifier; solAssert(!m_inheritanceHierarchy.empty(), "No inheritance hierarchy set."); for (ContractDefinition const* contract: m_inheritanceHierarchy) for (ModifierDefinition const* modifier: contract->functionModifiers()) - if (modifier->name() == _name) + if (modifier->name() == _modifier.name()) return *modifier; - solAssert(false, "Function modifier " + _name + " not found."); + solAssert(false, "Function modifier " + _modifier.name() + " not found in inheritance hierarchy."); } unsigned CompilerContext::baseStackOffsetOfVariable(Declaration const& _declaration) const diff --git a/libsolidity/codegen/CompilerContext.h b/libsolidity/codegen/CompilerContext.h index cf6266833..7b6632774 100644 --- a/libsolidity/codegen/CompilerContext.h +++ b/libsolidity/codegen/CompilerContext.h @@ -130,7 +130,7 @@ public: void appendMissingLowLevelFunctions(); ABIFunctions& abiFunctions() { return m_abiFunctions; } - ModifierDefinition const& functionModifier(std::string const& _name) const; + ModifierDefinition const& resolveVirtualFunctionModifier(ModifierDefinition const& _modifier) const; /// Returns the distance of the given local variable from the bottom of the stack (of the current function). unsigned baseStackOffsetOfVariable(Declaration const& _declaration) const; /// If supplied by a value returned by @ref baseStackOffsetOfVariable(variable), returns diff --git a/libsolidity/codegen/ContractCompiler.cpp b/libsolidity/codegen/ContractCompiler.cpp index 5a9498f0e..95d6c8b5c 100644 --- a/libsolidity/codegen/ContractCompiler.cpp +++ b/libsolidity/codegen/ContractCompiler.cpp @@ -1002,7 +1002,10 @@ void ContractCompiler::appendModifierOrFunctionCode() appendModifierOrFunctionCode(); else { - ModifierDefinition const& modifier = m_context.functionModifier(modifierInvocation->name()->name()); + ModifierDefinition const& nonVirtualModifier = dynamic_cast( + *modifierInvocation->name()->annotation().referencedDeclaration + ); + ModifierDefinition const& modifier = m_context.resolveVirtualFunctionModifier(nonVirtualModifier); CompilerContext::LocationSetter locationSetter(m_context, modifier); solAssert(modifier.parameters().size() == modifierInvocation->arguments().size(), ""); for (unsigned i = 0; i < modifier.parameters().size(); ++i) From 58af150c3dce26206825122d048e4e0732fcb50f Mon Sep 17 00:00:00 2001 From: chriseth Date: Tue, 13 Mar 2018 16:50:27 +0100 Subject: [PATCH 021/197] Changelog entry. --- Changelog.md | 1 + .../virtualLookup/modifiers_in_libraries.sol | 14 ++++++++++++++ 2 files changed, 15 insertions(+) create mode 100644 test/libsolidity/syntaxTests/virtualLookup/modifiers_in_libraries.sol diff --git a/Changelog.md b/Changelog.md index 648af66c6..2e950f1d7 100644 --- a/Changelog.md +++ b/Changelog.md @@ -4,6 +4,7 @@ Features: Bugfixes: * Code Generator: Properly skip unneeded storgae array cleanup when not reducing length. + * Code Generator: Bugfix in modifier lookup in libraries. * Commandline interface: Support ``--evm-version constantinople`` properly. * Standard JSON: Support ``constantinople`` as ``evmVersion`` properly. diff --git a/test/libsolidity/syntaxTests/virtualLookup/modifiers_in_libraries.sol b/test/libsolidity/syntaxTests/virtualLookup/modifiers_in_libraries.sol new file mode 100644 index 000000000..b033fd0c7 --- /dev/null +++ b/test/libsolidity/syntaxTests/virtualLookup/modifiers_in_libraries.sol @@ -0,0 +1,14 @@ +library WithModifier { + modifier mod() { require(msg.value > 10 ether); _; } + function withMod(uint self) mod() internal view { require(self > 0); } +} + +contract Test { + using WithModifier for *; + + function f(uint _value) public payable { + _value.withMod(); + WithModifier.withMod(_value); + } +} +// ---- From 51f9e350b152483968391757155189ed0880c755 Mon Sep 17 00:00:00 2001 From: chriseth Date: Tue, 13 Mar 2018 16:50:38 +0100 Subject: [PATCH 022/197] Tests. --- test/libsolidity/SolidityEndToEndTest.cpp | 52 +++++++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/test/libsolidity/SolidityEndToEndTest.cpp b/test/libsolidity/SolidityEndToEndTest.cpp index 33cd1419c..282136f74 100644 --- a/test/libsolidity/SolidityEndToEndTest.cpp +++ b/test/libsolidity/SolidityEndToEndTest.cpp @@ -2879,6 +2879,58 @@ BOOST_AUTO_TEST_CASE(function_modifier_multiple_times_local_vars) ABI_CHECK(callContractFunction("a()"), encodeArgs(0)); } +BOOST_AUTO_TEST_CASE(function_modifier_library) +{ + char const* sourceCode = R"( + library L { + struct S { uint v; } + modifier mod(S storage s) { s.v++; _; } + function libFun(S storage s) mod(s) internal { s.v += 0x100; } + } + + contract Test { + using L for *; + L.S s; + + function f() public returns (uint) { + s.libFun(); + L.libFun(s); + return s.v; + } + } + )"; + compileAndRun(sourceCode); + ABI_CHECK(callContractFunction("f()"), encodeArgs(0x202)); +} + +BOOST_AUTO_TEST_CASE(function_modifier_library_inheritance) +{ + // Tests that virtual lookup for modifiers in libraries does not consider + // the current inheritance hierarchy. + + char const* sourceCode = R"( + library L { + struct S { uint v; } + modifier mod(S storage s) { s.v++; _; } + function libFun(S storage s) mod(s) internal { s.v += 0x100; } + } + + contract Test { + using L for *; + L.S s; + modifier mod(L.S storage) { revert(); _; } + + function f() public returns (uint) { + s.libFun(); + L.libFun(s); + return s.v; + } + } + )"; + compileAndRun(sourceCode); + ABI_CHECK(callContractFunction("f()"), encodeArgs(0x202)); +} + BOOST_AUTO_TEST_CASE(crazy_elementary_typenames_on_stack) { char const* sourceCode = R"( From c032a7ded1371c67aac2a1721bf522760c4d41a4 Mon Sep 17 00:00:00 2001 From: Daniel Kirchner Date: Tue, 13 Mar 2018 14:21:17 +0100 Subject: [PATCH 023/197] Add soltest.sh script that invokes soltest with the correct --testpath. --- scripts/soltest.sh | 43 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100755 scripts/soltest.sh diff --git a/scripts/soltest.sh b/scripts/soltest.sh new file mode 100755 index 000000000..00f484a14 --- /dev/null +++ b/scripts/soltest.sh @@ -0,0 +1,43 @@ +#!/usr/bin/env bash + +set -e + +REPO_ROOT="$(dirname "$0")"/.. +USE_DEBUGGER=0 +DEBUGGER="gdb --args" +BOOST_OPTIONS= +SOLTEST_OPTIONS= + +while [ $# -gt 0 ] +do + case "$1" in + --debugger) + shift + DEBUGGER="$1" + USE_DEBUGGER=1 + ;; + --debug) + USE_DEBUGGER=1 + ;; + --boost-options) + shift + BOOST_OPTIONS="${BOOST_OPTIONS} $1" + ;; + -t) + shift + BOOST_OPTIONS="${BOOST_OPTIONS} -t $1" + ;; + --show-progress | -p) + BOOST_OPTIONS="${BOOST_OPTIONS} $1" + ;; + *) + SOLTEST_OPTIONS="${SOLTEST_OPTIONS} $1" + ;; + esac + shift +done +if [ "$USE_DEBUGGER" -ne "0" ]; then + DEBUG_PREFIX=${DEBUGGER} +fi + +exec ${DEBUG_PREFIX} ${REPO_ROOT}/build/test/soltest ${BOOST_OPTIONS} -- --testpath ${REPO_ROOT}/test ${SOLTEST_OPTIONS} From eecc26deec1815191bc3405e54ef84daaba853a1 Mon Sep 17 00:00:00 2001 From: chriseth Date: Tue, 13 Mar 2018 17:18:21 +0100 Subject: [PATCH 024/197] Make external library functions accessible. --- Changelog.md | 1 + libsolidity/ast/AST.h | 1 + libsolidity/ast/Types.cpp | 4 ++-- test/libsolidity/SolidityEndToEndTest.cpp | 15 +++++++++++++++ .../visibility/external_library_function.sol | 14 ++++++++++++++ 5 files changed, 33 insertions(+), 2 deletions(-) create mode 100644 test/libsolidity/syntaxTests/visibility/external_library_function.sol diff --git a/Changelog.md b/Changelog.md index 648af66c6..58b93972e 100644 --- a/Changelog.md +++ b/Changelog.md @@ -6,6 +6,7 @@ Bugfixes: * Code Generator: Properly skip unneeded storgae array cleanup when not reducing length. * Commandline interface: Support ``--evm-version constantinople`` properly. * Standard JSON: Support ``constantinople`` as ``evmVersion`` properly. + * Type System: Make external library functions accessible. ### 0.4.21 (2018-03-07) diff --git a/libsolidity/ast/AST.h b/libsolidity/ast/AST.h index 863ad2fec..a25df64b7 100644 --- a/libsolidity/ast/AST.h +++ b/libsolidity/ast/AST.h @@ -203,6 +203,7 @@ public: bool isPublic() const { return visibility() >= Visibility::Public; } virtual bool isVisibleInContract() const { return visibility() != Visibility::External; } bool isVisibleInDerivedContracts() const { return isVisibleInContract() && visibility() >= Visibility::Internal; } + bool isVisibleAsLibraryMember() const { return visibility() >= Visibility::Internal; } std::string fullyQualifiedName() const { return sourceUnitName() + ":" + name(); } diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp index c08e0e673..b2881beac 100644 --- a/libsolidity/ast/Types.cpp +++ b/libsolidity/ast/Types.cpp @@ -304,7 +304,7 @@ MemberList::MemberMap Type::boundFunctions(Type const& _type, ContractDefinition ); for (FunctionDefinition const* function: library.definedFunctions()) { - if (!function->isVisibleInDerivedContracts() || seenFunctions.count(function)) + if (!function->isVisibleAsLibraryMember() || seenFunctions.count(function)) continue; seenFunctions.insert(function); FunctionType funType(*function, false); @@ -2875,7 +2875,7 @@ MemberList::MemberMap TypeType::nativeMembers(ContractDefinition const* _current } if (contract.isLibrary()) for (FunctionDefinition const* function: contract.definedFunctions()) - if (function->isVisibleInDerivedContracts()) + if (function->isVisibleAsLibraryMember()) members.push_back(MemberList::Member( function->name(), FunctionType(*function).asMemberFunction(true), diff --git a/test/libsolidity/SolidityEndToEndTest.cpp b/test/libsolidity/SolidityEndToEndTest.cpp index 33cd1419c..d8c8b8ade 100644 --- a/test/libsolidity/SolidityEndToEndTest.cpp +++ b/test/libsolidity/SolidityEndToEndTest.cpp @@ -6955,6 +6955,21 @@ BOOST_AUTO_TEST_CASE(library_call) ABI_CHECK(callContractFunction("f(uint256)", u256(33)), encodeArgs(u256(33) * 9)); } +BOOST_AUTO_TEST_CASE(library_function_external) +{ + char const* sourceCode = R"( + library Lib { function m(bytes b) external pure returns (byte) { return b[2]; } } + contract Test { + function f(bytes b) public pure returns (byte) { + return Lib.m(b); + } + } + )"; + compileAndRun(sourceCode, 0, "Lib"); + compileAndRun(sourceCode, 0, "Test", bytes(), map{{"Lib", m_contractAddress}}); + ABI_CHECK(callContractFunction("f(bytes)", u256(0x20), u256(5), "abcde"), encodeArgs("c")); +} + BOOST_AUTO_TEST_CASE(library_stray_values) { char const* sourceCode = R"( diff --git a/test/libsolidity/syntaxTests/visibility/external_library_function.sol b/test/libsolidity/syntaxTests/visibility/external_library_function.sol new file mode 100644 index 000000000..110e74dbd --- /dev/null +++ b/test/libsolidity/syntaxTests/visibility/external_library_function.sol @@ -0,0 +1,14 @@ +library L { + function f(uint) pure external {} +} + +contract C { + using L for *; + + function f() public pure { + L.f(2); + uint x; + x.f(); + } +} +// ---- From 834d63de2c4dc9c119862f8bf25b4f7c9f408d6e Mon Sep 17 00:00:00 2001 From: chriseth Date: Tue, 13 Mar 2018 17:28:58 +0100 Subject: [PATCH 025/197] Allow ``block.blockhash`` without being called. --- Changelog.md | 1 + libsolidity/codegen/ExpressionCompiler.cpp | 3 +++ test/libsolidity/SolidityEndToEndTest.cpp | 17 +++++++++++++++++ 3 files changed, 21 insertions(+) diff --git a/Changelog.md b/Changelog.md index 648af66c6..80c9e8252 100644 --- a/Changelog.md +++ b/Changelog.md @@ -3,6 +3,7 @@ Features: Bugfixes: + * Code Generator: Allow ``block.blockhash`` without being called. * Code Generator: Properly skip unneeded storgae array cleanup when not reducing length. * Commandline interface: Support ``--evm-version constantinople`` properly. * Standard JSON: Support ``constantinople`` as ``evmVersion`` properly. diff --git a/libsolidity/codegen/ExpressionCompiler.cpp b/libsolidity/codegen/ExpressionCompiler.cpp index 7162cb0dd..f50628ff4 100644 --- a/libsolidity/codegen/ExpressionCompiler.cpp +++ b/libsolidity/codegen/ExpressionCompiler.cpp @@ -1147,6 +1147,9 @@ 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 == "blockhash") + { + } else solAssert(false, "Unknown magic member."); break; diff --git a/test/libsolidity/SolidityEndToEndTest.cpp b/test/libsolidity/SolidityEndToEndTest.cpp index 33cd1419c..c0ff586d7 100644 --- a/test/libsolidity/SolidityEndToEndTest.cpp +++ b/test/libsolidity/SolidityEndToEndTest.cpp @@ -1788,6 +1788,23 @@ BOOST_AUTO_TEST_CASE(transfer_ether) ABI_CHECK(callContractFunction("b(address,uint256)", oogRecipient, 10), encodeArgs()); } +BOOST_AUTO_TEST_CASE(uncalled_blockhash) +{ + char const* code = R"( + contract C { + function f() public view returns (bytes32) + { + var x = block.blockhash; + return x(block.number - 1); + } + } + )"; + compileAndRun(code, 0, "C"); + bytes result = callContractFunction("f()"); + BOOST_REQUIRE_EQUAL(result.size(), 32); + BOOST_CHECK(result[0] != 0 || result[1] != 0 || result[2] != 0); +} + BOOST_AUTO_TEST_CASE(log0) { char const* sourceCode = R"( From 09420f1a4480e8bfeb13f861fda801e49bce8487 Mon Sep 17 00:00:00 2001 From: Daniel Kirchner Date: Tue, 13 Mar 2018 17:54:22 +0100 Subject: [PATCH 026/197] Store filenames in static variable to guarantee sufficient lifetime. --- test/libsolidity/SyntaxTest.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/test/libsolidity/SyntaxTest.cpp b/test/libsolidity/SyntaxTest.cpp index f1c60458a..45a851b60 100644 --- a/test/libsolidity/SyntaxTest.cpp +++ b/test/libsolidity/SyntaxTest.cpp @@ -21,6 +21,7 @@ #include #include #include +#include #include using namespace dev; @@ -188,6 +189,9 @@ int SyntaxTest::registerTests( } else { + static vector> filenames; + + filenames.emplace_back(new string(_path.string())); _suite.add(make_test_case( [fullpath] { @@ -196,7 +200,7 @@ int SyntaxTest::registerTests( BOOST_ERROR("Test expectation mismatch.\n" + errorStream.str()); }, _path.stem().string(), - _path.string(), + *filenames.back(), 0 )); numTestsAdded = 1; From 0d0c9b868817bad17968f9c23bce5d3844a5971c Mon Sep 17 00:00:00 2001 From: Daniel Kirchner Date: Wed, 14 Mar 2018 09:55:04 +0100 Subject: [PATCH 027/197] DocStringParser: Fix error message for empty parameter description. --- libsolidity/parsing/DocStringParser.cpp | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/libsolidity/parsing/DocStringParser.cpp b/libsolidity/parsing/DocStringParser.cpp index 0409de720..d058d5569 100644 --- a/libsolidity/parsing/DocStringParser.cpp +++ b/libsolidity/parsing/DocStringParser.cpp @@ -119,21 +119,17 @@ DocStringParser::iter DocStringParser::parseDocTagParam(iter _pos, iter _end) return _end; } auto nameEndPos = firstSpaceOrTab(nameStartPos, _end); - if (nameEndPos == _end) - { - appendError("End of param name not found: " + string(nameStartPos, _end)); - return _end; - } auto paramName = string(nameStartPos, nameEndPos); auto descStartPos = skipWhitespace(nameEndPos, _end); - if (descStartPos == _end) + auto nlPos = find(descStartPos, _end, '\n'); + + if (descStartPos == nlPos) { appendError("No description given for param " + paramName); return _end; } - auto nlPos = find(descStartPos, _end, '\n'); auto paramDesc = string(descStartPos, nlPos); newTag("param"); m_lastTag->paramName = paramName; From 9d079fd1261e40339157bff6fd46de96ae844562 Mon Sep 17 00:00:00 2001 From: Daniel Kirchner Date: Wed, 14 Mar 2018 10:34:16 +0100 Subject: [PATCH 028/197] DocStringParser: Add Changelog entry and test case for empty descriptions. --- Changelog.md | 1 + .../libsolidity/syntaxTests/docstring_empty_description.sol | 6 ++++++ 2 files changed, 7 insertions(+) create mode 100644 test/libsolidity/syntaxTests/docstring_empty_description.sol diff --git a/Changelog.md b/Changelog.md index 648af66c6..a0ad62426 100644 --- a/Changelog.md +++ b/Changelog.md @@ -6,6 +6,7 @@ Bugfixes: * Code Generator: Properly skip unneeded storgae array cleanup when not reducing length. * Commandline interface: Support ``--evm-version constantinople`` properly. * Standard JSON: Support ``constantinople`` as ``evmVersion`` properly. + * DocString Parser: Fix error message for empty descriptions. ### 0.4.21 (2018-03-07) diff --git a/test/libsolidity/syntaxTests/docstring_empty_description.sol b/test/libsolidity/syntaxTests/docstring_empty_description.sol new file mode 100644 index 000000000..0caa1b232 --- /dev/null +++ b/test/libsolidity/syntaxTests/docstring_empty_description.sol @@ -0,0 +1,6 @@ +contract C { + /// @param id + function vote(uint id) public; +} +// ---- +// DocstringParsingError: No description given for param id From 1882c508c6d3ef46847ba23d06689ea17b01ccb5 Mon Sep 17 00:00:00 2001 From: Daniel Kirchner Date: Wed, 14 Mar 2018 10:45:01 +0100 Subject: [PATCH 029/197] soltest: force the use of the --testpath option for soltest with an explicit error. --- test/boostTest.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/test/boostTest.cpp b/test/boostTest.cpp index e557ff95a..8ad97db39 100644 --- a/test/boostTest.cpp +++ b/test/boostTest.cpp @@ -55,6 +55,10 @@ test_suite* init_unit_test_suite( int /*argc*/, char* /*argv*/[] ) { master_test_suite_t& master = framework::master_test_suite(); master.p_name.value = "SolidityTests"; + solAssert( + !dev::test::Options::get().testPath.empty(), + "No test path specified. The --testpath argument is required." + ); solAssert(dev::solidity::test::SyntaxTest::registerTests( master, dev::test::Options::get().testPath / "libsolidity", From d63d41b3b545b0e13e2ee7f880120b2ba852c654 Mon Sep 17 00:00:00 2001 From: Daniel Kirchner Date: Wed, 14 Mar 2018 12:04:04 +0100 Subject: [PATCH 030/197] test: Rename test/TestHelper.* to test/Options.* and add Options::validate(). --- test/ExecutionFramework.h | 2 +- test/{TestHelper.cpp => Options.cpp} | 16 +++++++++++++++- test/{TestHelper.h => Options.h} | 1 + test/RPCSession.cpp | 2 +- test/boostTest.cpp | 7 ++----- test/libdevcore/Checksum.cpp | 2 +- test/libdevcore/IndentedWriter.cpp | 2 +- test/libdevcore/JSON.cpp | 2 +- test/libdevcore/StringUtils.cpp | 2 +- test/libdevcore/SwarmHash.cpp | 2 +- test/libdevcore/UTF8.cpp | 2 +- test/libdevcore/Whiskers.cpp | 2 +- test/libevmasm/Optimiser.cpp | 2 +- test/libevmasm/SourceLocation.cpp | 2 +- test/libjulia/Common.cpp | 2 +- test/libjulia/Parser.cpp | 2 +- test/liblll/Compiler.cpp | 2 +- test/liblll/EndToEndTest.cpp | 2 +- test/libsolidity/ASTJSON.cpp | 2 +- test/libsolidity/AnalysisFramework.cpp | 2 +- test/libsolidity/Assembly.cpp | 2 +- test/libsolidity/Imports.cpp | 2 +- test/libsolidity/InlineAssembly.cpp | 2 +- test/libsolidity/JSONCompiler.cpp | 4 ++-- test/libsolidity/Metadata.cpp | 4 ++-- test/libsolidity/SemVerMatcher.cpp | 2 +- test/libsolidity/SolidityABIJSON.cpp | 2 +- test/libsolidity/SolidityEndToEndTest.cpp | 2 +- test/libsolidity/SolidityExpressionCompiler.cpp | 2 +- .../SolidityNameAndTypeResolution.cpp | 2 +- test/libsolidity/SolidityNatspecJSON.cpp | 2 +- test/libsolidity/SolidityParser.cpp | 4 ++-- test/libsolidity/ViewPureChecker.cpp | 2 +- 33 files changed, 51 insertions(+), 39 deletions(-) rename test/{TestHelper.cpp => Options.cpp} (85%) rename test/{TestHelper.h => Options.h} (98%) diff --git a/test/ExecutionFramework.h b/test/ExecutionFramework.h index a7971b810..ee8da3226 100644 --- a/test/ExecutionFramework.h +++ b/test/ExecutionFramework.h @@ -22,7 +22,7 @@ #pragma once -#include +#include #include #include diff --git a/test/TestHelper.cpp b/test/Options.cpp similarity index 85% rename from test/TestHelper.cpp rename to test/Options.cpp index 77fa204fb..ff4a7c988 100644 --- a/test/TestHelper.cpp +++ b/test/Options.cpp @@ -19,9 +19,10 @@ * @date 2014 */ -#include +#include #include +#include #include @@ -71,6 +72,19 @@ Options::Options() testPath = path; } +void Options::validate() const +{ + solAssert( + !dev::test::Options::get().testPath.empty(), + "No test path specified. The --testpath argument is required." + ); + if (!disableIPC) + solAssert( + !dev::test::Options::get().ipcPath.empty(), + "No ipc path specified. The --ipcpath argument is required, unless --no-ipc is used." + ); +} + dev::solidity::EVMVersion Options::evmVersion() const { if (!evmVersionString.empty()) diff --git a/test/TestHelper.h b/test/Options.h similarity index 98% rename from test/TestHelper.h rename to test/Options.h index f7b1d94c8..9bc698762 100644 --- a/test/TestHelper.h +++ b/test/Options.h @@ -41,6 +41,7 @@ struct Options: boost::noncopyable bool disableIPC = false; bool disableSMT = false; + void validate() const; solidity::EVMVersion evmVersion() const; static Options const& get(); diff --git a/test/RPCSession.cpp b/test/RPCSession.cpp index 54871057f..03b1341cb 100644 --- a/test/RPCSession.cpp +++ b/test/RPCSession.cpp @@ -21,7 +21,7 @@ #include -#include +#include #include diff --git a/test/boostTest.cpp b/test/boostTest.cpp index 8ad97db39..f16973b53 100644 --- a/test/boostTest.cpp +++ b/test/boostTest.cpp @@ -35,7 +35,7 @@ #pragma GCC diagnostic pop -#include +#include #include using namespace boost::unit_test; @@ -55,10 +55,7 @@ test_suite* init_unit_test_suite( int /*argc*/, char* /*argv*/[] ) { master_test_suite_t& master = framework::master_test_suite(); master.p_name.value = "SolidityTests"; - solAssert( - !dev::test::Options::get().testPath.empty(), - "No test path specified. The --testpath argument is required." - ); + dev::test::Options::get().validate(); solAssert(dev::solidity::test::SyntaxTest::registerTests( master, dev::test::Options::get().testPath / "libsolidity", diff --git a/test/libdevcore/Checksum.cpp b/test/libdevcore/Checksum.cpp index 4eedbd999..95066b69f 100644 --- a/test/libdevcore/Checksum.cpp +++ b/test/libdevcore/Checksum.cpp @@ -22,7 +22,7 @@ #include -#include "../TestHelper.h" +#include using namespace std; diff --git a/test/libdevcore/IndentedWriter.cpp b/test/libdevcore/IndentedWriter.cpp index a694aa1b9..916c99d09 100644 --- a/test/libdevcore/IndentedWriter.cpp +++ b/test/libdevcore/IndentedWriter.cpp @@ -20,7 +20,7 @@ #include -#include "../TestHelper.h" +#include using namespace std; diff --git a/test/libdevcore/JSON.cpp b/test/libdevcore/JSON.cpp index 39d71b42b..39d958f5f 100644 --- a/test/libdevcore/JSON.cpp +++ b/test/libdevcore/JSON.cpp @@ -21,7 +21,7 @@ #include -#include "../TestHelper.h" +#include using namespace std; diff --git a/test/libdevcore/StringUtils.cpp b/test/libdevcore/StringUtils.cpp index 597457cca..9ee93d02a 100644 --- a/test/libdevcore/StringUtils.cpp +++ b/test/libdevcore/StringUtils.cpp @@ -20,7 +20,7 @@ #include -#include "../TestHelper.h" +#include using namespace std; diff --git a/test/libdevcore/SwarmHash.cpp b/test/libdevcore/SwarmHash.cpp index 1ed1da181..913586f89 100644 --- a/test/libdevcore/SwarmHash.cpp +++ b/test/libdevcore/SwarmHash.cpp @@ -20,7 +20,7 @@ #include -#include "../TestHelper.h" +#include using namespace std; diff --git a/test/libdevcore/UTF8.cpp b/test/libdevcore/UTF8.cpp index 719ada720..6de4570fe 100644 --- a/test/libdevcore/UTF8.cpp +++ b/test/libdevcore/UTF8.cpp @@ -21,7 +21,7 @@ #include #include -#include "../TestHelper.h" +#include using namespace std; diff --git a/test/libdevcore/Whiskers.cpp b/test/libdevcore/Whiskers.cpp index 84149173e..b12acdd70 100644 --- a/test/libdevcore/Whiskers.cpp +++ b/test/libdevcore/Whiskers.cpp @@ -20,7 +20,7 @@ #include -#include "../TestHelper.h" +#include using namespace std; diff --git a/test/libevmasm/Optimiser.cpp b/test/libevmasm/Optimiser.cpp index e6abcb531..f630b3048 100644 --- a/test/libevmasm/Optimiser.cpp +++ b/test/libevmasm/Optimiser.cpp @@ -20,7 +20,7 @@ * Tests for the Solidity optimizer. */ -#include +#include #include #include diff --git a/test/libevmasm/SourceLocation.cpp b/test/libevmasm/SourceLocation.cpp index 6889b3e67..764da3cd6 100644 --- a/test/libevmasm/SourceLocation.cpp +++ b/test/libevmasm/SourceLocation.cpp @@ -22,7 +22,7 @@ #include -#include "../TestHelper.h" +#include namespace dev { diff --git a/test/libjulia/Common.cpp b/test/libjulia/Common.cpp index 41f5d3206..24519b015 100644 --- a/test/libjulia/Common.cpp +++ b/test/libjulia/Common.cpp @@ -21,7 +21,7 @@ #include -#include +#include #include diff --git a/test/libjulia/Parser.cpp b/test/libjulia/Parser.cpp index df905dd68..9d66658e1 100644 --- a/test/libjulia/Parser.cpp +++ b/test/libjulia/Parser.cpp @@ -19,7 +19,7 @@ * Unit tests for parsing Julia. */ -#include "../TestHelper.h" +#include #include diff --git a/test/liblll/Compiler.cpp b/test/liblll/Compiler.cpp index 6c6eae3f9..ebdea1858 100644 --- a/test/liblll/Compiler.cpp +++ b/test/liblll/Compiler.cpp @@ -20,7 +20,7 @@ * Unit tests for the LLL compiler. */ -#include +#include #include diff --git a/test/liblll/EndToEndTest.cpp b/test/liblll/EndToEndTest.cpp index e5e70cf8c..fd8099f20 100644 --- a/test/liblll/EndToEndTest.cpp +++ b/test/liblll/EndToEndTest.cpp @@ -21,7 +21,7 @@ */ #include -#include +#include #include diff --git a/test/libsolidity/ASTJSON.cpp b/test/libsolidity/ASTJSON.cpp index 9bf60b647..b44dd331a 100644 --- a/test/libsolidity/ASTJSON.cpp +++ b/test/libsolidity/ASTJSON.cpp @@ -20,7 +20,7 @@ * Tests for the json ast output. */ -#include +#include #include #include diff --git a/test/libsolidity/AnalysisFramework.cpp b/test/libsolidity/AnalysisFramework.cpp index 7c335a482..4538757de 100644 --- a/test/libsolidity/AnalysisFramework.cpp +++ b/test/libsolidity/AnalysisFramework.cpp @@ -20,7 +20,7 @@ #include -#include +#include #include #include diff --git a/test/libsolidity/Assembly.cpp b/test/libsolidity/Assembly.cpp index aff610a48..5519ae0d9 100644 --- a/test/libsolidity/Assembly.cpp +++ b/test/libsolidity/Assembly.cpp @@ -20,7 +20,7 @@ * Unit tests for Assembly Items from evmasm/Assembly.h */ -#include +#include #include #include diff --git a/test/libsolidity/Imports.cpp b/test/libsolidity/Imports.cpp index bc81b3b1c..1b5dd4a5b 100644 --- a/test/libsolidity/Imports.cpp +++ b/test/libsolidity/Imports.cpp @@ -21,7 +21,7 @@ */ #include -#include +#include #include #include diff --git a/test/libsolidity/InlineAssembly.cpp b/test/libsolidity/InlineAssembly.cpp index a4dcc4d5f..34ca33e3c 100644 --- a/test/libsolidity/InlineAssembly.cpp +++ b/test/libsolidity/InlineAssembly.cpp @@ -20,7 +20,7 @@ * Unit tests for inline assembly. */ -#include "../TestHelper.h" +#include #include #include diff --git a/test/libsolidity/JSONCompiler.cpp b/test/libsolidity/JSONCompiler.cpp index 285c56049..aed0a3706 100644 --- a/test/libsolidity/JSONCompiler.cpp +++ b/test/libsolidity/JSONCompiler.cpp @@ -25,8 +25,8 @@ #include #include -#include "../Metadata.h" -#include "../TestHelper.h" +#include +#include using namespace std; diff --git a/test/libsolidity/Metadata.cpp b/test/libsolidity/Metadata.cpp index f1edeeb72..808bd1e15 100644 --- a/test/libsolidity/Metadata.cpp +++ b/test/libsolidity/Metadata.cpp @@ -19,8 +19,8 @@ * Unit tests for the metadata output. */ -#include "../Metadata.h" -#include "../TestHelper.h" +#include +#include #include #include #include diff --git a/test/libsolidity/SemVerMatcher.cpp b/test/libsolidity/SemVerMatcher.cpp index 08ef5277f..07f8fba62 100644 --- a/test/libsolidity/SemVerMatcher.cpp +++ b/test/libsolidity/SemVerMatcher.cpp @@ -25,7 +25,7 @@ #include #include #include -#include "../TestHelper.h" +#include using namespace std; diff --git a/test/libsolidity/SolidityABIJSON.cpp b/test/libsolidity/SolidityABIJSON.cpp index 0d471b32f..107abc26a 100644 --- a/test/libsolidity/SolidityABIJSON.cpp +++ b/test/libsolidity/SolidityABIJSON.cpp @@ -20,7 +20,7 @@ * Unit tests for the solidity compiler JSON Interface output. */ -#include "../TestHelper.h" +#include #include #include diff --git a/test/libsolidity/SolidityEndToEndTest.cpp b/test/libsolidity/SolidityEndToEndTest.cpp index 33cd1419c..195ec85b3 100644 --- a/test/libsolidity/SolidityEndToEndTest.cpp +++ b/test/libsolidity/SolidityEndToEndTest.cpp @@ -23,7 +23,7 @@ #include -#include +#include #include #include diff --git a/test/libsolidity/SolidityExpressionCompiler.cpp b/test/libsolidity/SolidityExpressionCompiler.cpp index 5f044b44c..c8adfc6ed 100644 --- a/test/libsolidity/SolidityExpressionCompiler.cpp +++ b/test/libsolidity/SolidityExpressionCompiler.cpp @@ -30,7 +30,7 @@ #include #include #include -#include "../TestHelper.h" +#include using namespace std; diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index 1f76c01b5..c757037c7 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -22,7 +22,7 @@ #include -#include +#include #include diff --git a/test/libsolidity/SolidityNatspecJSON.cpp b/test/libsolidity/SolidityNatspecJSON.cpp index 49a725e04..eeebeb745 100644 --- a/test/libsolidity/SolidityNatspecJSON.cpp +++ b/test/libsolidity/SolidityNatspecJSON.cpp @@ -20,7 +20,7 @@ * Unit tests for the solidity compiler JSON Interface output. */ -#include "../TestHelper.h" +#include #include #include #include diff --git a/test/libsolidity/SolidityParser.cpp b/test/libsolidity/SolidityParser.cpp index f03b30e13..4e862f608 100644 --- a/test/libsolidity/SolidityParser.cpp +++ b/test/libsolidity/SolidityParser.cpp @@ -25,8 +25,8 @@ #include #include #include -#include "../TestHelper.h" -#include "ErrorCheck.h" +#include +#include using namespace std; diff --git a/test/libsolidity/ViewPureChecker.cpp b/test/libsolidity/ViewPureChecker.cpp index 26ff461c6..a6ce6d917 100644 --- a/test/libsolidity/ViewPureChecker.cpp +++ b/test/libsolidity/ViewPureChecker.cpp @@ -20,7 +20,7 @@ #include -#include +#include #include From f0c174af8fda32f705e30c95170ef2eb087b729b Mon Sep 17 00:00:00 2001 From: Daniel R Date: Thu, 15 Mar 2018 10:42:59 +0000 Subject: [PATCH 031/197] Fix Typo in changelog.md --- Changelog.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Changelog.md b/Changelog.md index e9a9548db..d37bce63d 100644 --- a/Changelog.md +++ b/Changelog.md @@ -4,7 +4,7 @@ Features: Bugfixes: * Code Generator: Allow ``block.blockhash`` without being called. - * Code Generator: Properly skip unneeded storgae array cleanup when not reducing length. + * Code Generator: Properly skip unneeded storage array cleanup when not reducing length. * Code Generator: Bugfix in modifier lookup in libraries. * Commandline interface: Support ``--evm-version constantinople`` properly. * DocString Parser: Fix error message for empty descriptions. From 269241e9105a3b3014002bf711ade985d87febe4 Mon Sep 17 00:00:00 2001 From: Daniel Kirchner Date: Mon, 12 Mar 2018 13:57:48 +0100 Subject: [PATCH 032/197] Add formatted printing to SyntaxTest and expand its public interface. --- test/libsolidity/FormattedScope.h | 66 +++++++++++++++++++++++++++++++ test/libsolidity/SyntaxTest.cpp | 56 ++++++++++++++++++++------ test/libsolidity/SyntaxTest.h | 17 ++++++-- 3 files changed, 123 insertions(+), 16 deletions(-) create mode 100644 test/libsolidity/FormattedScope.h diff --git a/test/libsolidity/FormattedScope.h b/test/libsolidity/FormattedScope.h new file mode 100644 index 000000000..78560848e --- /dev/null +++ b/test/libsolidity/FormattedScope.h @@ -0,0 +1,66 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ + +#pragma once + +#include + +#include +#include + +namespace dev +{ +namespace solidity +{ +namespace test +{ + +namespace formatting +{ + +static constexpr char const* RESET = "\033[0m"; +static constexpr char const* RED = "\033[1;31m"; +static constexpr char const* GREEN = "\033[1;32m"; +static constexpr char const* YELLOW = "\033[1;33m"; +static constexpr char const* CYAN = "\033[1;36m"; +static constexpr char const* BOLD = "\033[1m"; +static constexpr char const* INVERSE = "\033[7m"; + +} + +class FormattedScope: boost::noncopyable +{ +public: + /// @arg _formatting List of formatting strings (e.g. colors) defined in the formatting namespace. + FormattedScope(std::ostream& _stream, bool const _enabled, std::vector const& _formatting): + m_stream(_stream), m_enabled(_enabled) + { + if (m_enabled) + for (auto const& format: _formatting) + m_stream << format; + } + ~FormattedScope() { if (m_enabled) m_stream << formatting::RESET; } + template + std::ostream& operator<<(T&& _t) { return m_stream << std::forward(_t); } +private: + std::ostream& m_stream; + bool m_enabled; +}; + +} +} +} diff --git a/test/libsolidity/SyntaxTest.cpp b/test/libsolidity/SyntaxTest.cpp index 45a851b60..70abac48a 100644 --- a/test/libsolidity/SyntaxTest.cpp +++ b/test/libsolidity/SyntaxTest.cpp @@ -27,6 +27,7 @@ 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; @@ -56,41 +57,70 @@ SyntaxTest::SyntaxTest(string const& _filename) m_expectations = parseExpectations(file); } -bool SyntaxTest::run(ostream& _stream, string const& _indent) +bool SyntaxTest::run(ostream& _stream, string const& _linePrefix, bool const _formatted) { m_errorList = parseAnalyseAndReturnError(m_source, true, true, true).second; if (!matchesExpectations(m_errorList)) { - std::string nextIndentLevel = _indent + "\t"; - _stream << _indent << "Expected result:" << endl; - printExpected(_stream, nextIndentLevel); - _stream << _indent << "Obtained result:\n"; - printErrorList(_stream, m_errorList, nextIndentLevel); + std::string nextIndentLevel = _linePrefix + " "; + FormattedScope(_stream, _formatted, {BOLD, CYAN}) << _linePrefix << "Expected result:" << endl; + printExpected(_stream, nextIndentLevel, _formatted); + FormattedScope(_stream, _formatted, {BOLD, CYAN}) << _linePrefix << "Obtained result:\n"; + printErrorList(_stream, m_errorList, nextIndentLevel, false, false, _formatted); return false; } return true; } -void SyntaxTest::printExpected(ostream& _stream, string const& _indent) const +void SyntaxTest::printExpected(ostream& _stream, string const& _linePrefix, bool const _formatted) const { if (m_expectations.empty()) - _stream << _indent << "Success" << endl; + FormattedScope(_stream, _formatted, {BOLD, GREEN}) << _linePrefix << "Success" << endl; else for (auto const& expectation: m_expectations) - _stream << _indent << expectation.type << ": " << expectation.message << endl; + FormattedScope(_stream, _formatted, {BOLD, expectation.type == "Warning" ? YELLOW : RED}) << + _linePrefix << expectation.type << ": " << expectation.message << endl; } void SyntaxTest::printErrorList( ostream& _stream, ErrorList const& _errorList, - string const& _indent + string const& _linePrefix, + bool const _ignoreWarnings, + bool const _lineNumbers, + bool const _formatted ) const { if (_errorList.empty()) - _stream << _indent << "Success" << endl; + FormattedScope(_stream, _formatted, {BOLD, GREEN}) << _linePrefix << "Success" << endl; else for (auto const& error: _errorList) - _stream << _indent << error->typeName() << ": " << errorMessage(*error) << endl; + { + bool isWarning = (error->type() == Error::Type::Warning); + if (isWarning && _ignoreWarnings) continue; + + FormattedScope scope(_stream, _formatted, {BOLD, isWarning ? YELLOW : RED}); + _stream << _linePrefix; + if (_lineNumbers) + { + int line = offsetToLineNumber( + boost::get_error_info(*error)->start + ); + if (line >= 0) + _stream << "(" << line << "): "; + } + _stream << error->typeName() << ": " << errorMessage(*error) << endl; + } +} + +int SyntaxTest::offsetToLineNumber(int _location) const +{ + // parseAnalyseAndReturnError(...) prepends a version pragma + _location -= strlen("pragma solidity >=0.0;\n"); + if (_location < 0 || static_cast(_location) >= m_source.size()) + return -1; + else + return 1 + std::count(m_source.begin(), m_source.begin() + _location, '\n'); } bool SyntaxTest::matchesExpectations(ErrorList const& _errorList) const @@ -196,7 +226,7 @@ int SyntaxTest::registerTests( [fullpath] { std::stringstream errorStream; - if (!SyntaxTest(fullpath.string()).run(errorStream, "")) + if (!SyntaxTest(fullpath.string()).run(errorStream)) BOOST_ERROR("Test expectation mismatch.\n" + errorStream.str()); }, _path.stem().string(), diff --git a/test/libsolidity/SyntaxTest.h b/test/libsolidity/SyntaxTest.h index 4379c77bc..441cc4f82 100644 --- a/test/libsolidity/SyntaxTest.h +++ b/test/libsolidity/SyntaxTest.h @@ -18,6 +18,7 @@ #pragma once #include +#include #include #include @@ -47,13 +48,22 @@ class SyntaxTest: AnalysisFramework public: SyntaxTest(std::string const& _filename); - bool run(std::ostream& _stream, std::string const& _indent); + bool run(std::ostream& _stream, std::string const& _linePrefix = "", bool const _formatted = false); + + std::vector const& expectations() const { return m_expectations; } + std::string const& source() const { return m_source; } + ErrorList const& errorList() const { return m_errorList; } + ErrorList const& compilerErrors() const { return m_compiler.errors(); } + + void printExpected(std::ostream& _stream, std::string const& _linePrefix, bool const _formatted = false) const; - void printExpected(std::ostream& _stream, std::string const& _indent) const; void printErrorList( std::ostream& _stream, ErrorList const& _errors, - std::string const& _indent + std::string const& _linePrefix, + bool const _ignoreWarnings, + bool const _lineNumbers, + bool const _formatted = false ) const; static int registerTests( @@ -66,6 +76,7 @@ private: static std::string errorMessage(Error const& _e); static std::string parseSource(std::istream& _stream); static std::vector parseExpectations(std::istream& _stream); + int offsetToLineNumber(int _location) const; std::string m_source; std::vector m_expectations; From 7fa892eca93cc1b3fd75eb9341c11f0a471970d9 Mon Sep 17 00:00:00 2001 From: Daniel Kirchner Date: Wed, 14 Mar 2018 19:15:48 +0100 Subject: [PATCH 033/197] Add interactive test tool isoltest. --- libdevcore/CommonIO.cpp | 49 ++++++ libdevcore/CommonIO.h | 3 + scripts/isoltest.sh | 6 + test/tools/CMakeLists.txt | 3 + test/tools/isoltest.cpp | 346 ++++++++++++++++++++++++++++++++++++++ 5 files changed, 407 insertions(+) create mode 100755 scripts/isoltest.sh create mode 100644 test/tools/isoltest.cpp diff --git a/libdevcore/CommonIO.cpp b/libdevcore/CommonIO.cpp index 8c7e08f6b..6526baf97 100644 --- a/libdevcore/CommonIO.cpp +++ b/libdevcore/CommonIO.cpp @@ -27,6 +27,7 @@ #if defined(_WIN32) #include #else +#include #include #endif #include @@ -118,3 +119,51 @@ void dev::writeFile(std::string const& _file, bytesConstRef _data, bool _writeDe } } } + +#if defined(_WIN32) +class DisableConsoleBuffering +{ +public: + DisableConsoleBuffering() + { + m_stdin = GetStdHandle(STD_INPUT_HANDLE); + GetConsoleMode(m_stdin, &m_oldMode); + SetConsoleMode(m_stdin, m_oldMode & (~(ENABLE_ECHO_INPUT | ENABLE_LINE_INPUT))); + } + ~DisableConsoleBuffering() + { + SetConsoleMode(m_stdin, m_oldMode); + } +private: + HANDLE m_stdin; + DWORD m_oldMode; +}; +#else +class DisableConsoleBuffering +{ +public: + DisableConsoleBuffering() + { + tcgetattr(0, &m_termios); + m_termios.c_lflag &= ~ICANON; + m_termios.c_lflag &= ~ECHO; + m_termios.c_cc[VMIN] = 1; + m_termios.c_cc[VTIME] = 0; + tcsetattr(0, TCSANOW, &m_termios); + } + ~DisableConsoleBuffering() + { + m_termios.c_lflag |= ICANON; + m_termios.c_lflag |= ECHO; + tcsetattr(0, TCSADRAIN, &m_termios); + } +private: + struct termios m_termios; +}; +#endif + +int dev::readStandardInputChar() +{ + DisableConsoleBuffering disableConsoleBuffering; + return cin.get(); +} diff --git a/libdevcore/CommonIO.h b/libdevcore/CommonIO.h index 33769ec3d..3ecdb4c38 100644 --- a/libdevcore/CommonIO.h +++ b/libdevcore/CommonIO.h @@ -37,6 +37,9 @@ std::string readFileAsString(std::string const& _file); /// Retrieve and returns the contents of standard input (until EOF). 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 diff --git a/scripts/isoltest.sh b/scripts/isoltest.sh new file mode 100755 index 000000000..8aed0b3aa --- /dev/null +++ b/scripts/isoltest.sh @@ -0,0 +1,6 @@ +#!/usr/bin/env bash + +set -e + +REPO_ROOT="$(dirname "$0")"/.. +exec ${REPO_ROOT}/build/test/tools/isoltest --testpath ${REPO_ROOT}/test diff --git a/test/tools/CMakeLists.txt b/test/tools/CMakeLists.txt index a693ebab4..febb0c26b 100644 --- a/test/tools/CMakeLists.txt +++ b/test/tools/CMakeLists.txt @@ -1,2 +1,5 @@ add_executable(solfuzzer fuzzer.cpp) target_link_libraries(solfuzzer PRIVATE libsolc evmasm ${Boost_PROGRAM_OPTIONS_LIBRARIES}) + +add_executable(isoltest isoltest.cpp ../Options.cpp ../libsolidity/SyntaxTest.cpp ../libsolidity/AnalysisFramework.cpp) +target_link_libraries(isoltest PRIVATE libsolc solidity evmasm ${Boost_PROGRAM_OPTIONS_LIBRARIES} ${Boost_UNIT_TEST_FRAMEWORK_LIBRARIES}) diff --git a/test/tools/isoltest.cpp b/test/tools/isoltest.cpp new file mode 100644 index 000000000..668481cf8 --- /dev/null +++ b/test/tools/isoltest.cpp @@ -0,0 +1,346 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ + +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include + +using namespace dev; +using namespace dev::solidity; +using namespace dev::solidity::test; +using namespace dev::solidity::test::formatting; +using namespace std; +namespace po = boost::program_options; +namespace fs = boost::filesystem; + +struct SyntaxTestStats +{ + int successCount; + int runCount; + operator bool() const { return successCount == runCount; } +}; + +class SyntaxTestTool +{ +public: + SyntaxTestTool(string const& _name, fs::path const& _path, bool _formatted): + m_formatted(_formatted), m_name(_name), m_path(_path) + {} + + enum class Result + { + Success, + Failure, + ParserError, + InputOutputError + }; + + Result process(); + + static SyntaxTestStats processPath( + fs::path const& _basepath, + fs::path const& _path, + bool const _formatted + ); + + static string editor; +private: + enum class Request + { + Skip, + Rerun, + Quit + }; + + Request handleResponse(bool const _parserError); + + void printContract() const; + + bool const m_formatted; + string const m_name; + fs::path const m_path; + unique_ptr m_test; +}; + +string SyntaxTestTool::editor; + +void SyntaxTestTool::printContract() const +{ + stringstream stream(m_test->source()); + string line; + while (getline(stream, line)) + cout << " " << line << endl; + cout << endl; +} + +SyntaxTestTool::Result SyntaxTestTool::process() +{ + bool success; + bool parserError = false; + std::stringstream outputMessages; + + (FormattedScope(cout, m_formatted, {BOLD}) << m_name << ": ").flush(); + + try + { + m_test = unique_ptr(new SyntaxTest(m_path.string())); + } + catch (std::exception const& _e) + { + FormattedScope(cout, m_formatted, {BOLD, RED}) << "cannot read test: " << _e.what() << endl; + return Result::InputOutputError; + } + + try + { + success = m_test->run(outputMessages, " ", m_formatted); + } + catch (...) + { + success = false; + parserError = true; + } + + if (success) + { + FormattedScope(cout, m_formatted, {BOLD, GREEN}) << "OK" << endl; + return Result::Success; + } + else + { + FormattedScope(cout, m_formatted, {BOLD, RED}) << "FAIL" << endl; + + FormattedScope(cout, m_formatted, {BOLD, CYAN}) << " Contract:" << endl; + printContract(); + + if (parserError) + { + cout << " "; + FormattedScope(cout, m_formatted, {INVERSE, RED}) << "Parsing failed:" << endl; + m_test->printErrorList(cout, m_test->compilerErrors(), " ", true, true, m_formatted); + cout << endl; + return Result::ParserError; + } + else + { + cout << outputMessages.str() << endl; + return Result::Failure; + } + } +} + +SyntaxTestTool::Request SyntaxTestTool::handleResponse(bool const _parserError) +{ + if (_parserError) + cout << "(e)dit/(s)kip/(q)uit? "; + else + cout << "(e)dit/(u)pdate expectations/(s)kip/(q)uit? "; + cout.flush(); + + while (true) + { + switch(readStandardInputChar()) + { + case 's': + cout << endl; + return Request::Skip; + case 'u': + if (_parserError) + break; + else + { + cout << endl; + ofstream file(m_path.string(), ios::trunc); + file << m_test->source(); + file << "// ----" << endl; + if (!m_test->errorList().empty()) + m_test->printErrorList(file, m_test->errorList(), "// ", false, false, false); + return Request::Rerun; + } + case 'e': + cout << endl << endl; + if (system((editor + " \"" + m_path.string() + "\"").c_str())) + cerr << "Error running editor command." << endl << endl; + return Request::Rerun; + case 'q': + cout << endl; + return Request::Quit; + default: + break; + } + } +} + + +SyntaxTestStats SyntaxTestTool::processPath( + fs::path const& _basepath, + fs::path const& _path, + bool const _formatted +) +{ + std::queue paths; + paths.push(_path); + int successCount = 0; + int runCount = 0; + + while (!paths.empty()) + { + auto currentPath = paths.front(); + + fs::path fullpath = _basepath / currentPath; + if (fs::is_directory(fullpath)) + { + paths.pop(); + for (auto const& entry: boost::iterator_range( + fs::directory_iterator(fullpath), + fs::directory_iterator() + )) + paths.push(currentPath / entry.path().filename()); + } + else + { + SyntaxTestTool testTool(currentPath.string(), fullpath, _formatted); + ++runCount; + auto result = testTool.process(); + + switch(result) + { + case Result::Failure: + case Result::ParserError: + switch(testTool.handleResponse(result == Result::ParserError)) + { + case Request::Quit: + return { successCount, runCount }; + case Request::Rerun: + cout << "Re-running test case..." << endl; + --runCount; + break; + case Request::Skip: + paths.pop(); + break; + } + break; + case Result::Success: + paths.pop(); + ++successCount; + break; + default: + // non-recoverable error; continue with next test case + paths.pop(); + break; + } + } + } + + return { successCount, runCount }; + +} + +int main(int argc, char *argv[]) +{ + if (getenv("EDITOR")) + SyntaxTestTool::editor = getenv("EDITOR"); + + fs::path testPath; + bool formatted = true; + po::options_description options( + R"(isoltest, tool for interactively managing test contracts. +Usage: isoltest [Options] --testpath path +Interactively validates test contracts. + +Allowed options)", + po::options_description::m_default_line_length, + po::options_description::m_default_line_length - 23); + options.add_options() + ("help", "Show this help screen.") + ("testpath", po::value(&testPath), "path to test files") + ("no-color", "don't use colors") + ("editor", po::value(&SyntaxTestTool::editor), "editor for opening contracts"); + + po::variables_map arguments; + try + { + po::command_line_parser cmdLineParser(argc, argv); + cmdLineParser.options(options); + po::store(cmdLineParser.run(), arguments); + + if (arguments.count("help")) + { + cout << options << endl; + return 0; + } + + if (arguments.count("no-color")) + formatted = false; + + po::notify(arguments); + } + catch (po::error 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; + } + } + } + + fs::path syntaxTestPath = testPath / "libsolidity" / "syntaxTests"; + + if (fs::exists(syntaxTestPath) && fs::is_directory(syntaxTestPath)) + { + auto stats = SyntaxTestTool::processPath(testPath / "libsolidity", "syntaxTests", formatted); + + cout << endl << "Summary: "; + FormattedScope(cout, formatted, {BOLD, stats ? GREEN : RED}) << + stats.successCount << "/" << stats.runCount; + cout << " tests successful." << endl; + + return stats ? 0 : 1; + } + else + { + cerr << "Test path not found. Use the --testpath argument." << endl; + return 1; + } +} From 50ad89d369a3ffd90471b2738f2f30491841e197 Mon Sep 17 00:00:00 2001 From: Daniel Kirchner Date: Thu, 15 Mar 2018 16:27:34 +0100 Subject: [PATCH 034/197] Only colour error type, not error message in isoltest. --- test/libsolidity/SyntaxTest.cpp | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/test/libsolidity/SyntaxTest.cpp b/test/libsolidity/SyntaxTest.cpp index 70abac48a..acfdff294 100644 --- a/test/libsolidity/SyntaxTest.cpp +++ b/test/libsolidity/SyntaxTest.cpp @@ -78,8 +78,11 @@ void SyntaxTest::printExpected(ostream& _stream, string const& _linePrefix, bool FormattedScope(_stream, _formatted, {BOLD, GREEN}) << _linePrefix << "Success" << endl; else for (auto const& expectation: m_expectations) + { FormattedScope(_stream, _formatted, {BOLD, expectation.type == "Warning" ? YELLOW : RED}) << - _linePrefix << expectation.type << ": " << expectation.message << endl; + _linePrefix << expectation.type << ": "; + _stream << expectation.message << endl; + } } void SyntaxTest::printErrorList( @@ -99,17 +102,20 @@ void SyntaxTest::printErrorList( bool isWarning = (error->type() == Error::Type::Warning); if (isWarning && _ignoreWarnings) continue; - FormattedScope scope(_stream, _formatted, {BOLD, isWarning ? YELLOW : RED}); - _stream << _linePrefix; - if (_lineNumbers) { - int line = offsetToLineNumber( - boost::get_error_info(*error)->start - ); - if (line >= 0) - _stream << "(" << line << "): "; + FormattedScope scope(_stream, _formatted, {BOLD, isWarning ? YELLOW : RED}); + _stream << _linePrefix; + if (_lineNumbers) + { + int line = offsetToLineNumber( + boost::get_error_info(*error)->start + ); + if (line >= 0) + _stream << "(" << line << "): "; + } + _stream << error->typeName() << ": "; } - _stream << error->typeName() << ": " << errorMessage(*error) << endl; + _stream << errorMessage(*error) << endl; } } From ea8d5f8afc8cdd886be70efc8f49163af0675014 Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 15 Mar 2018 18:20:06 +0100 Subject: [PATCH 035/197] Use /usr/bin/editor if exists. --- test/tools/isoltest.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/tools/isoltest.cpp b/test/tools/isoltest.cpp index 668481cf8..b71f14b92 100644 --- a/test/tools/isoltest.cpp +++ b/test/tools/isoltest.cpp @@ -263,6 +263,8 @@ int main(int argc, char *argv[]) { if (getenv("EDITOR")) SyntaxTestTool::editor = getenv("EDITOR"); + else if (fs::exists("/usr/bin/editor")) + SyntaxTestTool::editor = "/usr/bin/editor"; fs::path testPath; bool formatted = true; From 0a4d4d6c528ef57a9391f7f8edf302489ea54bdb Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 15 Mar 2018 23:06:19 +0100 Subject: [PATCH 036/197] Remove ccache on circle. --- circle.yml | 18 +----------------- 1 file changed, 1 insertion(+), 17 deletions(-) diff --git a/circle.yml b/circle.yml index 49c73ce4d..2b34b1aa9 100644 --- a/circle.yml +++ b/circle.yml @@ -89,20 +89,12 @@ jobs: name: Install build dependencies command: | apt-get -qq update - apt-get -qy install ccache cmake libboost-all-dev libz3-dev + apt-get -qy install cmake libboost-regex-dev libboost-filesystem-dev libboost-test-dev libboost-system-dev libboost-program-options-dev libz3-dev - run: 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 - - restore_cache: - key: ccache-{{ arch }}-{{ .Branch }}-{{ .Environment.CIRCLE_SHA1 }} - key: ccache-{{ arch }}-{{ .Branch }}- - key: ccache-{{ arch }}-develop- - key: ccache-{{ arch }}- - - run: - name: Configure ccache - command: ccache -M 200M && ccache -c && ccache -s && ccache -z - run: name: Build command: | @@ -110,14 +102,6 @@ jobs: cd build cmake .. -DCMAKE_BUILD_TYPE=RelWithDebInfo make -j4 - - run: - name: CCache statistics - command: ccache -s - - save_cache: - key: ccache-{{ arch }}-{{ .Branch }}-{{ .Environment.CIRCLE_SHA1 }} - when: always - paths: - - ~/.ccache - store_artifacts: path: build/solc/solc destination: solc From 658955579050c51cc9a714adc5986212796f8196 Mon Sep 17 00:00:00 2001 From: chriseth Date: Fri, 16 Mar 2018 00:46:57 +0100 Subject: [PATCH 037/197] Test extraction tool. --- scripts/extract_test_cases.py | 49 +++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100755 scripts/extract_test_cases.py diff --git a/scripts/extract_test_cases.py b/scripts/extract_test_cases.py new file mode 100755 index 000000000..07ef9a96a --- /dev/null +++ b/scripts/extract_test_cases.py @@ -0,0 +1,49 @@ +#!/usr/bin/python +# +# This script reads C++ or RST source files and writes all +# multi-line strings into individual files. +# This can be used to extract the Solidity test cases +# into files for e.g. fuzz testing as +# scripts/isolate_tests.py test/libsolidity/* + +import sys +import re +import os +import hashlib +from os.path import join + +def extract_test_cases(path): + lines = open(path, 'rb').read().splitlines() + + inside = False + delimiter = '' + test = '' + + ctr = 1 + test_name = '' + + for l in lines: + if inside: + if l.strip().endswith(')' + delimiter + '";'): + open('%03d_%s.sol' % (ctr, test_name), 'wb').write(test) + ctr += 1 + inside = False + test = '' + else: + l = re.sub('^\t\t', '', l) + l = l.replace('\t', ' ') + test += l + '\n' + else: + m = re.search(r'BOOST_AUTO_TEST_CASE\(([^(]*)\)', l.strip()) + if m: + test_name = m.group(1) + m = re.search(r'R"([^(]*)\($', l.strip()) + if m: + inside = True + delimiter = m.group(1) + + +if __name__ == '__main__': + path = sys.argv[1] + extract_test_cases(path) + From c9db105ad7c8cb4913d0e0ed859ce4808aae288a Mon Sep 17 00:00:00 2001 From: chriseth Date: Fri, 16 Mar 2018 00:47:12 +0100 Subject: [PATCH 038/197] Extract scoping tests. --- .../SolidityNameAndTypeResolution.cpp | 223 ------------------ .../scoping/double_function_declaration.sol | 6 + ...le_variable_declaration_disjoint_scope.sol | 8 + ...ariable_declaration_disjoint_scope_050.sol | 10 + ..._declaration_disjoint_scope_activation.sol | 8 + ...laration_disjoint_scope_activation_050.sol | 10 + .../syntaxTests/scoping/name_shadowing.sol | 7 + .../syntaxTests/scoping/scoping.sol | 11 + .../scoping/scoping_activation.sol | 9 + .../scoping/scoping_activation_old.sol | 6 + .../syntaxTests/scoping/scoping_for.sol | 8 + .../syntaxTests/scoping/scoping_for2.sol | 7 + .../syntaxTests/scoping/scoping_for3.sol | 11 + .../scoping/scoping_for_decl_in_body.sol | 10 + .../syntaxTests/scoping/scoping_old.sol | 6 + .../syntaxTests/scoping/scoping_self_use.sol | 5 + .../scoping/scoping_self_use_050.sol | 8 + 17 files changed, 130 insertions(+), 223 deletions(-) create mode 100644 test/libsolidity/syntaxTests/scoping/double_function_declaration.sol create mode 100644 test/libsolidity/syntaxTests/scoping/double_variable_declaration_disjoint_scope.sol create mode 100644 test/libsolidity/syntaxTests/scoping/double_variable_declaration_disjoint_scope_050.sol create mode 100644 test/libsolidity/syntaxTests/scoping/double_variable_declaration_disjoint_scope_activation.sol create mode 100644 test/libsolidity/syntaxTests/scoping/double_variable_declaration_disjoint_scope_activation_050.sol create mode 100644 test/libsolidity/syntaxTests/scoping/name_shadowing.sol create mode 100644 test/libsolidity/syntaxTests/scoping/scoping.sol create mode 100644 test/libsolidity/syntaxTests/scoping/scoping_activation.sol create mode 100644 test/libsolidity/syntaxTests/scoping/scoping_activation_old.sol create mode 100644 test/libsolidity/syntaxTests/scoping/scoping_for.sol create mode 100644 test/libsolidity/syntaxTests/scoping/scoping_for2.sol create mode 100644 test/libsolidity/syntaxTests/scoping/scoping_for3.sol create mode 100644 test/libsolidity/syntaxTests/scoping/scoping_for_decl_in_body.sol create mode 100644 test/libsolidity/syntaxTests/scoping/scoping_old.sol create mode 100644 test/libsolidity/syntaxTests/scoping/scoping_self_use.sol create mode 100644 test/libsolidity/syntaxTests/scoping/scoping_self_use_050.sol diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index c757037c7..2abc0fc75 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -43,229 +43,6 @@ namespace test BOOST_FIXTURE_TEST_SUITE(SolidityNameAndTypeResolution, AnalysisFramework) - -BOOST_AUTO_TEST_CASE(double_function_declaration) -{ - char const* text = R"( - contract test { - function fun() public { } - function fun() public { } - } - )"; - CHECK_ERROR(text, DeclarationError, "Function with same name and arguments defined twice."); -} - -BOOST_AUTO_TEST_CASE(double_variable_declaration_disjoint_scope) -{ - string text = R"( - contract test { - function f() pure public { - { uint x; } - { uint x; } - } - } - )"; - CHECK_ERROR(text, DeclarationError, "Identifier already declared"); -} - -BOOST_AUTO_TEST_CASE(double_variable_declaration_disjoint_scope_050) -{ - string text = R"( - pragma experimental "v0.5.0"; - contract test { - function f() pure public { - { uint x; } - { uint x; } - } - } - )"; - CHECK_WARNING_ALLOW_MULTI(text, (vector{ - "Unused local variable", - "Unused local variable" - })); -} - -BOOST_AUTO_TEST_CASE(double_variable_declaration_disjoint_scope_activation) -{ - string text = R"( - contract test { - function f() pure public { - { uint x; } - uint x; - } - } - )"; - CHECK_ERROR(text, DeclarationError, "Identifier already declared"); -} - -BOOST_AUTO_TEST_CASE(double_variable_declaration_disjoint_scope_activation_050) -{ - string text = R"( - pragma experimental "v0.5.0"; - contract test { - function f() pure public { - { uint x; } - uint x; - } - } - )"; - CHECK_WARNING_ALLOW_MULTI(text, (vector{ - "Unused local variable", - "Unused local variable" - })); -} -BOOST_AUTO_TEST_CASE(scoping_old) -{ - char const* text = R"( - contract test { - function f() pure public { - x = 4; - uint256 x = 2; - } - } - )"; - CHECK_SUCCESS_NO_WARNINGS(text); -} - -BOOST_AUTO_TEST_CASE(scoping) -{ - char const* text = R"( - pragma experimental "v0.5.0"; - contract test { - function f() public { - { - uint256 x; - } - x = 2; - } - } - )"; - CHECK_ERROR(text, DeclarationError, "Undeclared identifier"); -} - -BOOST_AUTO_TEST_CASE(scoping_activation_old) -{ - char const* text = R"( - contract test { - function f() pure public { - x = 3; - uint x; - } - } - )"; - CHECK_SUCCESS_NO_WARNINGS(text); -} - -BOOST_AUTO_TEST_CASE(scoping_activation) -{ - char const* text = R"( - pragma experimental "v0.5.0"; - contract test { - function f() pure public { - x = 3; - uint x; - } - } - )"; - CHECK_ERROR(text, DeclarationError, "Undeclared identifier"); -} - -BOOST_AUTO_TEST_CASE(scoping_self_use) -{ - char const* text = R"( - contract test { - function f() pure public { - uint a = a; - } - } - )"; - CHECK_SUCCESS_NO_WARNINGS(text); -} - -BOOST_AUTO_TEST_CASE(scoping_self_use_050) -{ - char const* text = R"( - pragma experimental "v0.5.0"; - contract test { - function f() pure public { - uint a = a; - } - } - )"; - CHECK_ERROR(text, DeclarationError, "Undeclared identifier"); -} - -BOOST_AUTO_TEST_CASE(scoping_for) -{ - char const* text = R"( - pragma experimental "v0.5.0"; - contract test { - function f() pure public { - for (uint x = 0; x < 10; x ++){ - x = 2; - } - } - } - )"; - CHECK_SUCCESS_NO_WARNINGS(text); -} - -BOOST_AUTO_TEST_CASE(scoping_for2) -{ - char const* text = R"( - pragma experimental "v0.5.0"; - contract test { - function f() pure public { - for (uint x = 0; x < 10; x ++) - x = 2; - } - } - )"; - CHECK_SUCCESS_NO_WARNINGS(text); -} - -BOOST_AUTO_TEST_CASE(scoping_for3) -{ - char const* text = R"( - pragma experimental "v0.5.0"; - contract test { - function f() pure public { - for (uint x = 0; x < 10; x ++){ - x = 2; - } - x = 4; - } - } - )"; - CHECK_ERROR(text, DeclarationError, "Undeclared identifier"); -} - -BOOST_AUTO_TEST_CASE(scoping_for_decl_in_body) -{ - char const* text = R"( - pragma experimental "v0.5.0"; - contract test { - function f() pure public { - for (;; y++){ - uint y = 3; - } - } - } - )"; - CHECK_ERROR(text, DeclarationError, "Undeclared identifier"); -} - -BOOST_AUTO_TEST_CASE(name_shadowing) -{ - char const* text = R"( - contract test { - uint256 variable; - function f() public { uint32 variable; variable = 2; } - } - )"; - CHECK_SUCCESS(text); -} - BOOST_AUTO_TEST_CASE(name_references) { char const* text = R"( diff --git a/test/libsolidity/syntaxTests/scoping/double_function_declaration.sol b/test/libsolidity/syntaxTests/scoping/double_function_declaration.sol new file mode 100644 index 000000000..2841fd382 --- /dev/null +++ b/test/libsolidity/syntaxTests/scoping/double_function_declaration.sol @@ -0,0 +1,6 @@ +contract test { + function fun() public { } + function fun() public { } +} +// ---- +// DeclarationError: Function with same name and arguments defined twice. diff --git a/test/libsolidity/syntaxTests/scoping/double_variable_declaration_disjoint_scope.sol b/test/libsolidity/syntaxTests/scoping/double_variable_declaration_disjoint_scope.sol new file mode 100644 index 000000000..ea61d0f37 --- /dev/null +++ b/test/libsolidity/syntaxTests/scoping/double_variable_declaration_disjoint_scope.sol @@ -0,0 +1,8 @@ +contract test { + function f() pure public { + { uint x; } + { uint x; } + } +} +// ---- +// DeclarationError: Identifier already declared. 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 new file mode 100644 index 000000000..221959635 --- /dev/null +++ b/test/libsolidity/syntaxTests/scoping/double_variable_declaration_disjoint_scope_050.sol @@ -0,0 +1,10 @@ +pragma experimental "v0.5.0"; +contract test { + function f() pure public { + { uint x; } + { uint x; } + } +} +// ---- +// Warning: Unused local variable. +// Warning: 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 new file mode 100644 index 000000000..6af89c93d --- /dev/null +++ b/test/libsolidity/syntaxTests/scoping/double_variable_declaration_disjoint_scope_activation.sol @@ -0,0 +1,8 @@ +contract test { + function f() pure public { + { uint x; } + uint x; + } +} +// ---- +// DeclarationError: Identifier already declared. 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 new file mode 100644 index 000000000..73cddfed5 --- /dev/null +++ b/test/libsolidity/syntaxTests/scoping/double_variable_declaration_disjoint_scope_activation_050.sol @@ -0,0 +1,10 @@ +pragma experimental "v0.5.0"; +contract test { + function f() pure public { + { uint x; } + uint x; + } +} +// ---- +// Warning: Unused local variable. +// Warning: Unused local variable. diff --git a/test/libsolidity/syntaxTests/scoping/name_shadowing.sol b/test/libsolidity/syntaxTests/scoping/name_shadowing.sol new file mode 100644 index 000000000..d16877f95 --- /dev/null +++ b/test/libsolidity/syntaxTests/scoping/name_shadowing.sol @@ -0,0 +1,7 @@ +contract test { + uint256 variable; + function f() pure public { uint32 variable; variable = 2; } +} +// ---- +// Warning: This declaration shadows an existing declaration. + diff --git a/test/libsolidity/syntaxTests/scoping/scoping.sol b/test/libsolidity/syntaxTests/scoping/scoping.sol new file mode 100644 index 000000000..f47a3e99e --- /dev/null +++ b/test/libsolidity/syntaxTests/scoping/scoping.sol @@ -0,0 +1,11 @@ +pragma experimental "v0.5.0"; +contract test { + function f() public { + { + uint256 x; + } + x = 2; + } +} +// ---- +// DeclarationError: Undeclared identifier. diff --git a/test/libsolidity/syntaxTests/scoping/scoping_activation.sol b/test/libsolidity/syntaxTests/scoping/scoping_activation.sol new file mode 100644 index 000000000..0ed74a00d --- /dev/null +++ b/test/libsolidity/syntaxTests/scoping/scoping_activation.sol @@ -0,0 +1,9 @@ +pragma experimental "v0.5.0"; +contract test { + function f() pure public { + x = 3; + uint x; + } +} +// ---- +// DeclarationError: Undeclared identifier. Did you mean "x"? diff --git a/test/libsolidity/syntaxTests/scoping/scoping_activation_old.sol b/test/libsolidity/syntaxTests/scoping/scoping_activation_old.sol new file mode 100644 index 000000000..d893a889a --- /dev/null +++ b/test/libsolidity/syntaxTests/scoping/scoping_activation_old.sol @@ -0,0 +1,6 @@ +contract test { + function f() pure public { + x = 3; + uint x; + } +} diff --git a/test/libsolidity/syntaxTests/scoping/scoping_for.sol b/test/libsolidity/syntaxTests/scoping/scoping_for.sol new file mode 100644 index 000000000..6e5b7095e --- /dev/null +++ b/test/libsolidity/syntaxTests/scoping/scoping_for.sol @@ -0,0 +1,8 @@ +pragma experimental "v0.5.0"; +contract test { + function f() pure public { + for (uint x = 0; x < 10; x ++){ + x = 2; + } + } +} diff --git a/test/libsolidity/syntaxTests/scoping/scoping_for2.sol b/test/libsolidity/syntaxTests/scoping/scoping_for2.sol new file mode 100644 index 000000000..eb74b8ab4 --- /dev/null +++ b/test/libsolidity/syntaxTests/scoping/scoping_for2.sol @@ -0,0 +1,7 @@ +pragma experimental "v0.5.0"; +contract test { + function f() pure public { + for (uint x = 0; x < 10; x ++) + x = 2; + } +} diff --git a/test/libsolidity/syntaxTests/scoping/scoping_for3.sol b/test/libsolidity/syntaxTests/scoping/scoping_for3.sol new file mode 100644 index 000000000..9bc7d5695 --- /dev/null +++ b/test/libsolidity/syntaxTests/scoping/scoping_for3.sol @@ -0,0 +1,11 @@ +pragma experimental "v0.5.0"; +contract test { + function f() pure public { + for (uint x = 0; x < 10; x ++){ + x = 2; + } + x = 4; + } +} +// ---- +// DeclarationError: 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 new file mode 100644 index 000000000..07503983e --- /dev/null +++ b/test/libsolidity/syntaxTests/scoping/scoping_for_decl_in_body.sol @@ -0,0 +1,10 @@ +pragma experimental "v0.5.0"; +contract test { + function f() pure public { + for (;; y++){ + uint y = 3; + } + } +} +// ---- +// DeclarationError: Undeclared identifier. diff --git a/test/libsolidity/syntaxTests/scoping/scoping_old.sol b/test/libsolidity/syntaxTests/scoping/scoping_old.sol new file mode 100644 index 000000000..83f6b60ba --- /dev/null +++ b/test/libsolidity/syntaxTests/scoping/scoping_old.sol @@ -0,0 +1,6 @@ +contract test { + function f() pure public { + x = 4; + uint256 x = 2; + } +} diff --git a/test/libsolidity/syntaxTests/scoping/scoping_self_use.sol b/test/libsolidity/syntaxTests/scoping/scoping_self_use.sol new file mode 100644 index 000000000..9e2c0171b --- /dev/null +++ b/test/libsolidity/syntaxTests/scoping/scoping_self_use.sol @@ -0,0 +1,5 @@ +contract test { + function f() pure public { + uint a = a; + } +} diff --git a/test/libsolidity/syntaxTests/scoping/scoping_self_use_050.sol b/test/libsolidity/syntaxTests/scoping/scoping_self_use_050.sol new file mode 100644 index 000000000..e942020e8 --- /dev/null +++ b/test/libsolidity/syntaxTests/scoping/scoping_self_use_050.sol @@ -0,0 +1,8 @@ +pragma experimental "v0.5.0"; +contract test { + function f() pure public { + uint a = a; + } +} +// ---- +// DeclarationError: Undeclared identifier. Did you mean "a"? From e68c19c47b03eebb9af528bf2b03bb0086484c63 Mon Sep 17 00:00:00 2001 From: Daniel Kirchner Date: Fri, 16 Mar 2018 11:40:03 +0100 Subject: [PATCH 039/197] Only consider files ending with .sol and not starting with ~ in syntax tests. --- test/libsolidity/SyntaxTest.cpp | 10 +++++++++- test/libsolidity/SyntaxTest.h | 1 + test/tools/isoltest.cpp | 4 +++- 3 files changed, 13 insertions(+), 2 deletions(-) diff --git a/test/libsolidity/SyntaxTest.cpp b/test/libsolidity/SyntaxTest.cpp index acfdff294..ca0511384 100644 --- a/test/libsolidity/SyntaxTest.cpp +++ b/test/libsolidity/SyntaxTest.cpp @@ -205,6 +205,13 @@ test_case *make_test_case( } #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, @@ -220,7 +227,8 @@ int SyntaxTest::registerTests( fs::directory_iterator(fullpath), fs::directory_iterator() )) - numTestsAdded += registerTests(*sub_suite, _basepath, _path / entry.path().filename()); + if (fs::is_directory(entry.path()) || isTestFilename(entry.path().filename())) + numTestsAdded += registerTests(*sub_suite, _basepath, _path / entry.path().filename()); _suite.add(sub_suite); } else diff --git a/test/libsolidity/SyntaxTest.h b/test/libsolidity/SyntaxTest.h index 441cc4f82..cb6ee05cd 100644 --- a/test/libsolidity/SyntaxTest.h +++ b/test/libsolidity/SyntaxTest.h @@ -71,6 +71,7 @@ public: boost::filesystem::path const& _basepath, boost::filesystem::path const& _path ); + static bool isTestFilename(boost::filesystem::path const& _filename); private: bool matchesExpectations(ErrorList const& _errors) const; static std::string errorMessage(Error const& _e); diff --git a/test/tools/isoltest.cpp b/test/tools/isoltest.cpp index 668481cf8..5ad3bfb5d 100644 --- a/test/tools/isoltest.cpp +++ b/test/tools/isoltest.cpp @@ -19,6 +19,7 @@ #include #include +#include #include #include #include @@ -218,7 +219,8 @@ SyntaxTestStats SyntaxTestTool::processPath( fs::directory_iterator(fullpath), fs::directory_iterator() )) - paths.push(currentPath / entry.path().filename()); + if (fs::is_directory(entry.path()) || SyntaxTest::isTestFilename(entry.path().filename())) + paths.push(currentPath / entry.path().filename()); } else { From 6e730df036f6672e07cf96189a8cf721bf7d0cc4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mat=C3=ADas=20A=2E=20R=C3=A9=20Medina?= Date: Mon, 19 Mar 2018 16:38:20 -0300 Subject: [PATCH 040/197] Fix: Missing payable at function forceOwnerChange forceOwnerChange expects ether, and does not have the payable keyword. --- docs/common-patterns.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/common-patterns.rst b/docs/common-patterns.rst index 7e09f5346..c62b5acab 100644 --- a/docs/common-patterns.rst +++ b/docs/common-patterns.rst @@ -194,6 +194,7 @@ restrictions highly readable. function forceOwnerChange(address _newOwner) public + payable costs(200 ether) { owner = _newOwner; From 2cdf44f65cdd15f31293ae2fa78d9996d6219af0 Mon Sep 17 00:00:00 2001 From: chriseth Date: Tue, 20 Feb 2018 10:13:58 +0100 Subject: [PATCH 041/197] Move the old ABI decoder code. --- libsolidity/codegen/CompilerUtils.cpp | 99 ++++++++++++++++++++++ libsolidity/codegen/CompilerUtils.h | 5 ++ libsolidity/codegen/ContractCompiler.cpp | 103 +---------------------- libsolidity/codegen/ContractCompiler.h | 4 - 4 files changed, 106 insertions(+), 105 deletions(-) diff --git a/libsolidity/codegen/CompilerUtils.cpp b/libsolidity/codegen/CompilerUtils.cpp index 533aca5cb..704d8da88 100644 --- a/libsolidity/codegen/CompilerUtils.cpp +++ b/libsolidity/codegen/CompilerUtils.cpp @@ -159,6 +159,105 @@ void CompilerUtils::storeInMemoryDynamic(Type const& _type, bool _padToWordBound } } +void CompilerUtils::abiDecode(TypePointers const& _typeParameters, bool _fromMemory) +{ + // We do not check the calldata size, everything is zero-padded + + if (m_context.experimentalFeatureActive(ExperimentalFeature::ABIEncoderV2)) + { + // Use the new JULIA-based decoding function + auto stackHeightBefore = m_context.stackHeight(); + abiDecodeV2(_typeParameters, _fromMemory); + solAssert(m_context.stackHeight() - stackHeightBefore == sizeOnStack(_typeParameters) - 1, ""); + return; + } + + //@todo this does not yet support nested dynamic arrays + + // Retain the offset pointer as base_offset, the point from which the data offsets are computed. + m_context << Instruction::DUP1; + for (TypePointer const& parameterType: _typeParameters) + { + // stack: v1 v2 ... v(k-1) base_offset current_offset + TypePointer type = parameterType->decodingType(); + solUnimplementedAssert(type, "No decoding type found."); + if (type->category() == Type::Category::Array) + { + auto const& arrayType = dynamic_cast(*type); + solUnimplementedAssert(!arrayType.baseType()->isDynamicallySized(), "Nested arrays not yet implemented."); + if (_fromMemory) + { + solUnimplementedAssert( + arrayType.baseType()->isValueType(), + "Nested memory arrays not yet implemented here." + ); + // @todo If base type is an array or struct, it is still calldata-style encoded, so + // we would have to convert it like below. + solAssert(arrayType.location() == DataLocation::Memory, ""); + if (arrayType.isDynamicallySized()) + { + // compute data pointer + m_context << Instruction::DUP1 << Instruction::MLOAD; + m_context << Instruction::DUP3 << Instruction::ADD; + m_context << Instruction::SWAP2 << Instruction::SWAP1; + m_context << u256(0x20) << Instruction::ADD; + } + else + { + m_context << Instruction::SWAP1 << Instruction::DUP2; + m_context << u256(arrayType.calldataEncodedSize(true)) << Instruction::ADD; + } + } + else + { + // first load from calldata and potentially convert to memory if arrayType is memory + TypePointer calldataType = arrayType.copyForLocation(DataLocation::CallData, false); + if (calldataType->isDynamicallySized()) + { + // put on stack: data_pointer length + loadFromMemoryDynamic(IntegerType(256), !_fromMemory); + // stack: base_offset data_offset next_pointer + m_context << Instruction::SWAP1 << Instruction::DUP3 << Instruction::ADD; + // stack: base_offset next_pointer data_pointer + // retrieve length + loadFromMemoryDynamic(IntegerType(256), !_fromMemory, true); + // stack: base_offset next_pointer length data_pointer + m_context << Instruction::SWAP2; + // stack: base_offset data_pointer length next_pointer + } + else + { + // leave the pointer on the stack + m_context << Instruction::DUP1; + m_context << u256(calldataType->calldataEncodedSize()) << Instruction::ADD; + } + if (arrayType.location() == DataLocation::Memory) + { + // stack: base_offset calldata_ref [length] next_calldata + // copy to memory + // move calldata type up again + moveIntoStack(calldataType->sizeOnStack()); + convertType(*calldataType, arrayType, false, false, true); + // fetch next pointer again + moveToStackTop(arrayType.sizeOnStack()); + } + // move base_offset up + moveToStackTop(1 + arrayType.sizeOnStack()); + m_context << Instruction::SWAP1; + } + } + else + { + solAssert(!type->isDynamicallySized(), "Unknown dynamically sized type: " + type->toString()); + loadFromMemoryDynamic(*type, !_fromMemory, true); + moveToStackTop(1 + type->sizeOnStack()); + m_context << Instruction::SWAP1; + } + // stack: v1 v2 ... v(k-1) v(k) base_offset mem_offset + } + m_context << Instruction::POP << Instruction::POP; +} + void CompilerUtils::encodeToMemory( TypePointers const& _givenTypes, TypePointers const& _targetTypes, diff --git a/libsolidity/codegen/CompilerUtils.h b/libsolidity/codegen/CompilerUtils.h index 3cde281bf..9fc97b9e3 100644 --- a/libsolidity/codegen/CompilerUtils.h +++ b/libsolidity/codegen/CompilerUtils.h @@ -88,6 +88,11 @@ public: /// Stack post: (memory_offset+length) void storeInMemoryDynamic(Type const& _type, bool _padToWords = true); + /// 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. + /// Expects source offset on the stack, which is removed. + 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. /// Removes the values from the stack and leaves the updated memory pointer. diff --git a/libsolidity/codegen/ContractCompiler.cpp b/libsolidity/codegen/ContractCompiler.cpp index 95d6c8b5c..480db98ec 100644 --- a/libsolidity/codegen/ContractCompiler.cpp +++ b/libsolidity/codegen/ContractCompiler.cpp @@ -280,7 +280,7 @@ void ContractCompiler::appendConstructor(FunctionDefinition const& _constructor) m_context << Instruction::DUP2 << Instruction::ADD; CompilerUtils(m_context).storeFreeMemoryPointer(); // stack: - appendCalldataUnpacker(FunctionType(_constructor).parameterTypes(), true); + CompilerUtils(m_context).abiDecode(FunctionType(_constructor).parameterTypes(), true); } _constructor.accept(*this); } @@ -367,7 +367,7 @@ void ContractCompiler::appendFunctionSelector(ContractDefinition const& _contrac { // Parameter for calldataUnpacker m_context << CompilerUtils::dataStartOffset; - appendCalldataUnpacker(functionType->parameterTypes()); + CompilerUtils(m_context).abiDecode(functionType->parameterTypes()); } m_context.appendJumpTo(m_context.functionEntryLabel(functionType->declaration())); m_context << returnTag; @@ -382,105 +382,6 @@ void ContractCompiler::appendFunctionSelector(ContractDefinition const& _contrac } } -void ContractCompiler::appendCalldataUnpacker(TypePointers const& _typeParameters, bool _fromMemory) -{ - // We do not check the calldata size, everything is zero-padded - - if (m_context.experimentalFeatureActive(ExperimentalFeature::ABIEncoderV2)) - { - // Use the new JULIA-based decoding function - auto stackHeightBefore = m_context.stackHeight(); - CompilerUtils(m_context).abiDecodeV2(_typeParameters, _fromMemory); - solAssert(m_context.stackHeight() - stackHeightBefore == CompilerUtils(m_context).sizeOnStack(_typeParameters) - 1, ""); - return; - } - - //@todo this does not yet support nested dynamic arrays - - // Retain the offset pointer as base_offset, the point from which the data offsets are computed. - m_context << Instruction::DUP1; - for (TypePointer const& parameterType: _typeParameters) - { - // stack: v1 v2 ... v(k-1) base_offset current_offset - TypePointer type = parameterType->decodingType(); - solUnimplementedAssert(type, "No decoding type found."); - if (type->category() == Type::Category::Array) - { - auto const& arrayType = dynamic_cast(*type); - solUnimplementedAssert(!arrayType.baseType()->isDynamicallySized(), "Nested arrays not yet implemented."); - if (_fromMemory) - { - solUnimplementedAssert( - arrayType.baseType()->isValueType(), - "Nested memory arrays not yet implemented here." - ); - // @todo If base type is an array or struct, it is still calldata-style encoded, so - // we would have to convert it like below. - solAssert(arrayType.location() == DataLocation::Memory, ""); - if (arrayType.isDynamicallySized()) - { - // compute data pointer - m_context << Instruction::DUP1 << Instruction::MLOAD; - m_context << Instruction::DUP3 << Instruction::ADD; - m_context << Instruction::SWAP2 << Instruction::SWAP1; - m_context << u256(0x20) << Instruction::ADD; - } - else - { - m_context << Instruction::SWAP1 << Instruction::DUP2; - m_context << u256(arrayType.calldataEncodedSize(true)) << Instruction::ADD; - } - } - else - { - // first load from calldata and potentially convert to memory if arrayType is memory - TypePointer calldataType = arrayType.copyForLocation(DataLocation::CallData, false); - if (calldataType->isDynamicallySized()) - { - // put on stack: data_pointer length - CompilerUtils(m_context).loadFromMemoryDynamic(IntegerType(256), !_fromMemory); - // stack: base_offset data_offset next_pointer - m_context << Instruction::SWAP1 << Instruction::DUP3 << Instruction::ADD; - // stack: base_offset next_pointer data_pointer - // retrieve length - CompilerUtils(m_context).loadFromMemoryDynamic(IntegerType(256), !_fromMemory, true); - // stack: base_offset next_pointer length data_pointer - m_context << Instruction::SWAP2; - // stack: base_offset data_pointer length next_pointer - } - else - { - // leave the pointer on the stack - m_context << Instruction::DUP1; - m_context << u256(calldataType->calldataEncodedSize()) << Instruction::ADD; - } - if (arrayType.location() == DataLocation::Memory) - { - // stack: base_offset calldata_ref [length] next_calldata - // copy to memory - // move calldata type up again - CompilerUtils(m_context).moveIntoStack(calldataType->sizeOnStack()); - CompilerUtils(m_context).convertType(*calldataType, arrayType, false, false, true); - // fetch next pointer again - CompilerUtils(m_context).moveToStackTop(arrayType.sizeOnStack()); - } - // move base_offset up - CompilerUtils(m_context).moveToStackTop(1 + arrayType.sizeOnStack()); - m_context << Instruction::SWAP1; - } - } - else - { - solAssert(!type->isDynamicallySized(), "Unknown dynamically sized type: " + type->toString()); - CompilerUtils(m_context).loadFromMemoryDynamic(*type, !_fromMemory, true); - CompilerUtils(m_context).moveToStackTop(1 + type->sizeOnStack()); - m_context << Instruction::SWAP1; - } - // stack: v1 v2 ... v(k-1) v(k) base_offset mem_offset - } - m_context << Instruction::POP << Instruction::POP; -} - void ContractCompiler::appendReturnValuePacker(TypePointers const& _typeParameters, bool _isLibrary) { CompilerUtils utils(m_context); diff --git a/libsolidity/codegen/ContractCompiler.h b/libsolidity/codegen/ContractCompiler.h index 8559ea587..e04a56fb1 100644 --- a/libsolidity/codegen/ContractCompiler.h +++ b/libsolidity/codegen/ContractCompiler.h @@ -90,10 +90,6 @@ private: void appendDelegatecallCheck(); void appendFunctionSelector(ContractDefinition const& _contract); void appendCallValueCheck(); - /// Creates code that unpacks the arguments for the given function represented by a vector of TypePointers. - /// From memory if @a _fromMemory is true, otherwise from call data. - /// Expects source offset on the stack, which is removed. - void appendCalldataUnpacker(TypePointers const& _typeParameters, bool _fromMemory = false); void appendReturnValuePacker(TypePointers const& _typeParameters, bool _isLibrary); void registerStateVariables(ContractDefinition const& _contract); From 32c94f505901201126000eb12087251f5695acbd Mon Sep 17 00:00:00 2001 From: chriseth Date: Wed, 13 Dec 2017 17:23:37 +0100 Subject: [PATCH 042/197] Simple size check for old ABI decoder. --- libsolidity/codegen/CompilerUtils.cpp | 117 +++++++++++++++++------ libsolidity/codegen/CompilerUtils.h | 10 +- libsolidity/codegen/ContractCompiler.cpp | 2 + 3 files changed, 97 insertions(+), 32 deletions(-) diff --git a/libsolidity/codegen/CompilerUtils.cpp b/libsolidity/codegen/CompilerUtils.cpp index 704d8da88..d91773125 100644 --- a/libsolidity/codegen/CompilerUtils.cpp +++ b/libsolidity/codegen/CompilerUtils.cpp @@ -22,11 +22,14 @@ #include #include -#include #include #include #include +#include + +#include + using namespace std; namespace dev @@ -159,26 +162,37 @@ void CompilerUtils::storeInMemoryDynamic(Type const& _type, bool _padToWordBound } } -void CompilerUtils::abiDecode(TypePointers const& _typeParameters, bool _fromMemory) +void CompilerUtils::abiDecode(TypePointers const& _typeParameters, bool _fromMemory, bool _revertOnOutOfBounds) { - // We do not check the calldata size, everything is zero-padded - + /// Stack: if (m_context.experimentalFeatureActive(ExperimentalFeature::ABIEncoderV2)) { // Use the new JULIA-based decoding function auto stackHeightBefore = m_context.stackHeight(); abiDecodeV2(_typeParameters, _fromMemory); - solAssert(m_context.stackHeight() - stackHeightBefore == sizeOnStack(_typeParameters) - 1, ""); + solAssert(m_context.stackHeight() - stackHeightBefore == sizeOnStack(_typeParameters) - 2, ""); return; } //@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"}); + } + + m_context << Instruction::DUP2 << Instruction::ADD; + m_context << Instruction::SWAP1; + /// Stack: + // Retain the offset pointer as base_offset, the point from which the data offsets are computed. m_context << Instruction::DUP1; for (TypePointer const& parameterType: _typeParameters) { - // stack: v1 v2 ... v(k-1) base_offset current_offset + // stack: v1 v2 ... v(k-1) input_end base_offset current_offset TypePointer type = parameterType->decodingType(); solUnimplementedAssert(type, "No decoding type found."); if (type->category() == Type::Category::Array) @@ -198,13 +212,35 @@ void CompilerUtils::abiDecode(TypePointers const& _typeParameters, bool _fromMem { // compute data pointer m_context << Instruction::DUP1 << Instruction::MLOAD; - m_context << Instruction::DUP3 << Instruction::ADD; - m_context << Instruction::SWAP2 << Instruction::SWAP1; + 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, )), 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; + // stack: v1 v2 ... v(k-1) input_end base_offset current_offset v(k) + moveIntoStack(3); m_context << u256(0x20) << Instruction::ADD; } else { - m_context << Instruction::SWAP1 << Instruction::DUP2; + // Size has already been checked for this one. + moveIntoStack(2); + m_context << Instruction::DUP3; m_context << u256(arrayType.calldataEncodedSize(true)) << Instruction::ADD; } } @@ -216,24 +252,43 @@ void CompilerUtils::abiDecode(TypePointers const& _typeParameters, bool _fromMem { // put on stack: data_pointer length loadFromMemoryDynamic(IntegerType(256), !_fromMemory); - // stack: base_offset data_offset next_pointer - m_context << Instruction::SWAP1 << Instruction::DUP3 << Instruction::ADD; - // stack: base_offset next_pointer data_pointer + 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 << 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"} + ); // retrieve length loadFromMemoryDynamic(IntegerType(256), !_fromMemory, true); - // stack: base_offset next_pointer length data_pointer + // stack: input_end base_offset next_pointer array_length data_pointer m_context << Instruction::SWAP2; - // stack: base_offset data_pointer length next_pointer + // 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"}); + } } else { - // leave the pointer on the stack + // size has already been checked + // stack: input_end base_offset data_offset m_context << Instruction::DUP1; m_context << u256(calldataType->calldataEncodedSize()) << Instruction::ADD; } if (arrayType.location() == DataLocation::Memory) { - // stack: base_offset calldata_ref [length] next_calldata + // stack: input_end base_offset calldata_ref [length] next_calldata // copy to memory // move calldata type up again moveIntoStack(calldataType->sizeOnStack()); @@ -241,21 +296,27 @@ void CompilerUtils::abiDecode(TypePointers const& _typeParameters, bool _fromMem // fetch next pointer again moveToStackTop(arrayType.sizeOnStack()); } - // move base_offset up - moveToStackTop(1 + arrayType.sizeOnStack()); + // move input_end up + // stack: input_end base_offset calldata_ref [length] next_calldata + moveToStackTop(2 + arrayType.sizeOnStack()); m_context << Instruction::SWAP1; + // stack: base_offset calldata_ref [length] input_end next_calldata + moveToStackTop(2 + arrayType.sizeOnStack()); + m_context << Instruction::SWAP1; + // stack: calldata_ref [length] input_end base_offset next_calldata } } else { solAssert(!type->isDynamicallySized(), "Unknown dynamically sized type: " + type->toString()); loadFromMemoryDynamic(*type, !_fromMemory, true); - moveToStackTop(1 + type->sizeOnStack()); - m_context << Instruction::SWAP1; + // stack: v1 v2 ... v(k-1) input_end base_offset v(k) mem_offset + moveToStackTop(1, type->sizeOnStack()); + moveIntoStack(3, type->sizeOnStack()); } - // stack: v1 v2 ... v(k-1) v(k) base_offset mem_offset + // stack: v1 v2 ... v(k-1) v(k) input_end base_offset next_offset } - m_context << Instruction::POP << Instruction::POP; + popStackSlots(3); } void CompilerUtils::encodeToMemory( @@ -420,15 +481,13 @@ void CompilerUtils::abiEncodeV2( void CompilerUtils::abiDecodeV2(TypePointers const& _parameterTypes, bool _fromMemory) { - // stack: + // stack: [stack top] auto ret = m_context.pushNewTag(); + moveIntoStack(2); + // stack: [stack top] + m_context << Instruction::DUP2 << Instruction::ADD; m_context << Instruction::SWAP1; - if (_fromMemory) - // TODO pass correct size for the memory case - m_context << (u256(1) << 63); - else - m_context << Instruction::CALLDATASIZE; - m_context << Instruction::SWAP1; + // stack: string decoderName = m_context.abiFunctions().tupleDecoder(_parameterTypes, _fromMemory); m_context.appendJumpTo(m_context.namedTag(decoderName)); m_context.adjustStackOffset(int(sizeOnStack(_parameterTypes)) - 3); diff --git a/libsolidity/codegen/CompilerUtils.h b/libsolidity/codegen/CompilerUtils.h index 9fc97b9e3..76670d472 100644 --- a/libsolidity/codegen/CompilerUtils.h +++ b/libsolidity/codegen/CompilerUtils.h @@ -90,8 +90,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. - /// Expects source offset on the stack, which is removed. - void abiDecode(TypePointers const& _typeParameters, bool _fromMemory = false); + /// 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. + /// Stack pre: + /// Stack post: ... + void abiDecode(TypePointers const& _typeParameters, bool _fromMemory = false, bool _revertOnOutOfBounds = 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. @@ -154,7 +158,7 @@ public: /// Decodes data from ABI encoding into internal encoding. If @a _fromMemory is set to true, /// the data is taken from memory instead of from calldata. /// Can allocate memory. - /// Stack pre: + /// Stack pre: /// Stack post: ... void abiDecodeV2(TypePointers const& _parameterTypes, bool _fromMemory = false); diff --git a/libsolidity/codegen/ContractCompiler.cpp b/libsolidity/codegen/ContractCompiler.cpp index 480db98ec..791edc65a 100644 --- a/libsolidity/codegen/ContractCompiler.cpp +++ b/libsolidity/codegen/ContractCompiler.cpp @@ -278,6 +278,7 @@ void ContractCompiler::appendConstructor(FunctionDefinition const& _constructor) m_context.appendProgramSize(); m_context << Instruction::DUP4 << Instruction::CODECOPY; m_context << Instruction::DUP2 << Instruction::ADD; + m_context << Instruction::DUP1; CompilerUtils(m_context).storeFreeMemoryPointer(); // stack: CompilerUtils(m_context).abiDecode(FunctionType(_constructor).parameterTypes(), true); @@ -367,6 +368,7 @@ void ContractCompiler::appendFunctionSelector(ContractDefinition const& _contrac { // Parameter for calldataUnpacker m_context << CompilerUtils::dataStartOffset; + m_context << Instruction::DUP1 << Instruction::CALLDATASIZE << Instruction::SUB; CompilerUtils(m_context).abiDecode(functionType->parameterTypes()); } m_context.appendJumpTo(m_context.functionEntryLabel(functionType->declaration())); From cc2f71e4acede70f6c220d3d0ba407ab73c2024c Mon Sep 17 00:00:00 2001 From: chriseth Date: Wed, 21 Feb 2018 00:40:38 +0100 Subject: [PATCH 043/197] Move dynamic type removal out of the type system. --- libsolidity/analysis/TypeChecker.cpp | 12 ++++++++--- libsolidity/ast/Types.cpp | 23 ++++++++++++---------- libsolidity/ast/Types.h | 3 +++ libsolidity/codegen/ExpressionCompiler.cpp | 17 +++++++++++----- 4 files changed, 37 insertions(+), 18 deletions(-) diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index bebdb9b6e..c8e64c789 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -1551,16 +1551,22 @@ bool TypeChecker::visit(FunctionCall const& _functionCall) _functionCall.expression().annotation().isPure && functionType->isPure(); + bool allowDynamicTypes = false; // @TODO if (!functionType) { m_errorReporter.typeError(_functionCall.location(), "Type is not callable"); _functionCall.annotation().type = make_shared(); return false; } - else if (functionType->returnParameterTypes().size() == 1) - _functionCall.annotation().type = functionType->returnParameterTypes().front(); + + auto returnTypes = + allowDynamicTypes ? + functionType->returnParameterTypes() : + functionType->returnParameterTypesWithoutDynamicTypes(); + if (returnTypes.size() == 1) + _functionCall.annotation().type = returnTypes.front(); else - _functionCall.annotation().type = make_shared(functionType->returnParameterTypes()); + _functionCall.annotation().type = make_shared(returnTypes); if (auto functionName = dynamic_cast(&_functionCall.expression())) { diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp index b2881beac..41700e283 100644 --- a/libsolidity/ast/Types.cpp +++ b/libsolidity/ast/Types.cpp @@ -2311,6 +2311,18 @@ vector FunctionType::parameterNames() const return vector(m_parameterNames.cbegin() + 1, m_parameterNames.cend()); } +TypePointers FunctionType::returnParameterTypesWithoutDynamicTypes() const +{ + TypePointers returnParameterTypes = m_returnParameterTypes; + + if (m_kind == Kind::External || m_kind == Kind::CallCode || m_kind == Kind::DelegateCall) + for (auto& param: returnParameterTypes) + if (param->isDynamicallySized() && !param->dataStoredIn(DataLocation::Storage)) + param = make_shared(); + + return returnParameterTypes; +} + TypePointers FunctionType::parameterTypes() const { if (!bound()) @@ -2772,18 +2784,9 @@ FunctionTypePointer FunctionType::asMemberFunction(bool _inLibrary, bool _bound) kind = Kind::DelegateCall; } - TypePointers returnParameterTypes = m_returnParameterTypes; - if (kind != Kind::Internal) - { - // Alter dynamic types to be non-accessible. - for (auto& param: returnParameterTypes) - if (param->isDynamicallySized()) - param = make_shared(); - } - return make_shared( parameterTypes, - returnParameterTypes, + m_returnParameterTypes, m_parameterNames, m_returnParameterNames, kind, diff --git a/libsolidity/ast/Types.h b/libsolidity/ast/Types.h index c20a025f3..c930dd240 100644 --- a/libsolidity/ast/Types.h +++ b/libsolidity/ast/Types.h @@ -973,6 +973,9 @@ public: TypePointers parameterTypes() const; std::vector parameterNames() const; TypePointers const& returnParameterTypes() const { return m_returnParameterTypes; } + /// @returns the list of return parameter types. All dynamically-sized types (this excludes + /// storage pointers) are replaced by InaccessibleDynamicType instances. + TypePointers returnParameterTypesWithoutDynamicTypes() const; std::vector const& returnParameterNames() const { return m_returnParameterNames; } /// @returns the "self" parameter type for a bound function TypePointer const& selfType() const; diff --git a/libsolidity/codegen/ExpressionCompiler.cpp b/libsolidity/codegen/ExpressionCompiler.cpp index f50628ff4..7d148c0c8 100644 --- a/libsolidity/codegen/ExpressionCompiler.cpp +++ b/libsolidity/codegen/ExpressionCompiler.cpp @@ -139,8 +139,8 @@ void ExpressionCompiler::appendStateVariableAccessor(VariableDeclaration const& utils().popStackSlots(paramTypes.size() - 1); } unsigned retSizeOnStack = 0; - solAssert(accessorType.returnParameterTypes().size() >= 1, ""); - auto const& returnTypes = accessorType.returnParameterTypes(); + auto returnTypes = accessorType.returnParameterTypes(); + solAssert(returnTypes.size() >= 1, ""); if (StructType const* structType = dynamic_cast(returnType.get())) { // remove offset @@ -1618,15 +1618,22 @@ void ExpressionCompiler::appendExternalFunctionCall( m_context.experimentalFeatureActive(ExperimentalFeature::V050) && m_context.evmVersion().hasStaticCall(); + bool allowDynamicTypes = false; // @TODO unsigned retSize = 0; + TypePointers returnTypes; if (returnSuccessCondition) retSize = 0; // return value actually is success condition + else if (allowDynamicTypes) + returnTypes = _functionType.returnParameterTypes(); else - for (auto const& retType: _functionType.returnParameterTypes()) + { + returnTypes = _functionType.returnParameterTypesWithoutDynamicTypes(); + for (auto const& retType: returnTypes) { solAssert(!retType->isDynamicallySized(), "Unable to return dynamic type from external call."); retSize += retType->calldataEncodedSize(); } + } // Evaluate arguments. TypePointers argumentTypes; @@ -1824,11 +1831,11 @@ void ExpressionCompiler::appendExternalFunctionCall( utils().fetchFreeMemoryPointer(); m_context << Instruction::SUB << Instruction::MLOAD; } - else if (!_functionType.returnParameterTypes().empty()) + else if (!returnTypes.empty()) { utils().fetchFreeMemoryPointer(); bool memoryNeeded = false; - for (auto const& retType: _functionType.returnParameterTypes()) + for (auto const& retType: returnTypes) { utils().loadFromMemoryDynamic(*retType, false, true, true); if (dynamic_cast(retType.get())) From c2709a2d8e53155513fa8002a564e434fce68c68 Mon Sep 17 00:00:00 2001 From: chriseth Date: Wed, 21 Feb 2018 11:07:30 +0100 Subject: [PATCH 044/197] Decode dynamic data. --- libsolidity/analysis/TypeChecker.cpp | 2 +- libsolidity/codegen/ABIFunctions.cpp | 3 + libsolidity/codegen/CompilerUtils.cpp | 4 +- libsolidity/codegen/ExpressionCompiler.cpp | 61 +++++++++++++------ .../SolidityNameAndTypeResolution.cpp | 5 ++ 5 files changed, 55 insertions(+), 20 deletions(-) diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index c8e64c789..999a2a97d 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -1551,7 +1551,7 @@ bool TypeChecker::visit(FunctionCall const& _functionCall) _functionCall.expression().annotation().isPure && functionType->isPure(); - bool allowDynamicTypes = false; // @TODO + bool allowDynamicTypes = m_evmVersion.supportsReturndata(); if (!functionType) { m_errorReporter.typeError(_functionCall.location(), "Type is not callable"); diff --git a/libsolidity/codegen/ABIFunctions.cpp b/libsolidity/codegen/ABIFunctions.cpp index 00f590653..8e8908540 100644 --- a/libsolidity/codegen/ABIFunctions.cpp +++ b/libsolidity/codegen/ABIFunctions.cpp @@ -253,6 +253,9 @@ string ABIFunctions::cleanupFunction(Type const& _type, bool _revertOnFailure) templ("body", w.render()); break; } + case Type::Category::InaccessibleDynamic: + templ("body", "cleaned := 0"); + break; default: solAssert(false, "Cleanup of type " + _type.identifier() + " requested."); } diff --git a/libsolidity/codegen/CompilerUtils.cpp b/libsolidity/codegen/CompilerUtils.cpp index d91773125..1bd1103b1 100644 --- a/libsolidity/codegen/CompilerUtils.cpp +++ b/libsolidity/codegen/CompilerUtils.cpp @@ -198,7 +198,7 @@ void CompilerUtils::abiDecode(TypePointers const& _typeParameters, bool _fromMem if (type->category() == Type::Category::Array) { auto const& arrayType = dynamic_cast(*type); - solUnimplementedAssert(!arrayType.baseType()->isDynamicallySized(), "Nested arrays not yet implemented."); + solUnimplementedAssert(!arrayType.baseType()->isDynamicallyEncoded(), "Nested arrays not yet implemented."); if (_fromMemory) { solUnimplementedAssert( @@ -308,7 +308,7 @@ void CompilerUtils::abiDecode(TypePointers const& _typeParameters, bool _fromMem } else { - solAssert(!type->isDynamicallySized(), "Unknown dynamically sized type: " + type->toString()); + solAssert(!type->isDynamicallyEncoded(), "Unknown dynamically sized type: " + type->toString()); loadFromMemoryDynamic(*type, !_fromMemory, true); // stack: v1 v2 ... v(k-1) input_end base_offset v(k) mem_offset moveToStackTop(1, type->sizeOnStack()); diff --git a/libsolidity/codegen/ExpressionCompiler.cpp b/libsolidity/codegen/ExpressionCompiler.cpp index 7d148c0c8..37069c3e7 100644 --- a/libsolidity/codegen/ExpressionCompiler.cpp +++ b/libsolidity/codegen/ExpressionCompiler.cpp @@ -1618,22 +1618,27 @@ void ExpressionCompiler::appendExternalFunctionCall( m_context.experimentalFeatureActive(ExperimentalFeature::V050) && m_context.evmVersion().hasStaticCall(); - bool allowDynamicTypes = false; // @TODO + bool haveReturndatacopy = m_context.evmVersion().supportsReturndata(); unsigned retSize = 0; TypePointers returnTypes; if (returnSuccessCondition) retSize = 0; // return value actually is success condition - else if (allowDynamicTypes) + else if (haveReturndatacopy) returnTypes = _functionType.returnParameterTypes(); else - { returnTypes = _functionType.returnParameterTypesWithoutDynamicTypes(); - for (auto const& retType: returnTypes) + + bool dynamicReturnSize = false; + for (auto const& retType: returnTypes) + if (retType->isDynamicallyEncoded()) { - solAssert(!retType->isDynamicallySized(), "Unable to return dynamic type from external call."); - retSize += retType->calldataEncodedSize(); + solAssert(haveReturndatacopy, ""); + dynamicReturnSize = true; + retSize = 0; + break; } - } + else + retSize += retType->calldataEncodedSize(); // Evaluate arguments. TypePointers argumentTypes; @@ -1834,17 +1839,39 @@ void ExpressionCompiler::appendExternalFunctionCall( else if (!returnTypes.empty()) { utils().fetchFreeMemoryPointer(); - bool memoryNeeded = false; - for (auto const& retType: returnTypes) - { - utils().loadFromMemoryDynamic(*retType, false, true, true); - if (dynamic_cast(retType.get())) - memoryNeeded = true; - } - if (memoryNeeded) - utils().storeFreeMemoryPointer(); + // Stack: return_data_start + + // The old decoder did not allocate any memory (i.e. did not touch the free + // memory pointer), but kept references to the return data for + // (statically-sized) arrays + bool needToUpdateFreeMemoryPtr = false; + if (dynamicReturnSize || m_context.experimentalFeatureActive(ExperimentalFeature::ABIEncoderV2)) + needToUpdateFreeMemoryPtr = true; else - m_context << Instruction::POP; + for (auto const& retType: returnTypes) + if (dynamic_cast(retType.get())) + needToUpdateFreeMemoryPtr = true; + + // Stack: return_data_start + if (dynamicReturnSize) + { + solAssert(haveReturndatacopy, ""); + m_context.appendInlineAssembly("{ returndatacopy(return_data_start, 0, returndatasize()) }", {"return_data_start"}); + } + else + solAssert(retSize > 0, ""); + // Always use the actual return length, and not our calculated expected length, if returndatacopy is supported. + // This ensures it can catch badly formatted input from external calls. + m_context << (haveReturndatacopy ? eth::AssemblyItem(Instruction::RETURNDATASIZE) : u256(retSize)); + // Stack: return_data_start return_data_size + if (needToUpdateFreeMemoryPtr) + m_context.appendInlineAssembly(R"({ + // round size to the next multiple of 32 + let newMem := add(start, and(add(size, 0x1f), not(0x1f))) + mstore(0x40, newMem) + })", {"start", "size"}); + + utils().abiDecode(returnTypes, true, true); } } diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index c757037c7..8a020ca6c 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -3372,6 +3372,11 @@ BOOST_AUTO_TEST_CASE(dynamic_return_types_not_possible) } } )"; + m_compiler.setEVMVersion(EVMVersion{}); + CHECK_WARNING(sourceCode, "Use of the \"var\" keyword is deprecated"); + m_compiler.setEVMVersion(*EVMVersion::fromString("byzantium")); + CHECK_WARNING(sourceCode, "Use of the \"var\" keyword is deprecated"); + m_compiler.setEVMVersion(*EVMVersion::fromString("homestead")); CHECK_ERROR(sourceCode, TypeError, "Explicit type conversion not allowed from \"inaccessible dynamic type\" to \"bytes storage pointer\"."); } From cc0f70263f41ea288391ef1ad416d70aff4b031e Mon Sep 17 00:00:00 2001 From: chriseth Date: Wed, 21 Feb 2018 15:59:34 +0100 Subject: [PATCH 045/197] Tests for returning dynamic data. --- test/libsolidity/ABIDecoderTests.cpp | 83 +++++++++++++++++++ .../SolidityNameAndTypeResolution.cpp | 27 ++++-- 2 files changed, 104 insertions(+), 6 deletions(-) diff --git a/test/libsolidity/ABIDecoderTests.cpp b/test/libsolidity/ABIDecoderTests.cpp index 15c04b373..beb7b5af6 100644 --- a/test/libsolidity/ABIDecoderTests.cpp +++ b/test/libsolidity/ABIDecoderTests.cpp @@ -786,6 +786,89 @@ BOOST_AUTO_TEST_CASE(complex_struct) } +BOOST_AUTO_TEST_CASE(return_dynamic_types_cross_call_simple) +{ + if (m_evmVersion == EVMVersion::homestead()) + return; + + string sourceCode = R"( + contract C { + function dyn() public returns (bytes) { + return "1234567890123456789012345678901234567890"; + } + function f() public returns (bytes) { + return this.dyn(); + } + } + )"; + BOTH_ENCODERS( + compileAndRun(sourceCode, 0, "C"); + ABI_CHECK(callContractFunction("f()"), encodeArgs(0x20, 40, string("1234567890123456789012345678901234567890"))); + ) +} + +BOOST_AUTO_TEST_CASE(return_dynamic_types_cross_call_advanced) +{ + if (m_evmVersion == EVMVersion::homestead()) + return; + + string sourceCode = R"( + contract C { + function dyn() public returns (bytes a, uint b, bytes20[] c, uint d) { + a = "1234567890123456789012345678901234567890"; + b = uint(-1); + c = new bytes20[](4); + c[0] = bytes20(1234); + c[3] = bytes20(6789); + d = 0x1234; + } + function f() public returns (bytes, uint, bytes20[], uint) { + return this.dyn(); + } + } + )"; + BOTH_ENCODERS( + compileAndRun(sourceCode, 0, "C"); + ABI_CHECK(callContractFunction("f()"), encodeArgs( + 0x80, u256(-1), 0xe0, 0x1234, + 40, string("1234567890123456789012345678901234567890"), + 4, u256(1234) << (8 * (32 - 20)), 0, 0, u256(6789) << (8 * (32 - 20)) + )); + ) +} + +BOOST_AUTO_TEST_CASE(return_dynamic_types_cross_call_out_of_range) +{ + string sourceCode = R"( + contract C { + function dyn(uint x) public returns (bytes a) { + assembly { + mstore(0, 0x20) + mstore(0x20, 0x21) + return(0, x) + } + } + function f(uint x) public returns (bool) { + this.dyn(x); + return true; + } + } + )"; + BOTH_ENCODERS( + compileAndRun(sourceCode, 0, "C"); + if (m_evmVersion == EVMVersion::homestead()) + { + ABI_CHECK(callContractFunction("f(uint256)", 0x60), encodeArgs(true)); + ABI_CHECK(callContractFunction("f(uint256)", 0x7f), encodeArgs(true)); + } + else + { + ABI_CHECK(callContractFunction("f(uint256)", 0x60), encodeArgs()); + ABI_CHECK(callContractFunction("f(uint256)", 0x61), encodeArgs(true)); + } + ABI_CHECK(callContractFunction("f(uint256)", 0x80), encodeArgs(true)); + ) +} BOOST_AUTO_TEST_SUITE_END() diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index 8a020ca6c..50ee2b2e4 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -3372,12 +3372,10 @@ BOOST_AUTO_TEST_CASE(dynamic_return_types_not_possible) } } )"; - m_compiler.setEVMVersion(EVMVersion{}); - CHECK_WARNING(sourceCode, "Use of the \"var\" keyword is deprecated"); - m_compiler.setEVMVersion(*EVMVersion::fromString("byzantium")); - CHECK_WARNING(sourceCode, "Use of the \"var\" keyword is deprecated"); - m_compiler.setEVMVersion(*EVMVersion::fromString("homestead")); - CHECK_ERROR(sourceCode, TypeError, "Explicit type conversion not allowed from \"inaccessible dynamic type\" to \"bytes storage pointer\"."); + 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\"."); + else + CHECK_WARNING(sourceCode, "Use of the \"var\" keyword is deprecated"); } BOOST_AUTO_TEST_CASE(memory_arrays_not_resizeable) @@ -6392,6 +6390,23 @@ BOOST_AUTO_TEST_CASE(return_structs) 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(return_recursive_structs) { char const* text = R"( From c7860a0fba74a4b62dfb93070544230e74ce98ab Mon Sep 17 00:00:00 2001 From: chriseth Date: Fri, 9 Mar 2018 14:20:30 +0100 Subject: [PATCH 046/197] Changelog entry. --- Changelog.md | 1 + 1 file changed, 1 insertion(+) diff --git a/Changelog.md b/Changelog.md index d37bce63d..c5577b86a 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,6 +1,7 @@ ### 0.4.22 (unreleased) Features: + * General: Support accessing dynamic return data in post-byzantium EVMs. Bugfixes: * Code Generator: Allow ``block.blockhash`` without being called. From 9d9c0bf908c4b61f72595ffca530d7d3fc4d28c2 Mon Sep 17 00:00:00 2001 From: Erik Kundt Date: Wed, 21 Mar 2018 12:47:08 +0100 Subject: [PATCH 047/197] Updates "How to contribute" Adds detailed description of the new syntax test tool. --- docs/contributing.rst | 62 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) diff --git a/docs/contributing.rst b/docs/contributing.rst index 8b4695e40..08c125f98 100644 --- a/docs/contributing.rst +++ b/docs/contributing.rst @@ -91,6 +91,68 @@ Alternatively, there is a testing script at ``scripts/test.sh`` which executes a Travis CI even runs some additional tests (including ``solc-js`` and testing third party Solidity frameworks) that require compiling the Emscripten target. +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. +The test suite will compile and check them against the given expectations. + +Example: ``./test/libsolidity/syntaxTests/double_stateVariable_declaration.sol`` + +:: + + contract test { + uint256 variable; + uint128 variable; + } + // ---- + // DeclarationError: Identifier already declared. + + +The comments in the contract above are used to describe the expected compiler errors. The state variable ``variable`` was declared twice, which is not allowed. +This will result in a ``DeclarationError`` stating that the identifer 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``: + +:: + + contract test { + uint256 variable; + } + // ---- + // DeclarationError: Identifier already declared. + +Running ``./test/isoltest`` again will result in a test failure: + +:: + + syntaxTests/double_stateVariable_declaration.sol: FAIL + Contract: + contract test { + uint256 variable; + } + + Expected result: + DeclarationError: 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: + +- 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 remove the annotation which contains the exception not met and runs this test again. +- skip: Skips the execution of this particular test. +- quit: Quits ``isoltest``. + + +.. 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. + Whiskers ======== From b79531bebf67b84fbd2cfe90c1911ec563855afa Mon Sep 17 00:00:00 2001 From: Federico Bond Date: Thu, 22 Mar 2018 01:20:50 -0300 Subject: [PATCH 048/197] Improve error message when trying to shift by fractional number --- Changelog.md | 1 + libsolidity/ast/Types.cpp | 2 +- test/libsolidity/SolidityNameAndTypeResolution.cpp | 10 ++++++++++ 3 files changed, 12 insertions(+), 1 deletion(-) diff --git a/Changelog.md b/Changelog.md index c5577b86a..11be5b9d1 100644 --- a/Changelog.md +++ b/Changelog.md @@ -10,6 +10,7 @@ Bugfixes: * Commandline interface: Support ``--evm-version constantinople`` properly. * DocString Parser: Fix error message for empty descriptions. * Standard JSON: Support ``constantinople`` as ``evmVersion`` properly. + * Type System: Improve error message when attempting to shift by a fractional amount. * Type System: Make external library functions accessible. ### 0.4.21 (2018-03-07) diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp index 41700e283..720215c90 100644 --- a/libsolidity/ast/Types.cpp +++ b/libsolidity/ast/Types.cpp @@ -327,7 +327,7 @@ bool isValidShiftAndAmountType(Token::Value _operator, Type const& _shiftAmountT else if (IntegerType const* otherInt = dynamic_cast(&_shiftAmountType)) return !otherInt->isAddress(); else if (RationalNumberType const* otherRat = dynamic_cast(&_shiftAmountType)) - return otherRat->integerType() && !otherRat->integerType()->isSigned(); + return !otherRat->isFractional() && otherRat->integerType() && !otherRat->integerType()->isSigned(); else return false; } diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index 50ee2b2e4..ab6113dd4 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -5866,6 +5866,16 @@ BOOST_AUTO_TEST_CASE(shift_constant_right_excessive_rvalue) 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"( From ef3595b0001614c19174c61d29d221844ed83a0e Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Tue, 27 Feb 2018 12:02:56 +0100 Subject: [PATCH 049/197] Allow overriding external functions in interfaces with public in an implementing contract --- Changelog.md | 1 + libsolidity/analysis/TypeChecker.cpp | 15 +++++++++++++++ .../libsolidity/SolidityNameAndTypeResolution.cpp | 14 ++++++++++++++ 3 files changed, 30 insertions(+) diff --git a/Changelog.md b/Changelog.md index c5577b86a..7a4149f38 100644 --- a/Changelog.md +++ b/Changelog.md @@ -2,6 +2,7 @@ Features: * General: Support accessing dynamic return data in post-byzantium EVMs. + * Interfaces: Allow overriding external functions in interfaces with public in an implementing contract. Bugfixes: * Code Generator: Allow ``block.blockhash`` without being called. diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index 999a2a97d..487959c5d 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -366,6 +366,16 @@ void TypeChecker::checkContractIllegalOverrides(ContractDefinition const& _contr } } +namespace { + +bool functionIsInInterface(FunctionDefinition const& _function) +{ + return dynamic_cast(_function.scope()) && + dynamic_cast(_function.scope())->contractKind() == ContractDefinition::ContractKind::Interface; +} + +} + void TypeChecker::checkFunctionOverride(FunctionDefinition const& function, FunctionDefinition const& super) { FunctionType functionType(function); @@ -378,7 +388,12 @@ void TypeChecker::checkFunctionOverride(FunctionDefinition const& function, Func function.annotation().superFunction = &super; if (function.visibility() != super.visibility()) + { + // visibility is enforced to be external in interfaces, but a contract can override that with public + if (functionIsInInterface(super) && !functionIsInInterface(function) && function.visibility() == FunctionDefinition::Visibility::Public) + return; overrideError(function, super, "Overriding function visibility differs."); + } else if (function.stateMutability() != super.stateMutability()) overrideError( diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index 50ee2b2e4..e30ea8a4a 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -6784,6 +6784,20 @@ BOOST_AUTO_TEST_CASE(using_interface_complex) 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"( From 8bae2dba7c9697d129a43c1dde54690f3e37a84a Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Wed, 14 Mar 2018 16:56:06 +0100 Subject: [PATCH 050/197] Introduce inContractKind helper on FunctionDefinition --- libsolidity/analysis/TypeChecker.cpp | 16 +++++----------- libsolidity/ast/AST.cpp | 7 +++++++ libsolidity/ast/AST.h | 2 ++ 3 files changed, 14 insertions(+), 11 deletions(-) diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index 487959c5d..6e287f838 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -366,16 +366,6 @@ void TypeChecker::checkContractIllegalOverrides(ContractDefinition const& _contr } } -namespace { - -bool functionIsInInterface(FunctionDefinition const& _function) -{ - return dynamic_cast(_function.scope()) && - dynamic_cast(_function.scope())->contractKind() == ContractDefinition::ContractKind::Interface; -} - -} - void TypeChecker::checkFunctionOverride(FunctionDefinition const& function, FunctionDefinition const& super) { FunctionType functionType(function); @@ -390,7 +380,11 @@ void TypeChecker::checkFunctionOverride(FunctionDefinition const& function, Func if (function.visibility() != super.visibility()) { // visibility is enforced to be external in interfaces, but a contract can override that with public - if (functionIsInInterface(super) && !functionIsInInterface(function) && function.visibility() == FunctionDefinition::Visibility::Public) + if ( + super.inContractKind() == ContractDefinition::ContractKind::Interface && + function.inContractKind() != ContractDefinition::ContractKind::Interface && + function.visibility() == FunctionDefinition::Visibility::Public + ) return; overrideError(function, super, "Overriding function visibility differs."); } diff --git a/libsolidity/ast/AST.cpp b/libsolidity/ast/AST.cpp index 27220b1f3..d8ad009d1 100644 --- a/libsolidity/ast/AST.cpp +++ b/libsolidity/ast/AST.cpp @@ -290,6 +290,13 @@ TypeDeclarationAnnotation& EnumDefinition::annotation() const return dynamic_cast(*m_annotation); } +ContractDefinition::ContractKind FunctionDefinition::inContractKind() const +{ + auto contractDef = dynamic_cast(scope()); + solAssert(contractDef, "Enclosing Scope of FunctionDefinition was not set."); + return contractDef->contractKind(); +} + shared_ptr FunctionDefinition::functionType(bool _internal) const { if (_internal) diff --git a/libsolidity/ast/AST.h b/libsolidity/ast/AST.h index a25df64b7..9c67d3549 100644 --- a/libsolidity/ast/AST.h +++ b/libsolidity/ast/AST.h @@ -624,6 +624,8 @@ public: /// arguments separated by commas all enclosed in parentheses without any spaces. std::string externalSignature() const; + ContractDefinition::ContractKind inContractKind() const; + virtual TypePointer type() const override; /// @param _internal false indicates external interface is concerned, true indicates internal interface is concerned. From ed632025fe5995d093472b1e7660087e943f2ca6 Mon Sep 17 00:00:00 2001 From: bitshift Date: Mon, 5 Mar 2018 19:23:49 +0100 Subject: [PATCH 051/197] Moves blockhash function to global level. --- libsolidity/analysis/GlobalContext.cpp | 1 + libsolidity/ast/Types.cpp | 29 ++++++++++++++++++-------- 2 files changed, 21 insertions(+), 9 deletions(-) diff --git a/libsolidity/analysis/GlobalContext.cpp b/libsolidity/analysis/GlobalContext.cpp index 34cb61d8c..6a858d36d 100644 --- a/libsolidity/analysis/GlobalContext.cpp +++ b/libsolidity/analysis/GlobalContext.cpp @@ -38,6 +38,7 @@ m_magicVariables(vector>{ make_shared("addmod", make_shared(strings{"uint256", "uint256", "uint256"}, strings{"uint256"}, FunctionType::Kind::AddMod, false, StateMutability::Pure)), make_shared("assert", make_shared(strings{"bool"}, strings{}, FunctionType::Kind::Assert, false, StateMutability::Pure)), make_shared("block", make_shared(MagicType::Kind::Block)), + make_shared("blockhash", make_shared(strings{"uint256"}, strings{"bytes32"}, FunctionType::Kind::BlockHash, false, StateMutability::View)), make_shared("ecrecover", make_shared(strings{"bytes32", "uint8", "bytes32", "bytes32"}, strings{"address"}, FunctionType::Kind::ECRecover, false, StateMutability::Pure)), make_shared("gasleft", make_shared(strings(), strings{"uint256"}, FunctionType::Kind::GasLeft, false, StateMutability::View)), make_shared("keccak256", make_shared(strings(), strings{"bytes32"}, FunctionType::Kind::SHA3, true, StateMutability::Pure)), diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp index 41700e283..f4559edae 100644 --- a/libsolidity/ast/Types.cpp +++ b/libsolidity/ast/Types.cpp @@ -3002,19 +3002,30 @@ bool MagicType::operator==(Type const& _other) const return other.m_kind == m_kind; } -MemberList::MemberMap MagicType::nativeMembers(ContractDefinition const*) const +MemberList::MemberMap MagicType::nativeMembers(ContractDefinition const* _contract) const { + const bool v050 = _contract->sourceUnit().annotation().experimentalFeatures.count(ExperimentalFeature::V050); switch (m_kind) { case Kind::Block: - return MemberList::MemberMap({ - {"coinbase", make_shared(160, IntegerType::Modifier::Address)}, - {"timestamp", make_shared(256)}, - {"blockhash", make_shared(strings{"uint"}, strings{"bytes32"}, FunctionType::Kind::BlockHash, false, StateMutability::View)}, - {"difficulty", make_shared(256)}, - {"number", make_shared(256)}, - {"gaslimit", make_shared(256)} - }); + { + std::vector members = { + {"coinbase", make_shared(160, IntegerType::Modifier::Address)}, + {"timestamp", make_shared(256)}, + {"difficulty", make_shared(256)}, + {"number", make_shared(256)}, + {"gaslimit", make_shared(256)} + }; + + if (!v050) + { + auto blockhashFun = make_shared(strings{"uint256"}, strings{"bytes32"}, + FunctionType::Kind::BlockHash, false, StateMutability::View); + + members.emplace_back("blockhash", blockhashFun); + } + return MemberList::MemberMap(members); + } case Kind::Message: return MemberList::MemberMap({ {"sender", make_shared(160, IntegerType::Modifier::Address)}, From be35a65eb3966965e30b6b582f7722338e558013 Mon Sep 17 00:00:00 2001 From: bitshift Date: Mon, 5 Mar 2018 19:24:33 +0100 Subject: [PATCH 052/197] Adds unit tests for moved function. --- test/libsolidity/SolidityEndToEndTest.cpp | 29 +++++++++++++++++++ .../SolidityExpressionCompiler.cpp | 9 ++++-- .../SolidityNameAndTypeResolution.cpp | 12 ++++++++ test/libsolidity/ViewPureChecker.cpp | 4 +-- 4 files changed, 49 insertions(+), 5 deletions(-) diff --git a/test/libsolidity/SolidityEndToEndTest.cpp b/test/libsolidity/SolidityEndToEndTest.cpp index 44dc40f73..43d390f56 100644 --- a/test/libsolidity/SolidityEndToEndTest.cpp +++ b/test/libsolidity/SolidityEndToEndTest.cpp @@ -1805,6 +1805,35 @@ BOOST_AUTO_TEST_CASE(uncalled_blockhash) BOOST_CHECK(result[0] != 0 || result[1] != 0 || result[2] != 0); } +BOOST_AUTO_TEST_CASE(blockhash_global_level) +{ + char const* sourceCode = R"( + contract Test { + function a() public returns (bytes32) { + return blockhash(0); + } + } + )"; + compileAndRun(sourceCode); + BOOST_CHECK(!callContractFunction("a()").empty()); +} + +BOOST_AUTO_TEST_CASE(blockhash_shadow) +{ + char const* sourceCode = R"( + contract Test { + function blockhash(uint256 blockNumber) public returns (bytes32) { + return "abc"; + } + function f() returns (bytes32) { + return blockhash(3); + } + } + )"; + compileAndRun(sourceCode); + BOOST_REQUIRE(callContractFunction("f()") != encodeArgs("abc")); +} + BOOST_AUTO_TEST_CASE(log0) { char const* sourceCode = R"( diff --git a/test/libsolidity/SolidityExpressionCompiler.cpp b/test/libsolidity/SolidityExpressionCompiler.cpp index c8adfc6ed..90d8265c6 100644 --- a/test/libsolidity/SolidityExpressionCompiler.cpp +++ b/test/libsolidity/SolidityExpressionCompiler.cpp @@ -503,12 +503,15 @@ BOOST_AUTO_TEST_CASE(blockhash) char const* sourceCode = R"( contract test { function f() { - block.blockhash(3); + blockhash(3); } } )"; - bytes code = compileFirstExpression(sourceCode, {}, {}, - {make_shared("block", make_shared(MagicType::Kind::Block))}); + + auto blockhashFun = make_shared(strings{"uint256"}, strings{"bytes32"}, + FunctionType::Kind::BlockHash, false, StateMutability::View); + + bytes code = compileFirstExpression(sourceCode, {}, {}, {make_shared("blockhash", blockhashFun)}); bytes expectation({byte(Instruction::PUSH1), 0x03, byte(Instruction::BLOCKHASH)}); diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index 50ee2b2e4..579fb2396 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -8555,6 +8555,18 @@ BOOST_AUTO_TEST_CASE(require_visibility_specifiers) CHECK_ERROR(text, SyntaxError, "No visibility specified."); } +BOOST_AUTO_TEST_CASE(blockhash_not_available_in_block) +{ + char const* text = R"( + contract Test { + function a() public returns (bytes32) { + return block.blockhash(0); + } + } + )"; + CHECK_ERROR(text, TypeError, "Member \"blockhash\" not found or not visible after argument-dependent lookup in block"); +} + BOOST_AUTO_TEST_SUITE_END() } diff --git a/test/libsolidity/ViewPureChecker.cpp b/test/libsolidity/ViewPureChecker.cpp index a6ce6d917..de25cfcfd 100644 --- a/test/libsolidity/ViewPureChecker.cpp +++ b/test/libsolidity/ViewPureChecker.cpp @@ -107,7 +107,6 @@ BOOST_AUTO_TEST_CASE(environment_access) vector view{ "block.coinbase", "block.timestamp", - "block.blockhash(7)", "block.difficulty", "block.number", "block.gaslimit", @@ -118,15 +117,16 @@ BOOST_AUTO_TEST_CASE(environment_access) "tx.origin", "tx.gasprice", "this", + "blockhash(7)", "address(1).balance" }; vector pure{ "msg.data", "msg.data[0]", "msg.sig", - "block.blockhash", // Not evaluating the function "msg", "block", + "blockhash", // Not evaluating the function "tx" }; for (string const& x: view) From bddfa47e770d27005bd6604a24033cf2f632b9ee Mon Sep 17 00:00:00 2001 From: bitshift Date: Mon, 5 Mar 2018 19:24:51 +0100 Subject: [PATCH 053/197] Updates docs for blockhash changes. --- docs/miscellaneous.rst | 20 +++++++++++++++++++- docs/units-and-global-variables.rst | 4 ++-- 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/docs/miscellaneous.rst b/docs/miscellaneous.rst index a7d5c4456..01154854c 100644 --- a/docs/miscellaneous.rst +++ b/docs/miscellaneous.rst @@ -314,7 +314,7 @@ The following is the order of precedence for operators, listed in order of evalu Global Variables ================ -- ``block.blockhash(uint blockNumber) returns (bytes32)``: hash of the given block - only works for 256 most recent blocks +- ``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 - ``block.difficulty`` (``uint``): current block difficulty - ``block.gaslimit`` (``uint``): current block gaslimit @@ -331,6 +331,7 @@ Global Variables - ``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) - ``revert()``: abort execution and revert state changes +- ``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 ` - ``sha3(...) returns (bytes32)``: an alias to ``keccak256`` - ``sha256(...) returns (bytes32)``: compute the SHA-256 hash of the :ref:`(tightly packed) arguments ` @@ -346,6 +347,23 @@ Global Variables - ``
.send(uint256 amount) returns (bool)``: send given amount of Wei to :ref:`address`, returns ``false`` on failure - ``
.transfer(uint256 amount)``: send given amount of Wei to :ref:`address`, throws on failure +.. note:: + Do not rely on ``block.timestamp``, ``now`` and ``blockhash`` as a source of randomness, + unless you know what you are doing. + + Both the timestamp and the block hash can be influenced by miners to some degree. + Bad actors in the mining community can for example run a casino payout function on a chosen hash + and just retry a different hash if they did not receive any money. + + The current block timestamp must be strictly larger than the timestamp of the last block, + but the only guarantee is that it will be somewhere between the timestamps of two + consecutive blocks in the canonical chain. + +.. note:: + The block hashes are not available for all blocks for scalability reasons. + You can only access the hashes of the most recent 256 blocks, all other + values will be zero. + .. index:: visibility, public, private, external, internal Function Visibility Specifiers diff --git a/docs/units-and-global-variables.rst b/docs/units-and-global-variables.rst index d789e87f3..2571f20aa 100644 --- a/docs/units-and-global-variables.rst +++ b/docs/units-and-global-variables.rst @@ -52,7 +52,7 @@ namespace and are mainly used to provide information about the blockchain. Block and Transaction Properties -------------------------------- -- ``block.blockhash(uint blockNumber) returns (bytes32)``: hash of the given block - only works for 256 most recent blocks excluding current +- ``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 - ``block.difficulty`` (``uint``): current block difficulty - ``block.gaslimit`` (``uint``): current block gaslimit @@ -74,7 +74,7 @@ Block and Transaction Properties This includes calls to library functions. .. note:: - Do not rely on ``block.timestamp``, ``now`` and ``block.blockhash`` as a source of randomness, + Do not rely on ``block.timestamp``, ``now`` and ``blockhash`` as a source of randomness, unless you know what you are doing. Both the timestamp and the block hash can be influenced by miners to some degree. From 2c56e530467c088c5096d95422313ca211786eca Mon Sep 17 00:00:00 2001 From: bitshift Date: Wed, 7 Mar 2018 10:48:10 +0100 Subject: [PATCH 054/197] Changes deprecation and adjusts tests. --- libsolidity/analysis/StaticAnalyzer.cpp | 15 +++++++++ libsolidity/ast/Types.cpp | 29 +++++----------- test/libsolidity/SolidityEndToEndTest.cpp | 31 ++++------------- .../SolidityNameAndTypeResolution.cpp | 27 +++++++++++---- test/libsolidity/ViewPureChecker.cpp | 33 ++++++++++++++----- 5 files changed, 76 insertions(+), 59 deletions(-) diff --git a/libsolidity/analysis/StaticAnalyzer.cpp b/libsolidity/analysis/StaticAnalyzer.cpp index d4de219af..6aee260e7 100644 --- a/libsolidity/analysis/StaticAnalyzer.cpp +++ b/libsolidity/analysis/StaticAnalyzer.cpp @@ -142,6 +142,7 @@ bool StaticAnalyzer::visit(MemberAccess const& _memberAccess) bool const v050 = m_currentContract->sourceUnit().annotation().experimentalFeatures.count(ExperimentalFeature::V050); if (MagicType const* type = dynamic_cast(_memberAccess.expression().annotation().type.get())) + { if (type->kind() == MagicType::Kind::Message && _memberAccess.memberName() == "gas") { if (v050) @@ -155,6 +156,20 @@ bool StaticAnalyzer::visit(MemberAccess const& _memberAccess) "\"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()\"" + ); + } + } if (m_nonPayablePublic && !m_library) if (MagicType const* type = dynamic_cast(_memberAccess.expression().annotation().type.get())) diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp index f4559edae..41700e283 100644 --- a/libsolidity/ast/Types.cpp +++ b/libsolidity/ast/Types.cpp @@ -3002,30 +3002,19 @@ bool MagicType::operator==(Type const& _other) const return other.m_kind == m_kind; } -MemberList::MemberMap MagicType::nativeMembers(ContractDefinition const* _contract) const +MemberList::MemberMap MagicType::nativeMembers(ContractDefinition const*) const { - const bool v050 = _contract->sourceUnit().annotation().experimentalFeatures.count(ExperimentalFeature::V050); switch (m_kind) { case Kind::Block: - { - std::vector members = { - {"coinbase", make_shared(160, IntegerType::Modifier::Address)}, - {"timestamp", make_shared(256)}, - {"difficulty", make_shared(256)}, - {"number", make_shared(256)}, - {"gaslimit", make_shared(256)} - }; - - if (!v050) - { - auto blockhashFun = make_shared(strings{"uint256"}, strings{"bytes32"}, - FunctionType::Kind::BlockHash, false, StateMutability::View); - - members.emplace_back("blockhash", blockhashFun); - } - return MemberList::MemberMap(members); - } + return MemberList::MemberMap({ + {"coinbase", make_shared(160, IntegerType::Modifier::Address)}, + {"timestamp", make_shared(256)}, + {"blockhash", make_shared(strings{"uint"}, strings{"bytes32"}, FunctionType::Kind::BlockHash, false, StateMutability::View)}, + {"difficulty", make_shared(256)}, + {"number", make_shared(256)}, + {"gaslimit", make_shared(256)} + }); case Kind::Message: return MemberList::MemberMap({ {"sender", make_shared(160, IntegerType::Modifier::Address)}, diff --git a/test/libsolidity/SolidityEndToEndTest.cpp b/test/libsolidity/SolidityEndToEndTest.cpp index 43d390f56..a866e46c3 100644 --- a/test/libsolidity/SolidityEndToEndTest.cpp +++ b/test/libsolidity/SolidityEndToEndTest.cpp @@ -1805,33 +1805,16 @@ BOOST_AUTO_TEST_CASE(uncalled_blockhash) BOOST_CHECK(result[0] != 0 || result[1] != 0 || result[2] != 0); } -BOOST_AUTO_TEST_CASE(blockhash_global_level) +BOOST_AUTO_TEST_CASE(blockhash_shadow_resolution) { - char const* sourceCode = R"( - contract Test { - function a() public returns (bytes32) { - return blockhash(0); - } + char const* code = R"( + contract C { + function blockhash(uint256 blockNumber) public returns(bytes32) { bytes32 x; return x; } + function f() public returns(bytes32) { return blockhash(3); } } )"; - compileAndRun(sourceCode); - BOOST_CHECK(!callContractFunction("a()").empty()); -} - -BOOST_AUTO_TEST_CASE(blockhash_shadow) -{ - char const* sourceCode = R"( - contract Test { - function blockhash(uint256 blockNumber) public returns (bytes32) { - return "abc"; - } - function f() returns (bytes32) { - return blockhash(3); - } - } - )"; - compileAndRun(sourceCode); - BOOST_REQUIRE(callContractFunction("f()") != encodeArgs("abc")); + compileAndRun(code, 0, "C"); + ABI_CHECK(callContractFunction("f()"), encodeArgs(0)); } BOOST_AUTO_TEST_CASE(log0) diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index 579fb2396..e4e7b8d82 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -8555,16 +8555,31 @@ BOOST_AUTO_TEST_CASE(require_visibility_specifiers) CHECK_ERROR(text, SyntaxError, "No visibility specified."); } -BOOST_AUTO_TEST_CASE(blockhash_not_available_in_block) +BOOST_AUTO_TEST_CASE(blockhash) { - char const* text = R"( - contract Test { - function a() public returns (bytes32) { - return block.blockhash(0); + char const* code = R"( + contract C { + function f() public view returns (bytes32) { + return block.blockhash(3); } } )"; - CHECK_ERROR(text, TypeError, "Member \"blockhash\" not found or not visible after argument-dependent lookup in block"); + 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()\""); } BOOST_AUTO_TEST_SUITE_END() diff --git a/test/libsolidity/ViewPureChecker.cpp b/test/libsolidity/ViewPureChecker.cpp index de25cfcfd..cd0a0b01c 100644 --- a/test/libsolidity/ViewPureChecker.cpp +++ b/test/libsolidity/ViewPureChecker.cpp @@ -25,6 +25,7 @@ #include #include +#include using namespace std; @@ -107,9 +108,11 @@ BOOST_AUTO_TEST_CASE(environment_access) vector view{ "block.coinbase", "block.timestamp", + "block.blockhash(7)", "block.difficulty", "block.number", "block.gaslimit", + "blockhash(7)", "gasleft()", "msg.gas", "msg.value", @@ -117,35 +120,47 @@ BOOST_AUTO_TEST_CASE(environment_access) "tx.origin", "tx.gasprice", "this", - "blockhash(7)", "address(1).balance" }; + // ``block.blockhash`` and ``blockhash`` are tested seperately below because their usage will + // produce warnings that can't be handled in a generic way. vector pure{ "msg.data", "msg.data[0]", "msg.sig", "msg", "block", - "blockhash", // Not evaluating the function "tx" }; for (string const& x: view) { CHECK_ERROR( - "contract C { function f() pure public { var x = " + x + "; x; } }", + "contract C { function f() pure public { " + x + "; } }", TypeError, "Function declared as pure, but this expression (potentially) reads from the environment or state and thus requires \"view\"" ); } for (string const& x: pure) { - CHECK_WARNING_ALLOW_MULTI( - "contract C { function f() view public { var x = " + x + "; x; } }", - (std::vector{ - "Function state mutability can be restricted to pure", - "Use of the \"var\" keyword is deprecated." - })); + CHECK_WARNING( + "contract C { function f() view public { " + x + "; } }", + "Function state mutability can be restricted to pure" + ); } + + CHECK_WARNING_ALLOW_MULTI( + "contract C { function f() view public { blockhash; } }", + (std::vector{ + "Function state mutability can be restricted to pure", + "Statement has no effect." + })); + + CHECK_WARNING_ALLOW_MULTI( + "contract C { function f() view public { block.blockhash; } }", + (std::vector{ + "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) From f8f50e14d24a85da2775662698db54a73905bd44 Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 8 Mar 2018 11:17:31 +0100 Subject: [PATCH 055/197] Test that internal functions only used by constructor are not included in runtime context. --- test/libsolidity/SolidityCompiler.cpp | 60 +++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) create mode 100644 test/libsolidity/SolidityCompiler.cpp diff --git a/test/libsolidity/SolidityCompiler.cpp b/test/libsolidity/SolidityCompiler.cpp new file mode 100644 index 000000000..e87ab6030 --- /dev/null +++ b/test/libsolidity/SolidityCompiler.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 . + */ +/** + * Unit tests for the compiler itself. + */ + +#include + +#include + +using namespace std; + +namespace dev +{ +namespace solidity +{ +namespace test +{ + +BOOST_FIXTURE_TEST_SUITE(Compiler, AnalysisFramework) + +BOOST_AUTO_TEST_CASE(does_not_include_creation_time_only_internal_functions) +{ + char const* sourceCode = R"( + contract C { + uint x; + function C() { f(); } + function f() internal { for (uint i = 0; i < 10; ++i) x += 3 + i; } + } + )"; + BOOST_REQUIRE(success(sourceCode)); + m_compiler.setOptimiserSettings(dev::test::Options::get().optimize); + BOOST_REQUIRE_MESSAGE(m_compiler.compile(), "Compiling contract failed"); + bytes const& creationBytecode = m_compiler.object("C").bytecode; + bytes const& runtimeBytecode = m_compiler.runtimeObject("C").bytecode; + BOOST_CHECK(creationBytecode.size() >= 120); + BOOST_CHECK(creationBytecode.size() <= 150); + BOOST_CHECK(runtimeBytecode.size() >= 50); + BOOST_CHECK(runtimeBytecode.size() <= 70); +} + +BOOST_AUTO_TEST_SUITE_END() + +} +} +} From 0a67d616db73c912fd16186f97be6ff2d9447975 Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 8 Mar 2018 11:17:08 +0100 Subject: [PATCH 056/197] Use shortcut for internal function calls to avoid runtime reference. --- libsolidity/codegen/ExpressionCompiler.cpp | 24 +++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/libsolidity/codegen/ExpressionCompiler.cpp b/libsolidity/codegen/ExpressionCompiler.cpp index 37069c3e7..f4ca9dfff 100644 --- a/libsolidity/codegen/ExpressionCompiler.cpp +++ b/libsolidity/codegen/ExpressionCompiler.cpp @@ -518,7 +518,25 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) arguments[i]->accept(*this); utils().convertType(*arguments[i]->annotation().type, *function.parameterTypes()[i]); } - _functionCall.expression().accept(*this); + + { + bool shortcutTaken = false; + if (auto identifier = dynamic_cast(&_functionCall.expression())) + if (auto functionDef = dynamic_cast(identifier->annotation().referencedDeclaration)) + { + // Do not directly visit the identifier, because this way, we can avoid + // the runtime entry label to be created at the creation time context. + CompilerContext::LocationSetter locationSetter2(m_context, *identifier); + m_context << m_context.functionEntryLabel(m_context.resolveVirtualFunction(*functionDef)).pushTag(); + if (m_context.runtimeContext()) + utils().leftShiftNumberOnStack(32); + shortcutTaken = true; + } + + if (!shortcutTaken) + _functionCall.expression().accept(*this); + } + unsigned parameterSize = CompilerUtils::sizeOnStack(function.parameterTypes()); if (function.bound()) { @@ -1359,6 +1377,10 @@ void ExpressionCompiler::endVisit(Identifier const& _identifier) } } else if (FunctionDefinition const* functionDef = dynamic_cast(declaration)) + // If the identifier is called right away, this code is executed in visit(FunctionCall...), because + // we want to avoid having a reference to the runtime function entry point in the + // constructor context, since this would force the compiler to include unreferenced + // internal functions in the runtime contex. utils().pushCombinedFunctionEntryLabel(m_context.resolveVirtualFunction(*functionDef)); else if (auto variable = dynamic_cast(declaration)) appendVariable(*variable, static_cast(_identifier)); From 0a58e57ceb2af6b7369742152ea3c4332e7585fb Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 8 Mar 2018 11:18:47 +0100 Subject: [PATCH 057/197] Changelog entry. --- Changelog.md | 1 + 1 file changed, 1 insertion(+) diff --git a/Changelog.md b/Changelog.md index c5577b86a..aa1554f59 100644 --- a/Changelog.md +++ b/Changelog.md @@ -5,6 +5,7 @@ Features: Bugfixes: * Code Generator: Allow ``block.blockhash`` without being called. + * Code Generator: Do not include internal functions in the runtime bytecode which are only referenced in the constructor. * Code Generator: Properly skip unneeded storage array cleanup when not reducing length. * Code Generator: Bugfix in modifier lookup in libraries. * Commandline interface: Support ``--evm-version constantinople`` properly. From fab527c4146f08971938c63074c8c92e6ff241bc Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Tue, 27 Mar 2018 03:39:37 +0100 Subject: [PATCH 058/197] Add runtimeOnly option to pushCombinedFunctionEntryLabel --- libsolidity/codegen/CompilerUtils.cpp | 9 +++++---- libsolidity/codegen/CompilerUtils.h | 3 ++- libsolidity/codegen/ExpressionCompiler.cpp | 4 +--- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/libsolidity/codegen/CompilerUtils.cpp b/libsolidity/codegen/CompilerUtils.cpp index 1bd1103b1..68f0b3a11 100644 --- a/libsolidity/codegen/CompilerUtils.cpp +++ b/libsolidity/codegen/CompilerUtils.cpp @@ -585,7 +585,7 @@ void CompilerUtils::combineExternalFunctionType(bool _leftAligned) leftShiftNumberOnStack(64); } -void CompilerUtils::pushCombinedFunctionEntryLabel(Declaration const& _function) +void CompilerUtils::pushCombinedFunctionEntryLabel(Declaration const& _function, bool _runtimeOnly) { m_context << m_context.functionEntryLabel(_function).pushTag(); // If there is a runtime context, we have to merge both labels into the same @@ -593,9 +593,10 @@ void CompilerUtils::pushCombinedFunctionEntryLabel(Declaration const& _function) if (CompilerContext* rtc = m_context.runtimeContext()) { leftShiftNumberOnStack(32); - m_context << - rtc->functionEntryLabel(_function).toSubAssemblyTag(m_context.runtimeSub()) << - Instruction::OR; + if (_runtimeOnly) + m_context << + rtc->functionEntryLabel(_function).toSubAssemblyTag(m_context.runtimeSub()) << + Instruction::OR; } } diff --git a/libsolidity/codegen/CompilerUtils.h b/libsolidity/codegen/CompilerUtils.h index 76670d472..389673ef0 100644 --- a/libsolidity/codegen/CompilerUtils.h +++ b/libsolidity/codegen/CompilerUtils.h @@ -188,7 +188,8 @@ public: /// Appends code that combines the construction-time (if available) and runtime function /// entry label of the given function into a single stack slot. /// Note: This might cause the compilation queue of the runtime context to be extended. - void pushCombinedFunctionEntryLabel(Declaration const& _function); + /// If @a _runtimeOnly, the entry label will include the runtime assembly tag. + void pushCombinedFunctionEntryLabel(Declaration const& _function, bool _runtimeOnly = true); /// Appends code for an implicit or explicit type conversion. This includes erasing higher /// order bits (@see appendHighBitCleanup) when widening integer but also copy to memory diff --git a/libsolidity/codegen/ExpressionCompiler.cpp b/libsolidity/codegen/ExpressionCompiler.cpp index f4ca9dfff..9e2d30d5e 100644 --- a/libsolidity/codegen/ExpressionCompiler.cpp +++ b/libsolidity/codegen/ExpressionCompiler.cpp @@ -527,9 +527,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) // Do not directly visit the identifier, because this way, we can avoid // the runtime entry label to be created at the creation time context. CompilerContext::LocationSetter locationSetter2(m_context, *identifier); - m_context << m_context.functionEntryLabel(m_context.resolveVirtualFunction(*functionDef)).pushTag(); - if (m_context.runtimeContext()) - utils().leftShiftNumberOnStack(32); + utils().pushCombinedFunctionEntryLabel(m_context.resolveVirtualFunction(*functionDef), false); shortcutTaken = true; } From 80458b0420b162d0cda889719e8cd645aa142a68 Mon Sep 17 00:00:00 2001 From: Erik Kundt Date: Tue, 27 Mar 2018 11:55:21 +0200 Subject: [PATCH 059/197] Explains test structure and update mechanism more detailed. --- docs/contributing.rst | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/docs/contributing.rst b/docs/contributing.rst index 08c125f98..1bcaed7c0 100644 --- a/docs/contributing.rst +++ b/docs/contributing.rst @@ -108,9 +108,10 @@ Example: ``./test/libsolidity/syntaxTests/double_stateVariable_declaration.sol`` // ---- // DeclarationError: 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. -The comments in the contract above are used to describe the expected compiler errors. 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 identifer 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``: @@ -143,10 +144,26 @@ which prints the expected result next to the obtained result, but also provides ``isoltest`` 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 remove the annotation which contains the exception not met and runs this test again. +- 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. - skip: Skips the execution of this particular test. - quit: Quits ``isoltest``. +Automatically updating the test above will change it to + +:: + + contract test { + uint256 variable; + } + // ---- + +and re-run the test. It will now pass again: + +:: + + Re-running test case... + syntaxTests/double_stateVariable_declaration.sol: OK + .. note:: From b540ba527a70df440299de9c0bf44f9d11ac6ef6 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Tue, 27 Mar 2018 14:38:28 +0100 Subject: [PATCH 060/197] Disallow empty structs --- Changelog.md | 1 + docs/grammar.txt | 2 +- libsolidity/analysis/SyntaxChecker.cpp | 7 +++++++ libsolidity/analysis/SyntaxChecker.h | 2 ++ test/libsolidity/SolidityNameAndTypeResolution.cpp | 2 +- test/libsolidity/syntaxTests/empty_struct.sol | 5 +++++ 6 files changed, 17 insertions(+), 2 deletions(-) create mode 100644 test/libsolidity/syntaxTests/empty_struct.sol diff --git a/Changelog.md b/Changelog.md index 9235ed3a2..3fa0c4856 100644 --- a/Changelog.md +++ b/Changelog.md @@ -12,6 +12,7 @@ Bugfixes: * Commandline interface: Support ``--evm-version constantinople`` properly. * DocString Parser: Fix error message for empty descriptions. * Standard JSON: Support ``constantinople`` as ``evmVersion`` properly. + * Syntax Checker: Issue error for empty structs. * Type System: Make external library functions accessible. ### 0.4.21 (2018-03-07) diff --git a/docs/grammar.txt b/docs/grammar.txt index a5c2acf35..b4ca5ca94 100644 --- a/docs/grammar.txt +++ b/docs/grammar.txt @@ -19,7 +19,7 @@ InheritanceSpecifier = UserDefinedTypeName ( '(' Expression ( ',' Expression )* StateVariableDeclaration = TypeName ( 'public' | 'internal' | 'private' | 'constant' )? Identifier ('=' Expression)? ';' UsingForDeclaration = 'using' Identifier 'for' ('*' | TypeName) ';' StructDefinition = 'struct' Identifier '{' - ( VariableDeclaration ';' (VariableDeclaration ';')* )? '}' + ( VariableDeclaration ';' (VariableDeclaration ';')* ) '}' ModifierDefinition = 'modifier' Identifier ParameterList? Block ModifierInvocation = Identifier ( '(' ExpressionList? ')' )? diff --git a/libsolidity/analysis/SyntaxChecker.cpp b/libsolidity/analysis/SyntaxChecker.cpp index ddac194b9..e03417a95 100644 --- a/libsolidity/analysis/SyntaxChecker.cpp +++ b/libsolidity/analysis/SyntaxChecker.cpp @@ -255,3 +255,10 @@ bool SyntaxChecker::visit(VariableDeclaration const& _declaration) } return true; } + +bool SyntaxChecker::visit(StructDefinition const& _struct) +{ + if (_struct.members().empty()) + 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 871bf0a98..1579df57b 100644 --- a/libsolidity/analysis/SyntaxChecker.h +++ b/libsolidity/analysis/SyntaxChecker.h @@ -71,6 +71,8 @@ private: virtual bool visit(VariableDeclaration const& _declaration) override; + virtual bool visit(StructDefinition const& _struct) override; + ErrorReporter& m_errorReporter; /// Flag that indicates whether a function modifier actually contains '_'. diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index c5abac03c..6163aed0b 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -7279,7 +7279,7 @@ BOOST_AUTO_TEST_CASE(modifiers_access_storage_pointer) { char const* text = R"( contract C { - struct S { } + struct S { uint a; } modifier m(S storage x) { x; _; diff --git a/test/libsolidity/syntaxTests/empty_struct.sol b/test/libsolidity/syntaxTests/empty_struct.sol new file mode 100644 index 000000000..87bc2ffe8 --- /dev/null +++ b/test/libsolidity/syntaxTests/empty_struct.sol @@ -0,0 +1,5 @@ +contract test { + struct A {} +} +// ---- +// SyntaxError: Defining empty structs is disallowed. From ebb12756adfe95ae694ed7e141890c6d47e8b7b4 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Tue, 27 Mar 2018 15:00:33 +0100 Subject: [PATCH 061/197] Still allow empty structs for non-0.5.0 mode --- Changelog.md | 2 +- libsolidity/analysis/SyntaxChecker.cpp | 9 ++++++++- test/libsolidity/syntaxTests/empty_struct.sol | 2 +- test/libsolidity/syntaxTests/empty_struct_050.sol | 6 ++++++ 4 files changed, 16 insertions(+), 3 deletions(-) create mode 100644 test/libsolidity/syntaxTests/empty_struct_050.sol diff --git a/Changelog.md b/Changelog.md index 3fa0c4856..e93148349 100644 --- a/Changelog.md +++ b/Changelog.md @@ -3,6 +3,7 @@ Features: * General: Support accessing dynamic return data in post-byzantium EVMs. * Interfaces: Allow overriding external functions in interfaces with public in an implementing contract. + * Syntax Checker: Issue warning for empty structs (or error as experimental 0.5.0 feature). Bugfixes: * Code Generator: Allow ``block.blockhash`` without being called. @@ -12,7 +13,6 @@ Bugfixes: * Commandline interface: Support ``--evm-version constantinople`` properly. * DocString Parser: Fix error message for empty descriptions. * Standard JSON: Support ``constantinople`` as ``evmVersion`` properly. - * Syntax Checker: Issue error for empty structs. * Type System: Make external library functions accessible. ### 0.4.21 (2018-03-07) diff --git a/libsolidity/analysis/SyntaxChecker.cpp b/libsolidity/analysis/SyntaxChecker.cpp index e03417a95..3a32810bd 100644 --- a/libsolidity/analysis/SyntaxChecker.cpp +++ b/libsolidity/analysis/SyntaxChecker.cpp @@ -258,7 +258,14 @@ bool SyntaxChecker::visit(VariableDeclaration const& _declaration) bool SyntaxChecker::visit(StructDefinition const& _struct) { + bool const v050 = m_sourceUnit->annotation().experimentalFeatures.count(ExperimentalFeature::V050); + if (_struct.members().empty()) - m_errorReporter.syntaxError(_struct.location(), "Defining empty structs is disallowed."); + { + if (v050) + m_errorReporter.syntaxError(_struct.location(), "Defining empty structs is disallowed."); + else + m_errorReporter.warning(_struct.location(), "Defining empty structs is deprecated."); + } return true; } diff --git a/test/libsolidity/syntaxTests/empty_struct.sol b/test/libsolidity/syntaxTests/empty_struct.sol index 87bc2ffe8..dcced618a 100644 --- a/test/libsolidity/syntaxTests/empty_struct.sol +++ b/test/libsolidity/syntaxTests/empty_struct.sol @@ -2,4 +2,4 @@ contract test { struct A {} } // ---- -// SyntaxError: Defining empty structs is disallowed. +// Warning: Defining empty structs is deprecated. diff --git a/test/libsolidity/syntaxTests/empty_struct_050.sol b/test/libsolidity/syntaxTests/empty_struct_050.sol new file mode 100644 index 000000000..dbec93c49 --- /dev/null +++ b/test/libsolidity/syntaxTests/empty_struct_050.sol @@ -0,0 +1,6 @@ +pragma experimental "v0.5.0"; +contract test { + struct A {} +} +// ---- +// SyntaxError: Defining empty structs is disallowed. From 8a18f22b878b86fff8e2f49a99397320180d4b22 Mon Sep 17 00:00:00 2001 From: Anthony Broad-Crawford Date: Fri, 16 Mar 2018 10:52:04 -0500 Subject: [PATCH 062/197] Support for error on non-existant or irregular files with command line option to ignore --- Changelog.md | 1 + solc/CommandLineInterface.cpp | 31 ++++++++++++++++++++++++++----- solc/CommandLineInterface.h | 2 +- test/cmdlineTests.sh | 2 +- 4 files changed, 29 insertions(+), 7 deletions(-) diff --git a/Changelog.md b/Changelog.md index 5cd4c2f85..a20a9a980 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,6 +1,7 @@ ### 0.4.22 (unreleased) Features: + * Commandline interface: Error when missing or inaccessible file detected. Suppress it with the ``--ignore-missing`` flag. * General: Support accessing dynamic return data in post-byzantium EVMs. * Interfaces: Allow overriding external functions in interfaces with public in an implementing contract. diff --git a/solc/CommandLineInterface.cpp b/solc/CommandLineInterface.cpp index d3d234c34..8f81e7994 100644 --- a/solc/CommandLineInterface.cpp +++ b/solc/CommandLineInterface.cpp @@ -116,6 +116,7 @@ static string const g_strStandardJSON = "standard-json"; static string const g_strStrictAssembly = "strict-assembly"; static string const g_strPrettyJson = "pretty-json"; static string const g_strVersion = "version"; +static string const g_strIgnoreMissingFiles = "ignore-missing"; static string const g_argAbi = g_strAbi; static string const g_argPrettyJson = g_strPrettyJson; @@ -152,6 +153,7 @@ static string const g_argStandardJSON = g_strStandardJSON; static string const g_argStrictAssembly = g_strStrictAssembly; static string const g_argVersion = g_strVersion; static string const g_stdinFileName = g_stdinFileNameStr; +static string const g_argIgnoreMissingFiles = g_strIgnoreMissingFiles; /// Possible arguments to for --combined-json static set const g_combinedJsonArgs @@ -398,8 +400,9 @@ void CommandLineInterface::handleGasEstimation(string const& _contract) } } -void CommandLineInterface::readInputFilesAndConfigureRemappings() +bool CommandLineInterface::readInputFilesAndConfigureRemappings() { + bool ignoreMissing = m_args.count(g_argIgnoreMissingFiles); bool addStdin = false; if (!m_args.count(g_argInputFile)) addStdin = true; @@ -416,13 +419,27 @@ void CommandLineInterface::readInputFilesAndConfigureRemappings() auto infile = boost::filesystem::path(path); if (!boost::filesystem::exists(infile)) { - cerr << "Skipping non-existent input file \"" << infile << "\"" << endl; + if (!ignoreMissing) + { + cerr << "\"" << infile << "\" is not found" << endl; + return false; + } + else + cerr << "\"" << infile << "\" is not found. Skipping." << endl; + continue; } if (!boost::filesystem::is_regular_file(infile)) { - cerr << "\"" << infile << "\" is not a valid file. Skipping" << endl; + if (!ignoreMissing) + { + cerr << "\"" << infile << "\" is not a valid file" << endl; + return false; + } + else + cerr << "\"" << infile << "\" is not a valid file. Skipping." << endl; + continue; } @@ -433,6 +450,8 @@ void CommandLineInterface::readInputFilesAndConfigureRemappings() } if (addStdin) m_sourceCodes[g_stdinFileName] = dev::readStandardInput(); + + return true; } bool CommandLineInterface::parseLibraryOption(string const& _input) @@ -599,7 +618,8 @@ Allowed options)", g_argAllowPaths.c_str(), po::value()->value_name("path(s)"), "Allow a given path for imports. A list of paths can be supplied by separating them with a comma." - ); + ) + (g_argIgnoreMissingFiles.c_str(), "Ignore missing files."); po::options_description outputComponents("Output Components"); outputComponents.add_options() (g_argAst.c_str(), "AST of all source files.") @@ -741,7 +761,8 @@ bool CommandLineInterface::processInput() return true; } - readInputFilesAndConfigureRemappings(); + if (!readInputFilesAndConfigureRemappings()) + return false; if (m_args.count(g_argLibraries)) for (string const& library: m_args[g_argLibraries].as>()) diff --git a/solc/CommandLineInterface.h b/solc/CommandLineInterface.h index 303023fcb..45ec1eb58 100644 --- a/solc/CommandLineInterface.h +++ b/solc/CommandLineInterface.h @@ -72,7 +72,7 @@ private: void handleFormal(); /// Fills @a m_sourceCodes initially and @a m_redirects. - void readInputFilesAndConfigureRemappings(); + bool readInputFilesAndConfigureRemappings(); /// Tries to read from the file @a _input or interprets _input literally if that fails. /// It then tries to parse the contents and appends to m_libraries. bool parseLibraryOption(std::string const& _input); diff --git a/test/cmdlineTests.sh b/test/cmdlineTests.sh index 92f9569a1..1137c7b05 100755 --- a/test/cmdlineTests.sh +++ b/test/cmdlineTests.sh @@ -32,7 +32,7 @@ REPO_ROOT=$(cd $(dirname "$0")/.. && pwd) echo $REPO_ROOT SOLC="$REPO_ROOT/build/solc/solc" -FULLARGS="--optimize --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,clone-bin,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 From 5c8a6aac698f6d084a22c6ec9f282b3f26ddb8bb Mon Sep 17 00:00:00 2001 From: chriseth Date: Fri, 16 Mar 2018 16:06:40 +0100 Subject: [PATCH 063/197] Prevent encoding of weird types and support packed encoding of external function types. --- Changelog.md | 2 ++ libsolidity/analysis/TypeChecker.cpp | 11 ++++++++ libsolidity/ast/Types.cpp | 2 ++ libsolidity/ast/Types.h | 11 +++++--- libsolidity/codegen/CompilerUtils.cpp | 1 - test/libsolidity/SolidityEndToEndTest.cpp | 25 +++++++++++++++++++ ...nspecified_encoding_internal_functions.sol | 11 ++++++++ ...ith_unspecified_encoding_special_types.sol | 13 ++++++++++ ...ypes_with_unspecified_encoding_structs.sol | 13 ++++++++++ .../types_with_unspecified_encoding_types.sol | 16 ++++++++++++ .../types_without_encoding_problems.sol | 9 +++++++ 11 files changed, 110 insertions(+), 4 deletions(-) create mode 100644 test/libsolidity/syntaxTests/specialFunctions/types_with_unspecified_encoding_internal_functions.sol create mode 100644 test/libsolidity/syntaxTests/specialFunctions/types_with_unspecified_encoding_special_types.sol create mode 100644 test/libsolidity/syntaxTests/specialFunctions/types_with_unspecified_encoding_structs.sol create mode 100644 test/libsolidity/syntaxTests/specialFunctions/types_with_unspecified_encoding_types.sol create mode 100644 test/libsolidity/syntaxTests/specialFunctions/types_without_encoding_problems.sol diff --git a/Changelog.md b/Changelog.md index a20a9a980..af09f8045 100644 --- a/Changelog.md +++ b/Changelog.md @@ -10,11 +10,13 @@ Bugfixes: * Code Generator: Do not include internal functions in the runtime bytecode which are only referenced in the constructor. * Code Generator: Properly skip unneeded storage array cleanup when not reducing length. * Code Generator: Bugfix in modifier lookup in libraries. + * Code Generator: Implement packed encoding of external function types. * Commandline interface: Support ``--evm-version constantinople`` properly. * DocString Parser: Fix error message for empty descriptions. * Standard JSON: Support ``constantinople`` as ``evmVersion`` properly. * Type System: Improve error message when attempting to shift by a fractional amount. * Type System: Make external library functions accessible. + * Type System: Prevent encoding of weird types. ### 0.4.21 (2018-03-07) diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index 6e287f838..620dfca48 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -1644,9 +1644,20 @@ bool TypeChecker::visit(FunctionCall const& _functionCall) auto const& argType = type(*arguments[i]); if (functionType->takesArbitraryParameters()) { + bool errored = false; if (auto t = dynamic_cast(argType.get())) if (!t->mobileType()) + { m_errorReporter.typeError(arguments[i]->location(), "Invalid rational number (too large or division by zero)."); + errored = true; + } + if (!errored && !( + argType->mobileType() && + argType->mobileType()->interfaceType(false) && + argType->mobileType()->interfaceType(false)->encodingType() && + !(dynamic_cast(argType->mobileType()->interfaceType(false)->encodingType().get())) + )) + m_errorReporter.typeError(arguments[i]->location(), "This type cannot be encoded."); } else if (!type(*arguments[i])->isImplicitlyConvertibleTo(*parameterTypes[i])) m_errorReporter.typeError( diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp index 720215c90..4c462d098 100644 --- a/libsolidity/ast/Types.cpp +++ b/libsolidity/ast/Types.cpp @@ -1262,6 +1262,8 @@ bool ContractType::isPayable() const TypePointer ContractType::unaryOperatorResult(Token::Value _operator) const { + if (isSuper()) + return TypePointer{}; return _operator == Token::Delete ? make_shared() : TypePointer(); } diff --git a/libsolidity/ast/Types.h b/libsolidity/ast/Types.h index c930dd240..b7e648918 100644 --- a/libsolidity/ast/Types.h +++ b/libsolidity/ast/Types.h @@ -692,22 +692,27 @@ public: virtual bool operator==(Type const& _other) const override; virtual unsigned calldataEncodedSize(bool _padded ) const override { + solAssert(!isSuper(), ""); return encodingType()->calldataEncodedSize(_padded); } - virtual unsigned storageBytes() const override { return 20; } - virtual bool canLiveOutsideStorage() const override { return true; } + virtual unsigned storageBytes() const override { solAssert(!isSuper(), ""); return 20; } + virtual bool canLiveOutsideStorage() const override { return !isSuper(); } virtual unsigned sizeOnStack() const override { return m_super ? 0 : 1; } - virtual bool isValueType() const override { return true; } + virtual bool isValueType() const override { return !isSuper(); } virtual std::string toString(bool _short) const override; virtual std::string canonicalName() const override; virtual MemberList::MemberMap nativeMembers(ContractDefinition const* _currentScope) const override; virtual TypePointer encodingType() const override { + if (isSuper()) + return TypePointer{}; return std::make_shared(160, IntegerType::Modifier::Address); } virtual TypePointer interfaceType(bool _inLibrary) const override { + if (isSuper()) + return TypePointer{}; return _inLibrary ? shared_from_this() : encodingType(); } diff --git a/libsolidity/codegen/CompilerUtils.cpp b/libsolidity/codegen/CompilerUtils.cpp index 68f0b3a11..676d5d4e3 100644 --- a/libsolidity/codegen/CompilerUtils.cpp +++ b/libsolidity/codegen/CompilerUtils.cpp @@ -142,7 +142,6 @@ void CompilerUtils::storeInMemoryDynamic(Type const& _type, bool _padToWordBound dynamic_cast(_type).kind() == FunctionType::Kind::External ) { - solUnimplementedAssert(_padToWordBoundaries, "Non-padded store for function not implemented."); combineExternalFunctionType(true); m_context << Instruction::DUP2 << Instruction::MSTORE; m_context << u256(_padToWordBoundaries ? 32 : 24) << Instruction::ADD; diff --git a/test/libsolidity/SolidityEndToEndTest.cpp b/test/libsolidity/SolidityEndToEndTest.cpp index a866e46c3..f5813aed1 100644 --- a/test/libsolidity/SolidityEndToEndTest.cpp +++ b/test/libsolidity/SolidityEndToEndTest.cpp @@ -2077,6 +2077,31 @@ BOOST_AUTO_TEST_CASE(packed_keccak256) testContractAgainstCpp("a(bytes32)", f, u256(-1)); } +BOOST_AUTO_TEST_CASE(packed_keccak256_complex_types) +{ + char const* sourceCode = R"( + contract test { + uint120[3] x; + function f() view 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); + } + } + )"; + compileAndRun(sourceCode); + // Strangely, arrays are encoded with intra-element padding. + ABI_CHECK(callContractFunction("f()"), encodeArgs( + dev::keccak256(encodeArgs(u256("0xfffffffffffffffffffffffffffffe"), u256("0xfffffffffffffffffffffffffffffd"), u256("0xfffffffffffffffffffffffffffffc"))), + dev::keccak256(encodeArgs(u256("0xfffffffffffffffffffffffffffffe"), u256("0xfffffffffffffffffffffffffffffd"), u256("0xfffffffffffffffffffffffffffffc"))), + dev::keccak256(fromHex(m_contractAddress.hex() + "26121ff0")) + )); +} + BOOST_AUTO_TEST_CASE(packed_sha256) { char const* sourceCode = R"( 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 new file mode 100644 index 000000000..9f57c3a4a --- /dev/null +++ b/test/libsolidity/syntaxTests/specialFunctions/types_with_unspecified_encoding_internal_functions.sol @@ -0,0 +1,11 @@ +contract C { + function f() public pure { + bytes32 h = keccak256(keccak256, f, this.f.gas, block.blockhash); + h; + } +} +// ---- +// TypeError: This type cannot be encoded. +// TypeError: This type cannot be encoded. +// TypeError: This type cannot be encoded. +// TypeError: 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 new file mode 100644 index 000000000..a7d13215b --- /dev/null +++ b/test/libsolidity/syntaxTests/specialFunctions/types_with_unspecified_encoding_special_types.sol @@ -0,0 +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; + } +} +// ---- +// TypeError: This type cannot be encoded. +// TypeError: This type cannot be encoded. +// TypeError: This type cannot be encoded. +// TypeError: This type cannot be encoded. +// TypeError: 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 new file mode 100644 index 000000000..f27290fdc --- /dev/null +++ b/test/libsolidity/syntaxTests/specialFunctions/types_with_unspecified_encoding_structs.sol @@ -0,0 +1,13 @@ +contract C { + struct S { uint x; } + S s; + struct T { } + T t; + function f() public pure { + bytes32 a = sha256(s, t); + a; + } +} +// ---- +// TypeError: This type cannot be encoded. +// TypeError: 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 new file mode 100644 index 000000000..991b2237a --- /dev/null +++ b/test/libsolidity/syntaxTests/specialFunctions/types_with_unspecified_encoding_types.sol @@ -0,0 +1,16 @@ +contract C { + struct S { uint x; } + S s; + struct T { } + T t; + enum A { X, Y } + function f() public pure { + bool a = address(this).delegatecall(S, A, A.X, T, uint, uint[]); + } +} +// ---- +// TypeError: This type cannot be encoded. +// TypeError: This type cannot be encoded. +// TypeError: This type cannot be encoded. +// TypeError: This type cannot be encoded. +// TypeError: This type cannot be encoded. diff --git a/test/libsolidity/syntaxTests/specialFunctions/types_without_encoding_problems.sol b/test/libsolidity/syntaxTests/specialFunctions/types_without_encoding_problems.sol new file mode 100644 index 000000000..c83645486 --- /dev/null +++ b/test/libsolidity/syntaxTests/specialFunctions/types_without_encoding_problems.sol @@ -0,0 +1,9 @@ +contract C { + uint[3] sarr; + function f() view public { + uint[3] memory arr; + bytes32 h = keccak256(this.f, arr, sarr); + h; + } +} +// ---- From 8aadc179ae9f47a7d56c85b2af492318dc571d5e Mon Sep 17 00:00:00 2001 From: wbt Date: Fri, 30 Mar 2018 15:28:15 -0400 Subject: [PATCH 064/197] Fix small formatting issue in which a constant was not treated as code, inconsistent with surrounding examples. --- docs/style-guide.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/style-guide.rst b/docs/style-guide.rst index 2261746f6..ee1ea4bd2 100644 --- a/docs/style-guide.rst +++ b/docs/style-guide.rst @@ -904,7 +904,7 @@ Constants ========= Constants should be named with all capital letters with underscores separating -words. Examples: ``MAX_BLOCKS``, `TOKEN_NAME`, ``TOKEN_TICKER``, ``CONTRACT_VERSION``. +words. Examples: ``MAX_BLOCKS``, ``TOKEN_NAME``, ``TOKEN_TICKER``, ``CONTRACT_VERSION``. Modifier Names From 076c0754ea8e37f20ba28c25864053f129176081 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Sat, 31 Mar 2018 00:27:01 +0100 Subject: [PATCH 065/197] Update tests for empty structs --- .../specialFunctions/types_with_unspecified_encoding_structs.sol | 1 + .../specialFunctions/types_with_unspecified_encoding_types.sol | 1 + 2 files changed, 2 insertions(+) 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 f27290fdc..378155e98 100644 --- a/test/libsolidity/syntaxTests/specialFunctions/types_with_unspecified_encoding_structs.sol +++ b/test/libsolidity/syntaxTests/specialFunctions/types_with_unspecified_encoding_structs.sol @@ -9,5 +9,6 @@ contract C { } } // ---- +// Warning: Defining empty structs is deprecated. // TypeError: This type cannot be encoded. // TypeError: 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 991b2237a..6e073fd88 100644 --- a/test/libsolidity/syntaxTests/specialFunctions/types_with_unspecified_encoding_types.sol +++ b/test/libsolidity/syntaxTests/specialFunctions/types_with_unspecified_encoding_types.sol @@ -9,6 +9,7 @@ contract C { } } // ---- +// Warning: Defining empty structs is deprecated. // TypeError: This type cannot be encoded. // TypeError: This type cannot be encoded. // TypeError: This type cannot be encoded. From e37b619593cc926873279dc1d5f532a7a13b6fa4 Mon Sep 17 00:00:00 2001 From: Jason Cobb Date: Fri, 16 Feb 2018 10:11:07 -0500 Subject: [PATCH 066/197] Set default fixed point decimal places to 18 --- docs/abi-spec.rst | 6 +++--- docs/types.rst | 2 +- libsolidity/ast/Types.cpp | 4 ++-- test/libsolidity/SolidityNameAndTypeResolution.cpp | 10 +++++----- 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/docs/abi-spec.rst b/docs/abi-spec.rst index 4d84a7dae..98301fdc4 100644 --- a/docs/abi-spec.rst +++ b/docs/abi-spec.rst @@ -54,7 +54,7 @@ The following elementary types exist: - ``ufixedx``: unsigned variant of ``fixedx``. -- ``fixed``, ``ufixed``: synonyms for ``fixed128x19``, ``ufixed128x19`` respectively. For computing the function selector, ``fixed128x19`` and ``ufixed128x19`` have to be used. +- ``fixed``, ``ufixed``: synonyms for ``fixed128x18``, ``ufixed128x18`` respectively. For computing the function selector, ``fixed128x18`` and ``ufixed128x18`` have to be used. - ``bytes``: binary type of ``M`` bytes, ``0 < M <= 32``. @@ -164,9 +164,9 @@ on the type of ``X`` being - ``int``: ``enc(X)`` is the big-endian two's complement encoding of ``X``, padded on the higher-order (left) side with ``0xff`` for negative ``X`` and with zero bytes for positive ``X`` such that the length is 32 bytes. - ``bool``: as in the ``uint8`` case, where ``1`` is used for ``true`` and ``0`` for ``false`` - ``fixedx``: ``enc(X)`` is ``enc(X * 10**N)`` where ``X * 10**N`` is interpreted as a ``int256``. -- ``fixed``: as in the ``fixed128x19`` case +- ``fixed``: as in the ``fixed128x18`` case - ``ufixedx``: ``enc(X)`` is ``enc(X * 10**N)`` where ``X * 10**N`` is interpreted as a ``uint256``. -- ``ufixed``: as in the ``ufixed128x19`` case +- ``ufixed``: as in the ``ufixed128x18`` case - ``bytes``: ``enc(X)`` is the sequence of bytes in ``X`` padded with trailing zero-bytes to a length of 32 bytes. Note that for any ``X``, ``len(enc(X))`` is a multiple of 32. diff --git a/docs/types.rst b/docs/types.rst index e704687ee..5de6d07e9 100644 --- a/docs/types.rst +++ b/docs/types.rst @@ -81,7 +81,7 @@ Fixed Point Numbers ``fixed`` / ``ufixed``: Signed and unsigned fixed point number of various sizes. Keywords ``ufixedMxN`` and ``fixedMxN``, where ``M`` represents the number of bits taken by the type and ``N`` represents how many decimal points are available. ``M`` must be divisible by 8 and goes from 8 to 256 bits. ``N`` must be between 0 and 80, inclusive. -``ufixed`` and ``fixed`` are aliases for ``ufixed128x19`` and ``fixed128x19``, respectively. +``ufixed`` and ``fixed`` are aliases for ``ufixed128x18`` and ``fixed128x18``, respectively. Operators: diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp index 4c462d098..42fd1c3da 100644 --- a/libsolidity/ast/Types.cpp +++ b/libsolidity/ast/Types.cpp @@ -208,9 +208,9 @@ TypePointer Type::fromElementaryTypeName(ElementaryTypeNameToken const& _type) case Token::UInt: return make_shared(256, IntegerType::Modifier::Unsigned); case Token::Fixed: - return make_shared(128, 19, FixedPointType::Modifier::Signed); + return make_shared(128, 18, FixedPointType::Modifier::Signed); case Token::UFixed: - return make_shared(128, 19, FixedPointType::Modifier::Unsigned); + return make_shared(128, 18, FixedPointType::Modifier::Unsigned); case Token::Byte: return make_shared(1); case Token::Address: diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index dd29bf48e..f18c906d4 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -2452,8 +2452,8 @@ BOOST_AUTO_TEST_CASE(test_fromElementaryTypeName) BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::BytesM, 31, 0)) == *make_shared(31)); BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::BytesM, 32, 0)) == *make_shared(32)); - BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::Fixed, 0, 0)) == *make_shared(128, 19, FixedPointType::Modifier::Signed)); - BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::UFixed, 0, 0)) == *make_shared(128, 19, FixedPointType::Modifier::Unsigned)); + BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::Fixed, 0, 0)) == *make_shared(128, 18, FixedPointType::Modifier::Signed)); + BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::UFixed, 0, 0)) == *make_shared(128, 18, FixedPointType::Modifier::Unsigned)); } BOOST_AUTO_TEST_CASE(test_byte_is_alias_of_byte1) @@ -4471,7 +4471,7 @@ BOOST_AUTO_TEST_CASE(invalid_int_implicit_conversion_from_fixed) } } )"; - CHECK_ERROR(text, TypeError, "Type fixed128x19 is not implicitly convertible to expected type int256"); + CHECK_ERROR(text, TypeError, "Type fixed128x18 is not implicitly convertible to expected type int256"); } BOOST_AUTO_TEST_CASE(rational_unary_operation) @@ -4589,7 +4589,7 @@ BOOST_AUTO_TEST_CASE(fixed_type_invalid_implicit_conversion_size) } } )"; - CHECK_ERROR(text, TypeError, "Type ufixed128x19 is not implicitly convertible to expected type ufixed248x8"); + 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) @@ -4676,7 +4676,7 @@ BOOST_AUTO_TEST_CASE(fixed_to_bytes_implicit_conversion) } } )"; - CHECK_ERROR(text, TypeError, "fixed128x19 is not implicitly convertible to expected type bytes32"); + CHECK_ERROR(text, TypeError, "fixed128x18 is not implicitly convertible to expected type bytes32"); } BOOST_AUTO_TEST_CASE(mapping_with_fixed_literal) From 29df18d4d79f35233dfc5dabb8ab95dd17580417 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Sat, 31 Mar 2018 01:11:56 +0100 Subject: [PATCH 067/197] Remove brew linkapps from the installation instructions --- docs/installing-solidity.rst | 1 - 1 file changed, 1 deletion(-) diff --git a/docs/installing-solidity.rst b/docs/installing-solidity.rst index e26870f03..6726ded90 100644 --- a/docs/installing-solidity.rst +++ b/docs/installing-solidity.rst @@ -122,7 +122,6 @@ We will re-add the pre-built bottles soon. brew upgrade brew tap ethereum/ethereum brew install solidity - brew linkapps solidity If you need a specific version of Solidity you can install a Homebrew formula directly from Github. From 884ea39d858c517f82196ecb2cf53bf10f46aa55 Mon Sep 17 00:00:00 2001 From: kevinflo Date: Mon, 2 Apr 2018 13:57:19 +0900 Subject: [PATCH 068/197] Removed documentation reference to the now-depricated var tuple variable assignment syntax --- docs/control-structures.rst | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/docs/control-structures.rst b/docs/control-structures.rst index 46e076e53..cdc2d20d4 100644 --- a/docs/control-structures.rst +++ b/docs/control-structures.rst @@ -284,10 +284,12 @@ Solidity internally allows tuple types, i.e. a list of objects of potentially di } function g() public { - // Declares and assigns the variables. Specifying the type explicitly is not possible. - var (x, b, y) = f(); - // Assigns to a pre-existing variable. - (x, y) = (2, 7); + // Variables declared with type + uint x; + bool b; + uint y; + // These pre-existing variables can then be assigned to the tuple values + (x, b, 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). From 826de65e2d9db8f05879a6f4557a9c50936610d1 Mon Sep 17 00:00:00 2001 From: Haoliang Yu Date: Mon, 2 Apr 2018 22:22:38 -0400 Subject: [PATCH 069/197] fix a wrong number --- docs/contracts.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/contracts.rst b/docs/contracts.rst index 121c4de0e..8cc4f6b2b 100644 --- a/docs/contracts.rst +++ b/docs/contracts.rst @@ -679,7 +679,7 @@ candidate, resolution fails. } } -Calling ``f(50)`` would create a type error since ``250`` can be implicitly converted both to ``uint8`` +Calling ``f(50)`` would create a type error since ``50`` can be implicitly converted both to ``uint8`` and ``uint256`` types. On another hand ``f(256)`` would resolve to ``f(uint256)`` overload as ``256`` cannot be implicitly converted to ``uint8``. From 8fe1cfb12ef49a74eaebed56d160e88cfd9a4de2 Mon Sep 17 00:00:00 2001 From: bitshift Date: Fri, 9 Mar 2018 17:38:17 +0100 Subject: [PATCH 070/197] Defaults to external visibility for interfaces. --- libsolidity/analysis/StaticAnalyzer.cpp | 8 ++++++++ libsolidity/analysis/SyntaxChecker.cpp | 15 +++------------ .../visibility/interface/function_default.sol | 6 ++++++ .../visibility/interface/function_default050.sol | 7 +++++++ 4 files changed, 24 insertions(+), 12 deletions(-) create mode 100644 test/libsolidity/syntaxTests/visibility/interface/function_default.sol create mode 100644 test/libsolidity/syntaxTests/visibility/interface/function_default050.sol diff --git a/libsolidity/analysis/StaticAnalyzer.cpp b/libsolidity/analysis/StaticAnalyzer.cpp index 6aee260e7..20464765b 100644 --- a/libsolidity/analysis/StaticAnalyzer.cpp +++ b/libsolidity/analysis/StaticAnalyzer.cpp @@ -50,6 +50,14 @@ 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 \"" + + (isInterface ? "external" : Declaration::visibilityToString(_function.visibility())) + + "\"." + ); if (_function.isImplemented()) m_currentFunction = &_function; else diff --git a/libsolidity/analysis/SyntaxChecker.cpp b/libsolidity/analysis/SyntaxChecker.cpp index ddac194b9..5de9a2705 100644 --- a/libsolidity/analysis/SyntaxChecker.cpp +++ b/libsolidity/analysis/SyntaxChecker.cpp @@ -214,18 +214,9 @@ bool SyntaxChecker::visit(FunctionDefinition const& _function) { bool const v050 = m_sourceUnit->annotation().experimentalFeatures.count(ExperimentalFeature::V050); - if (_function.noVisibilitySpecified()) - { - if (v050) - m_errorReporter.syntaxError(_function.location(), "No visibility specified."); - else - m_errorReporter.warning( - _function.location(), - "No visibility specified. Defaulting to \"" + - Declaration::visibilityToString(_function.visibility()) + - "\"." - ); - } + if (v050 && _function.noVisibilitySpecified()) + m_errorReporter.syntaxError(_function.location(), "No visibility specified."); + return true; } diff --git a/test/libsolidity/syntaxTests/visibility/interface/function_default.sol b/test/libsolidity/syntaxTests/visibility/interface/function_default.sol new file mode 100644 index 000000000..9889d038a --- /dev/null +++ b/test/libsolidity/syntaxTests/visibility/interface/function_default.sol @@ -0,0 +1,6 @@ +interface I { + function f(); +} +// ---- +// Warning: Functions in interfaces should be declared external. +// Warning: No visibility specified. Defaulting to "external". diff --git a/test/libsolidity/syntaxTests/visibility/interface/function_default050.sol b/test/libsolidity/syntaxTests/visibility/interface/function_default050.sol new file mode 100644 index 000000000..127d4e92a --- /dev/null +++ b/test/libsolidity/syntaxTests/visibility/interface/function_default050.sol @@ -0,0 +1,7 @@ +pragma experimental "v0.5.0"; +interface I { + function f(); +} +// ---- +// SyntaxError: No visibility specified. +// TypeError: Functions in interfaces must be declared external. From 601659c3844767686a324ea363498e85320c92b1 Mon Sep 17 00:00:00 2001 From: Erik Kundt Date: Fri, 23 Mar 2018 15:09:45 +0100 Subject: [PATCH 071/197] Moves all interface function visibility related tests. --- .../SolidityNameAndTypeResolution.cpp | 48 ------------------- .../interface/function_external050.sol | 5 ++ .../interface/function_internal.sol | 5 ++ .../visibility/interface/function_private.sol | 5 ++ .../visibility/interface/function_public.sol | 5 ++ .../interface/function_public050.sol | 6 +++ 6 files changed, 26 insertions(+), 48 deletions(-) create mode 100644 test/libsolidity/syntaxTests/visibility/interface/function_external050.sol create mode 100644 test/libsolidity/syntaxTests/visibility/interface/function_internal.sol create mode 100644 test/libsolidity/syntaxTests/visibility/interface/function_private.sol create mode 100644 test/libsolidity/syntaxTests/visibility/interface/function_public.sol create mode 100644 test/libsolidity/syntaxTests/visibility/interface/function_public050.sol diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index e5269bc5f..1e98d50d1 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -6429,54 +6429,6 @@ BOOST_AUTO_TEST_CASE(interface_function_bodies) CHECK_ERROR(text, TypeError, "Functions in interfaces cannot have an implementation"); } -BOOST_AUTO_TEST_CASE(interface_function_external) -{ - char const* text = R"( - pragma experimental "v0.5.0"; - interface I { - function f() external; - } - )"; - CHECK_SUCCESS(text); -} - -BOOST_AUTO_TEST_CASE(interface_function_public) -{ - char const* text = R"( - interface I { - function f() public; - } - )"; - CHECK_WARNING(text, "Functions in interfaces should be declared external."); - text = R"( - pragma experimental "v0.5.0"; - interface I { - function f() public; - } - )"; - CHECK_ERROR(text, TypeError, "Functions in interfaces must be declared external."); -} - -BOOST_AUTO_TEST_CASE(interface_function_internal) -{ - char const* text = R"( - interface I { - function f() internal; - } - )"; - CHECK_ERROR(text, TypeError, "Functions in interfaces cannot be internal or private."); -} - -BOOST_AUTO_TEST_CASE(interface_function_private) -{ - char const* text = R"( - interface I { - function f() private; - } - )"; - CHECK_ERROR(text, TypeError, "Functions in interfaces cannot be internal or private."); -} - BOOST_AUTO_TEST_CASE(interface_events) { char const* text = R"( diff --git a/test/libsolidity/syntaxTests/visibility/interface/function_external050.sol b/test/libsolidity/syntaxTests/visibility/interface/function_external050.sol new file mode 100644 index 000000000..3f0a9acab --- /dev/null +++ b/test/libsolidity/syntaxTests/visibility/interface/function_external050.sol @@ -0,0 +1,5 @@ +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 new file mode 100644 index 000000000..a1cf52463 --- /dev/null +++ b/test/libsolidity/syntaxTests/visibility/interface/function_internal.sol @@ -0,0 +1,5 @@ +interface I { + function f() internal; +} +// ---- +// TypeError: Functions in interfaces cannot be internal or private. diff --git a/test/libsolidity/syntaxTests/visibility/interface/function_private.sol b/test/libsolidity/syntaxTests/visibility/interface/function_private.sol new file mode 100644 index 000000000..887ebd4bf --- /dev/null +++ b/test/libsolidity/syntaxTests/visibility/interface/function_private.sol @@ -0,0 +1,5 @@ +interface I { + function f() private; +} +// ---- +// TypeError: Functions in interfaces cannot be internal or private. diff --git a/test/libsolidity/syntaxTests/visibility/interface/function_public.sol b/test/libsolidity/syntaxTests/visibility/interface/function_public.sol new file mode 100644 index 000000000..146d4f5b5 --- /dev/null +++ b/test/libsolidity/syntaxTests/visibility/interface/function_public.sol @@ -0,0 +1,5 @@ +interface I { + function f() public; +} +// ---- +// Warning: Functions in interfaces should be declared external. \ No newline at end of file diff --git a/test/libsolidity/syntaxTests/visibility/interface/function_public050.sol b/test/libsolidity/syntaxTests/visibility/interface/function_public050.sol new file mode 100644 index 000000000..f957f0b4c --- /dev/null +++ b/test/libsolidity/syntaxTests/visibility/interface/function_public050.sol @@ -0,0 +1,6 @@ +pragma experimental "v0.5.0"; +interface I { + function f() public; +} +// ---- +// TypeError: Functions in interfaces must be declared external. \ No newline at end of file From f9efa417492916546d23115da7a55e86090d47dd Mon Sep 17 00:00:00 2001 From: Erik Kundt Date: Wed, 28 Mar 2018 18:10:32 +0200 Subject: [PATCH 072/197] Makes visibility warning more concise. --- libsolidity/analysis/StaticAnalyzer.cpp | 6 ++++-- .../syntaxTests/visibility/interface/function_default.sol | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/libsolidity/analysis/StaticAnalyzer.cpp b/libsolidity/analysis/StaticAnalyzer.cpp index 20464765b..d96f87484 100644 --- a/libsolidity/analysis/StaticAnalyzer.cpp +++ b/libsolidity/analysis/StaticAnalyzer.cpp @@ -51,12 +51,14 @@ 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 \"" + - (isInterface ? "external" : Declaration::visibilityToString(_function.visibility())) + - "\"." + Declaration::visibilityToString(_function.visibility()) + + "\". " + + (isInterface ? "In interfaces it defaults to external." : "") ); if (_function.isImplemented()) m_currentFunction = &_function; diff --git a/test/libsolidity/syntaxTests/visibility/interface/function_default.sol b/test/libsolidity/syntaxTests/visibility/interface/function_default.sol index 9889d038a..7b9044dd2 100644 --- a/test/libsolidity/syntaxTests/visibility/interface/function_default.sol +++ b/test/libsolidity/syntaxTests/visibility/interface/function_default.sol @@ -3,4 +3,4 @@ interface I { } // ---- // Warning: Functions in interfaces should be declared external. -// Warning: No visibility specified. Defaulting to "external". +// Warning: No visibility specified. Defaulting to "public". In interfaces it defaults to external. From 6777f7a57fed6b39128773f13084da729dd64588 Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 8 Mar 2018 19:41:29 +0100 Subject: [PATCH 073/197] Optimize across MLOAD if MSIZE is not used. --- libevmasm/Assembly.cpp | 4 ++- libevmasm/CommonSubexpressionEliminator.h | 8 +++-- libevmasm/Instruction.cpp | 2 +- libevmasm/SemanticInformation.cpp | 7 ++++- libevmasm/SemanticInformation.h | 3 +- libsolidity/ast/Types.h | 13 ++++++++ libsolidity/codegen/CompilerUtils.cpp | 36 +++++++++++++++++----- libsolidity/codegen/ExpressionCompiler.cpp | 21 +++---------- test/libevmasm/Optimiser.cpp | 5 +-- test/libsolidity/SolidityEndToEndTest.cpp | 23 +++++++++++++- 10 files changed, 87 insertions(+), 35 deletions(-) diff --git a/libevmasm/Assembly.cpp b/libevmasm/Assembly.cpp index bd4ebf59a..b71bc80cb 100644 --- a/libevmasm/Assembly.cpp +++ b/libevmasm/Assembly.cpp @@ -438,13 +438,15 @@ map Assembly::optimiseInternal( // function types that can be stored in storage. AssemblyItems optimisedItems; + bool usesMSize = (find(m_items.begin(), m_items.end(), AssemblyItem(Instruction::MSIZE)) != m_items.end()); + auto iter = m_items.begin(); while (iter != m_items.end()) { KnownState emptyState; CommonSubexpressionEliminator eliminator(emptyState); auto orig = iter; - iter = eliminator.feedItems(iter, m_items.end()); + iter = eliminator.feedItems(iter, m_items.end(), usesMSize); bool shouldReplace = false; AssemblyItems optimisedChunk; try diff --git a/libevmasm/CommonSubexpressionEliminator.h b/libevmasm/CommonSubexpressionEliminator.h index 0b957a0ec..b20de2469 100644 --- a/libevmasm/CommonSubexpressionEliminator.h +++ b/libevmasm/CommonSubexpressionEliminator.h @@ -65,8 +65,9 @@ public: /// Feeds AssemblyItems into the eliminator and @returns the iterator pointing at the first /// item that must be fed into a new instance of the eliminator. + /// @param _msizeImportant if false, do not consider modification of MSIZE a side-effect template - _AssemblyItemIterator feedItems(_AssemblyItemIterator _iterator, _AssemblyItemIterator _end); + _AssemblyItemIterator feedItems(_AssemblyItemIterator _iterator, _AssemblyItemIterator _end, bool _msizeImportant); /// @returns the resulting items after optimization. AssemblyItems getOptimizedItems(); @@ -168,11 +169,12 @@ private: template _AssemblyItemIterator CommonSubexpressionEliminator::feedItems( _AssemblyItemIterator _iterator, - _AssemblyItemIterator _end + _AssemblyItemIterator _end, + bool _msizeImportant ) { assertThrow(!m_breakingItem, OptimizerException, "Invalid use of CommonSubexpressionEliminator."); - for (; _iterator != _end && !SemanticInformation::breaksCSEAnalysisBlock(*_iterator); ++_iterator) + for (; _iterator != _end && !SemanticInformation::breaksCSEAnalysisBlock(*_iterator, _msizeImportant); ++_iterator) feedItem(*_iterator); if (_iterator != _end) m_breakingItem = &(*_iterator++); diff --git a/libevmasm/Instruction.cpp b/libevmasm/Instruction.cpp index a677a631c..f9bbad2cf 100644 --- a/libevmasm/Instruction.cpp +++ b/libevmasm/Instruction.cpp @@ -199,7 +199,7 @@ static const std::map c_instructionInfo = { Instruction::ADDMOD, { "ADDMOD", 0, 3, 1, false, Tier::Mid } }, { Instruction::MULMOD, { "MULMOD", 0, 3, 1, false, Tier::Mid } }, { Instruction::SIGNEXTEND, { "SIGNEXTEND", 0, 2, 1, false, Tier::Low } }, - { Instruction::KECCAK256, { "KECCAK256", 0, 2, 1, false, Tier::Special } }, + { Instruction::KECCAK256, { "KECCAK256", 0, 2, 1, true, Tier::Special } }, { Instruction::ADDRESS, { "ADDRESS", 0, 0, 1, false, Tier::Base } }, { Instruction::BALANCE, { "BALANCE", 0, 1, 1, false, Tier::Balance } }, { Instruction::ORIGIN, { "ORIGIN", 0, 0, 1, false, Tier::Base } }, diff --git a/libevmasm/SemanticInformation.cpp b/libevmasm/SemanticInformation.cpp index 03870f7ca..71267ee89 100644 --- a/libevmasm/SemanticInformation.cpp +++ b/libevmasm/SemanticInformation.cpp @@ -28,7 +28,7 @@ using namespace std; using namespace dev; using namespace dev::eth; -bool SemanticInformation::breaksCSEAnalysisBlock(AssemblyItem const& _item) +bool SemanticInformation::breaksCSEAnalysisBlock(AssemblyItem const& _item, bool _msizeImportant) { switch (_item.type()) { @@ -59,6 +59,11 @@ bool SemanticInformation::breaksCSEAnalysisBlock(AssemblyItem const& _item) return false; if (_item.instruction() == Instruction::MSTORE) return false; + if (!_msizeImportant && ( + _item.instruction() == Instruction::MLOAD || + _item.instruction() == Instruction::KECCAK256 + )) + return false; //@todo: We do not handle the following memory instructions for now: // calldatacopy, codecopy, extcodecopy, mstore8, // msize (note that msize also depends on memory read access) diff --git a/libevmasm/SemanticInformation.h b/libevmasm/SemanticInformation.h index 83656252c..8bdc70bec 100644 --- a/libevmasm/SemanticInformation.h +++ b/libevmasm/SemanticInformation.h @@ -38,7 +38,8 @@ class AssemblyItem; struct SemanticInformation { /// @returns true if the given items starts a new block for common subexpression analysis. - static bool breaksCSEAnalysisBlock(AssemblyItem const& _item); + /// @param _msizeImportant if false, consider an operation non-breaking if its only side-effect is that it modifies msize. + static bool breaksCSEAnalysisBlock(AssemblyItem const& _item, bool _msizeImportant); /// @returns true if the item is a two-argument operation whose value does not depend on the /// order of its arguments. static bool isCommutativeOperation(AssemblyItem const& _item); diff --git a/libsolidity/ast/Types.h b/libsolidity/ast/Types.h index b7e648918..2c392705d 100644 --- a/libsolidity/ast/Types.h +++ b/libsolidity/ast/Types.h @@ -229,6 +229,9 @@ public: /// i.e. it behaves differently in lvalue context and in value context. virtual bool isValueType() const { return false; } virtual unsigned sizeOnStack() const { return 1; } + /// If it is possible to initialize such a value in memory by just writing zeros + /// of the size memoryHeadSize(). + virtual bool hasSimpleZeroValueInMemory() const { return true; } /// @returns the mobile (in contrast to static) type corresponding to the given type. /// This returns the corresponding IntegerType or FixedPointType for RationalNumberType /// and the pointer type for storage reference types. @@ -568,6 +571,7 @@ public: virtual TypePointer mobileType() const override { return copyForLocation(m_location, true); } virtual bool dataStoredIn(DataLocation _location) const override { return m_location == _location; } + virtual bool hasSimpleZeroValueInMemory() const override { return false; } /// Storage references can be pointers or bound references. In general, local variables are of /// pointer type, state variables are bound references. Assignments to pointers or deleting @@ -855,6 +859,7 @@ public: virtual u256 storageSize() const override; virtual bool canLiveOutsideStorage() const override { return false; } virtual unsigned sizeOnStack() const override; + virtual bool hasSimpleZeroValueInMemory() const override { return false; } virtual TypePointer mobileType() const override; /// Converts components to their temporary types and performs some wildcard matching. virtual TypePointer closestTemporaryType(TypePointer const& _targetType) const override; @@ -999,6 +1004,7 @@ public: virtual bool isValueType() const override { return true; } virtual bool canLiveOutsideStorage() const override { return m_kind == Kind::Internal || m_kind == Kind::External; } virtual unsigned sizeOnStack() const override; + virtual bool hasSimpleZeroValueInMemory() const override { return false; } virtual MemberList::MemberMap nativeMembers(ContractDefinition const* _currentScope) const override; virtual TypePointer encodingType() const override; virtual TypePointer interfaceType(bool _inLibrary) const override; @@ -1104,6 +1110,8 @@ public: return _inLibrary ? shared_from_this() : TypePointer(); } virtual bool dataStoredIn(DataLocation _location) const override { return _location == DataLocation::Storage; } + /// Cannot be stored in memory, but just in case. + virtual bool hasSimpleZeroValueInMemory() const override { solAssert(false, ""); } TypePointer const& keyType() const { return m_keyType; } TypePointer const& valueType() const { return m_valueType; } @@ -1132,6 +1140,7 @@ public: virtual u256 storageSize() const override; virtual bool canLiveOutsideStorage() const override { return false; } virtual unsigned sizeOnStack() const override; + virtual bool hasSimpleZeroValueInMemory() const override { solAssert(false, ""); } virtual std::string toString(bool _short) const override { return "type(" + m_actualType->toString(_short) + ")"; } virtual MemberList::MemberMap nativeMembers(ContractDefinition const* _currentScope) const override; @@ -1154,6 +1163,7 @@ public: virtual u256 storageSize() const override; virtual bool canLiveOutsideStorage() const override { return false; } virtual unsigned sizeOnStack() const override { return 0; } + virtual bool hasSimpleZeroValueInMemory() const override { solAssert(false, ""); } virtual std::string richIdentifier() const override; virtual bool operator==(Type const& _other) const override; virtual std::string toString(bool _short) const override; @@ -1179,6 +1189,7 @@ public: virtual bool operator==(Type const& _other) const override; virtual bool canBeStored() const override { return false; } virtual bool canLiveOutsideStorage() const override { return true; } + virtual bool hasSimpleZeroValueInMemory() const override { solAssert(false, ""); } virtual unsigned sizeOnStack() const override { return 0; } virtual MemberList::MemberMap nativeMembers(ContractDefinition const*) const override; @@ -1209,6 +1220,7 @@ public: virtual bool operator==(Type const& _other) const override; virtual bool canBeStored() const override { return false; } virtual bool canLiveOutsideStorage() const override { return true; } + virtual bool hasSimpleZeroValueInMemory() const override { solAssert(false, ""); } virtual unsigned sizeOnStack() const override { return 0; } virtual MemberList::MemberMap nativeMembers(ContractDefinition const*) const override; @@ -1238,6 +1250,7 @@ public: virtual bool canLiveOutsideStorage() const override { return false; } virtual bool isValueType() const override { return true; } virtual unsigned sizeOnStack() const override { return 1; } + virtual bool hasSimpleZeroValueInMemory() const override { solAssert(false, ""); } virtual std::string toString(bool) const override { return "inaccessible dynamic type"; } virtual TypePointer decodingType() const override { return std::make_shared(256); } }; diff --git a/libsolidity/codegen/CompilerUtils.cpp b/libsolidity/codegen/CompilerUtils.cpp index 676d5d4e3..deaef017a 100644 --- a/libsolidity/codegen/CompilerUtils.cpp +++ b/libsolidity/codegen/CompilerUtils.cpp @@ -495,14 +495,34 @@ void CompilerUtils::abiDecodeV2(TypePointers const& _parameterTypes, bool _fromM void CompilerUtils::zeroInitialiseMemoryArray(ArrayType const& _type) { - auto repeat = m_context.newTag(); - m_context << repeat; - pushZeroValue(*_type.baseType()); - storeInMemoryDynamic(*_type.baseType()); - m_context << Instruction::SWAP1 << u256(1) << Instruction::SWAP1; - m_context << Instruction::SUB << Instruction::SWAP1; - m_context << Instruction::DUP2; - m_context.appendConditionalJumpTo(repeat); + if (_type.baseType()->hasSimpleZeroValueInMemory()) + { + solAssert(_type.baseType()->isValueType(), ""); + Whiskers templ(R"({ + let size := mul(length, ) + // cheap way of zero-initializing a memory range + codecopy(memptr, codesize(), size) + memptr := add(memptr, size) + })"); + templ("element_size", to_string(_type.baseType()->memoryHeadSize())); + m_context.appendInlineAssembly(templ.render(), {"length", "memptr"}); + } + else + { + // TODO: Potential optimization: + // When we create a new multi-dimensional dynamic array, each element + // is initialized to an empty array. It actually does not hurt + // to re-use exactly the same empty array for all elements. Currently, + // a new one is created each time. + auto repeat = m_context.newTag(); + m_context << repeat; + pushZeroValue(*_type.baseType()); + storeInMemoryDynamic(*_type.baseType()); + m_context << Instruction::SWAP1 << u256(1) << Instruction::SWAP1; + m_context << Instruction::SUB << Instruction::SWAP1; + m_context << Instruction::DUP2; + m_context.appendConditionalJumpTo(repeat); + } m_context << Instruction::SWAP1 << Instruction::POP; } diff --git a/libsolidity/codegen/ExpressionCompiler.cpp b/libsolidity/codegen/ExpressionCompiler.cpp index 9e2d30d5e..76aa68438 100644 --- a/libsolidity/codegen/ExpressionCompiler.cpp +++ b/libsolidity/codegen/ExpressionCompiler.cpp @@ -850,8 +850,6 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) } case FunctionType::Kind::ObjectCreation: { - // Will allocate at the end of memory (MSIZE) and not write at all unless the base - // type is dynamically sized. ArrayType const& arrayType = dynamic_cast(*_functionCall.annotation().type); _functionCall.expression().accept(*this); solAssert(arguments.size() == 1, ""); @@ -861,15 +859,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) utils().convertType(*arguments[0]->annotation().type, IntegerType(256)); // Stack: requested_length - // Allocate at max(MSIZE, freeMemoryPointer) utils().fetchFreeMemoryPointer(); - m_context << Instruction::DUP1 << Instruction::MSIZE; - m_context << Instruction::LT; - auto initialise = m_context.appendConditionalJump(); - // Free memory pointer does not point to empty memory, use MSIZE. - m_context << Instruction::POP; - m_context << Instruction::MSIZE; - m_context << initialise; // Stack: requested_length memptr m_context << Instruction::SWAP1; @@ -894,13 +884,10 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) // Check if length is zero m_context << Instruction::DUP1 << Instruction::ISZERO; auto skipInit = m_context.appendConditionalJump(); - - // We only have to initialise if the base type is a not a value type. - if (dynamic_cast(arrayType.baseType().get())) - { - m_context << Instruction::DUP2 << u256(32) << Instruction::ADD; - utils().zeroInitialiseMemoryArray(arrayType); - } + // Always initialize because the free memory pointer might point at + // a dirty memory area. + m_context << Instruction::DUP2 << u256(32) << Instruction::ADD; + utils().zeroInitialiseMemoryArray(arrayType); m_context << skipInit; m_context << Instruction::POP; break; diff --git a/test/libevmasm/Optimiser.cpp b/test/libevmasm/Optimiser.cpp index f630b3048..28f6b8c0f 100644 --- a/test/libevmasm/Optimiser.cpp +++ b/test/libevmasm/Optimiser.cpp @@ -69,8 +69,9 @@ namespace { AssemblyItems input = addDummyLocations(_input); + bool usesMsize = (find(_input.begin(), _input.end(), AssemblyItem{Instruction::MSIZE}) != _input.end()); eth::CommonSubexpressionEliminator cse(_state); - BOOST_REQUIRE(cse.feedItems(input.begin(), input.end()) == input.end()); + BOOST_REQUIRE(cse.feedItems(input.begin(), input.end(), usesMsize) == input.end()); AssemblyItems output = cse.getOptimizedItems(); for (AssemblyItem const& item: output) @@ -124,7 +125,7 @@ BOOST_AUTO_TEST_CASE(cse_intermediate_swap) Instruction::SLOAD, Instruction::SWAP1, u256(100), Instruction::EXP, Instruction::SWAP1, Instruction::DIV, u256(0xff), Instruction::AND }; - BOOST_REQUIRE(cse.feedItems(input.begin(), input.end()) == input.end()); + BOOST_REQUIRE(cse.feedItems(input.begin(), input.end(), false) == input.end()); AssemblyItems output = cse.getOptimizedItems(); BOOST_CHECK(!output.empty()); } diff --git a/test/libsolidity/SolidityEndToEndTest.cpp b/test/libsolidity/SolidityEndToEndTest.cpp index f5813aed1..7fd65c141 100644 --- a/test/libsolidity/SolidityEndToEndTest.cpp +++ b/test/libsolidity/SolidityEndToEndTest.cpp @@ -9119,7 +9119,7 @@ BOOST_AUTO_TEST_CASE(calling_uninitialized_function_in_detail) int mutex; function t() returns (uint) { if (mutex > 0) - return 7; + { assembly { mstore(0, 7) return(0, 0x20) } } mutex = 1; // Avoid re-executing this function if we jump somewhere. x(); @@ -9132,6 +9132,27 @@ BOOST_AUTO_TEST_CASE(calling_uninitialized_function_in_detail) ABI_CHECK(callContractFunction("t()"), encodeArgs()); } +BOOST_AUTO_TEST_CASE(calling_uninitialized_function_through_array) +{ + char const* sourceCode = R"( + contract C { + int mutex; + function t() 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; + x[0](); + return 2; + } + } + )"; + + compileAndRun(sourceCode, 0, "C"); + ABI_CHECK(callContractFunction("t()"), encodeArgs()); +} + BOOST_AUTO_TEST_CASE(pass_function_types_internally) { char const* sourceCode = R"( From 138dba1a3fbb475a21a546bc39a55a746647439b Mon Sep 17 00:00:00 2001 From: chriseth Date: Tue, 13 Mar 2018 13:00:16 +0100 Subject: [PATCH 074/197] Test number of sstore operations. --- test/libsolidity/SolidityOptimizer.cpp | 40 +++++++++++++++++++++----- 1 file changed, 33 insertions(+), 7 deletions(-) diff --git a/test/libsolidity/SolidityOptimizer.cpp b/test/libsolidity/SolidityOptimizer.cpp index 33039ca94..cf4550c71 100644 --- a/test/libsolidity/SolidityOptimizer.cpp +++ b/test/libsolidity/SolidityOptimizer.cpp @@ -74,11 +74,11 @@ public: unsigned const _optimizeRuns = 200 ) { - bytes nonOptimizedBytecode = compileAndRunWithOptimizer(_sourceCode, _value, _contractName, false, _optimizeRuns); + m_nonOptimizedBytecode = compileAndRunWithOptimizer(_sourceCode, _value, _contractName, false, _optimizeRuns); m_nonOptimizedContract = m_contractAddress; - bytes optimizedBytecode = compileAndRunWithOptimizer(_sourceCode, _value, _contractName, true, _optimizeRuns); - size_t nonOptimizedSize = numInstructions(nonOptimizedBytecode); - size_t optimizedSize = numInstructions(optimizedBytecode); + m_optimizedBytecode = compileAndRunWithOptimizer(_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: ") + @@ -104,7 +104,7 @@ public: /// @returns the number of intructions in the given bytecode, not taking the metadata hash /// into account. - size_t numInstructions(bytes const& _bytecode) + size_t numInstructions(bytes const& _bytecode, boost::optional _which = boost::optional{}) { BOOST_REQUIRE(_bytecode.size() > 5); size_t metadataSize = (_bytecode[_bytecode.size() - 2] << 8) + _bytecode[_bytecode.size() - 1]; @@ -112,13 +112,16 @@ public: BOOST_REQUIRE(_bytecode.size() >= metadataSize + 2); bytes realCode = bytes(_bytecode.begin(), _bytecode.end() - metadataSize - 2); size_t instructions = 0; - solidity::eachInstruction(realCode, [&](Instruction, u256 const&) { - instructions++; + solidity::eachInstruction(realCode, [&](Instruction _instr, u256 const&) { + if (!_which || *_which == _instr) + instructions++; }); return instructions; } protected: + bytes m_nonOptimizedBytecode; + bytes m_optimizedBytecode; Address m_optimizedContract; Address m_nonOptimizedContract; }; @@ -581,6 +584,29 @@ BOOST_AUTO_TEST_CASE(invalid_state_at_control_flow_join) compareVersions("test()"); } +BOOST_AUTO_TEST_CASE(optimise_multi_stores) +{ + char const* sourceCode = R"( + contract Test { + struct S { uint16 a; uint16 b; uint16[3] c; uint[] dyn; } + uint padding; + S[] s; + function f() public returns (uint16, uint16, uint16[3], uint) { + uint16[3] memory c; + c[0] = 7; + c[1] = 8; + c[2] = 9; + s.push(S(1, 2, c, new uint[](4))); + return (s[0].a, s[0].b, s[0].c, s[0].dyn[2]); + } + } + )"; + compileBothVersions(sourceCode); + compareVersions("f()"); + BOOST_CHECK_EQUAL(numInstructions(m_nonOptimizedBytecode, Instruction::SSTORE), 13); + BOOST_CHECK_EQUAL(numInstructions(m_optimizedBytecode, Instruction::SSTORE), 11); +} + BOOST_AUTO_TEST_SUITE_END() } From e64e397f2417fc9a678690614a775fa88514f1dc Mon Sep 17 00:00:00 2001 From: chriseth Date: Fri, 30 Mar 2018 12:11:07 +0200 Subject: [PATCH 075/197] Add memory array init test. --- test/libsolidity/SolidityEndToEndTest.cpp | 26 +++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/test/libsolidity/SolidityEndToEndTest.cpp b/test/libsolidity/SolidityEndToEndTest.cpp index 7fd65c141..38d3ce4df 100644 --- a/test/libsolidity/SolidityEndToEndTest.cpp +++ b/test/libsolidity/SolidityEndToEndTest.cpp @@ -8756,6 +8756,32 @@ BOOST_AUTO_TEST_CASE(create_dynamic_array_with_zero_length) ABI_CHECK(callContractFunction("f()"), encodeArgs(u256(7))); } +BOOST_AUTO_TEST_CASE(correctly_initialize_memory_array_in_constructor) +{ + // Memory arrays are initialized using codecopy past the size of the code. + // This test checks that it also works in the constructor context. + char const* sourceCode = R"( + contract C { + bool public success; + function C() public { + // Make memory dirty. + assembly { + for { let i := 0 } lt(i, 64) { i := add(i, 1) } { + mstore(msize, not(0)) + } + } + uint16[3] memory c; + require(c[0] == 0 && c[1] == 0 && c[2] == 0); + uint16[] memory x = new uint16[](3); + require(x[0] == 0 && x[1] == 0 && x[2] == 0); + success = true; + } + } + )"; + compileAndRun(sourceCode, 0, "C"); + ABI_CHECK(callContractFunction("success()"), encodeArgs(u256(1))); +} + BOOST_AUTO_TEST_CASE(return_does_not_skip_modifier) { char const* sourceCode = R"( From deadff263fbf4d5da911d7c544821cc77081a6d3 Mon Sep 17 00:00:00 2001 From: chriseth Date: Fri, 30 Mar 2018 12:12:12 +0200 Subject: [PATCH 076/197] Changelog entry. --- Changelog.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Changelog.md b/Changelog.md index d35a2b456..88e88545d 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,9 +1,11 @@ ### 0.4.22 (unreleased) Features: + * Code Generator: Initialize arrays without using ``msize()``. * Commandline interface: Error when missing or inaccessible file detected. Suppress it with the ``--ignore-missing`` flag. * General: Support accessing dynamic return data in post-byzantium EVMs. * Interfaces: Allow overriding external functions in interfaces with public in an implementing contract. + * Optimizer: Optimize across ``mload`` if ``msize()`` is not used. * Syntax Checker: Issue warning for empty structs (or error as experimental 0.5.0 feature). Bugfixes: From a54fdc495f54bd16c329ca3f79d533d540cb9a3e Mon Sep 17 00:00:00 2001 From: chriseth Date: Fri, 16 Mar 2018 19:02:15 +0100 Subject: [PATCH 077/197] Fix: Treat empty base constructor argument list as not provided. --- Changelog.md | 1 + libsolidity/codegen/ContractCompiler.cpp | 8 +++++--- .../inheritance/base_arguments_empty_parentheses.sol | 6 ++++++ .../syntaxTests/inheritance/too_few_base_arguments.sol | 10 ++++++++++ 4 files changed, 22 insertions(+), 3 deletions(-) create mode 100644 test/libsolidity/syntaxTests/inheritance/base_arguments_empty_parentheses.sol create mode 100644 test/libsolidity/syntaxTests/inheritance/too_few_base_arguments.sol diff --git a/Changelog.md b/Changelog.md index d35a2b456..70e987515 100644 --- a/Changelog.md +++ b/Changelog.md @@ -12,6 +12,7 @@ Bugfixes: * Code Generator: Properly skip unneeded storage array cleanup when not reducing length. * Code Generator: Bugfix in modifier lookup in libraries. * Code Generator: Implement packed encoding of external function types. + * Code Generator: Treat empty base constructor argument list as not provided. * Commandline interface: Support ``--evm-version constantinople`` properly. * DocString Parser: Fix error message for empty descriptions. * Standard JSON: Support ``constantinople`` as ``evmVersion`` properly. diff --git a/libsolidity/codegen/ContractCompiler.cpp b/libsolidity/codegen/ContractCompiler.cpp index 791edc65a..ebd9139a7 100644 --- a/libsolidity/codegen/ContractCompiler.cpp +++ b/libsolidity/codegen/ContractCompiler.cpp @@ -143,8 +143,9 @@ void ContractCompiler::appendInitAndConstructorCode(ContractDefinition const& _c for (auto const& modifier: constructor->modifiers()) { auto baseContract = dynamic_cast( - modifier->name()->annotation().referencedDeclaration); - if (baseContract) + modifier->name()->annotation().referencedDeclaration + ); + if (baseContract && !modifier->arguments().empty()) if (m_baseArguments.count(baseContract->constructor()) == 0) m_baseArguments[baseContract->constructor()] = &modifier->arguments(); } @@ -156,7 +157,7 @@ void ContractCompiler::appendInitAndConstructorCode(ContractDefinition const& _c ); solAssert(baseContract, ""); - if (m_baseArguments.count(baseContract->constructor()) == 0) + if (!m_baseArguments.count(baseContract->constructor()) && !base->arguments().empty()) m_baseArguments[baseContract->constructor()] = &base->arguments(); } } @@ -238,6 +239,7 @@ void ContractCompiler::appendBaseConstructor(FunctionDefinition const& _construc solAssert(m_baseArguments.count(&_constructor), ""); std::vector> const* arguments = m_baseArguments[&_constructor]; solAssert(arguments, ""); + solAssert(arguments->size() == constructorType.parameterTypes().size(), ""); for (unsigned i = 0; i < arguments->size(); ++i) compileExpression(*(arguments->at(i)), constructorType.parameterTypes()[i]); } diff --git a/test/libsolidity/syntaxTests/inheritance/base_arguments_empty_parentheses.sol b/test/libsolidity/syntaxTests/inheritance/base_arguments_empty_parentheses.sol new file mode 100644 index 000000000..76df0657f --- /dev/null +++ b/test/libsolidity/syntaxTests/inheritance/base_arguments_empty_parentheses.sol @@ -0,0 +1,6 @@ +contract Base { + function Base(uint) public {} +} +contract Derived is Base(2) { } +contract Derived2 is Base(), Derived() { } +contract Derived3 is Base, Derived {} diff --git a/test/libsolidity/syntaxTests/inheritance/too_few_base_arguments.sol b/test/libsolidity/syntaxTests/inheritance/too_few_base_arguments.sol new file mode 100644 index 000000000..82aba3088 --- /dev/null +++ b/test/libsolidity/syntaxTests/inheritance/too_few_base_arguments.sol @@ -0,0 +1,10 @@ +contract Base { + function Base(uint, uint) public {} +} +contract Derived is Base(2) { } +contract Derived2 is Base { + function Derived2() Base(2) public { } +} +// ---- +// TypeError: Wrong argument count for constructor call: 1 arguments given but expected 2. +// TypeError: Wrong argument count for modifier invocation: 1 arguments given but expected 2. From c42caedec2b6dc2bfb268f538eecb1d4ee18f9d8 Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 15 Mar 2018 18:19:45 +0100 Subject: [PATCH 078/197] Extract recursive struct tests. --- .../SolidityNameAndTypeResolution.cpp | 93 ------------------- .../recursion/return_recursive_structs.sol | 7 ++ .../recursion/return_recursive_structs2.sol | 7 ++ .../recursion/return_recursive_structs3.sol | 8 ++ .../struct_definition_directly_recursive.sol | 8 ++ ...struct_definition_indirectly_recursive.sol | 12 +++ ...struct_definition_not_really_recursive.sol | 4 + ...truct_definition_recursion_via_mapping.sol | 7 ++ 8 files changed, 53 insertions(+), 93 deletions(-) create mode 100644 test/libsolidity/syntaxTests/structs/recursion/return_recursive_structs.sol create mode 100644 test/libsolidity/syntaxTests/structs/recursion/return_recursive_structs2.sol create mode 100644 test/libsolidity/syntaxTests/structs/recursion/return_recursive_structs3.sol create mode 100644 test/libsolidity/syntaxTests/structs/recursion/struct_definition_directly_recursive.sol create mode 100644 test/libsolidity/syntaxTests/structs/recursion/struct_definition_indirectly_recursive.sol create mode 100644 test/libsolidity/syntaxTests/structs/recursion/struct_definition_not_really_recursive.sol create mode 100644 test/libsolidity/syntaxTests/structs/recursion/struct_definition_recursion_via_mapping.sol diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index dcdc15199..2a03392e4 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -92,61 +92,6 @@ BOOST_AUTO_TEST_CASE(reference_to_later_declaration) CHECK_SUCCESS(text); } -BOOST_AUTO_TEST_CASE(struct_definition_directly_recursive) -{ - char const* text = R"( - contract test { - struct MyStructName { - address addr; - MyStructName x; - } - } - )"; - CHECK_ERROR(text, TypeError, "Recursive struct definition."); -} - -BOOST_AUTO_TEST_CASE(struct_definition_indirectly_recursive) -{ - char const* text = R"( - contract test { - struct MyStructName1 { - address addr; - uint256 count; - MyStructName2 x; - } - struct MyStructName2 { - MyStructName1 x; - } - } - )"; - CHECK_ERROR(text, TypeError, "Recursive struct definition."); -} - -BOOST_AUTO_TEST_CASE(struct_definition_not_really_recursive) -{ - char const* text = R"( - contract test { - struct s1 { uint a; } - struct s2 { s1 x; s1 y; } - } - )"; - CHECK_SUCCESS(text); -} - -BOOST_AUTO_TEST_CASE(struct_definition_recursion_via_mapping) -{ - char const* text = R"( - contract test { - struct MyStructName1 { - address addr; - uint256 count; - mapping(uint => MyStructName1) x; - } - } - )"; - CHECK_SUCCESS(text); -} - BOOST_AUTO_TEST_CASE(type_inference_smoke_test) { char const* text = R"( @@ -6193,44 +6138,6 @@ BOOST_AUTO_TEST_CASE(read_returned_struct) )"; CHECK_WARNING(text, "Experimental features"); } - -BOOST_AUTO_TEST_CASE(return_recursive_structs) -{ - char const* text = R"( - contract C { - struct S { uint a; S[] sub; } - function f() returns (uint, S) { - } - } - )"; - CHECK_ERROR(text, TypeError, "Internal or recursive type is not allowed for public or external functions."); -} - -BOOST_AUTO_TEST_CASE(return_recursive_structs2) -{ - char const* text = R"( - contract C { - struct S { uint a; S[2][] sub; } - function f() returns (uint, S) { - } - } - )"; - CHECK_ERROR(text, TypeError, "Internal or recursive type is not allowed for public or external functions."); -} - -BOOST_AUTO_TEST_CASE(return_recursive_structs3) -{ - char const* text = R"( - contract C { - struct S { uint a; S[][][] sub; } - struct T { S s; } - function f() returns (uint x, T t) { - } - } - )"; - CHECK_ERROR(text, TypeError, "Internal or recursive type is not allowed for public or external functions."); -} - BOOST_AUTO_TEST_CASE(address_checksum_type_deduction) { char const* text = R"( diff --git a/test/libsolidity/syntaxTests/structs/recursion/return_recursive_structs.sol b/test/libsolidity/syntaxTests/structs/recursion/return_recursive_structs.sol new file mode 100644 index 000000000..c02a8aa48 --- /dev/null +++ b/test/libsolidity/syntaxTests/structs/recursion/return_recursive_structs.sol @@ -0,0 +1,7 @@ +contract C { + struct S { uint a; S[] sub; } + function f() public pure returns (uint, S) { + } +} +// ---- +// TypeError: 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 new file mode 100644 index 000000000..e9488cf49 --- /dev/null +++ b/test/libsolidity/syntaxTests/structs/recursion/return_recursive_structs2.sol @@ -0,0 +1,7 @@ +contract C { + struct S { uint a; S[2][] sub; } + function f() public pure returns (uint, S) { + } +} +// ---- +// TypeError: 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 new file mode 100644 index 000000000..6728baec6 --- /dev/null +++ b/test/libsolidity/syntaxTests/structs/recursion/return_recursive_structs3.sol @@ -0,0 +1,8 @@ +contract C { + struct S { uint a; S[][][] sub; } + struct T { S s; } + function f() public pure returns (uint x, T t) { + } +} +// ---- +// TypeError: Internal or recursive type is not allowed for public or external functions. diff --git a/test/libsolidity/syntaxTests/structs/recursion/struct_definition_directly_recursive.sol b/test/libsolidity/syntaxTests/structs/recursion/struct_definition_directly_recursive.sol new file mode 100644 index 000000000..cac2e23fd --- /dev/null +++ b/test/libsolidity/syntaxTests/structs/recursion/struct_definition_directly_recursive.sol @@ -0,0 +1,8 @@ +contract Test { + struct MyStructName { + address addr; + MyStructName x; + } +} +// ---- +// TypeError: Recursive struct definition. diff --git a/test/libsolidity/syntaxTests/structs/recursion/struct_definition_indirectly_recursive.sol b/test/libsolidity/syntaxTests/structs/recursion/struct_definition_indirectly_recursive.sol new file mode 100644 index 000000000..11fc63078 --- /dev/null +++ b/test/libsolidity/syntaxTests/structs/recursion/struct_definition_indirectly_recursive.sol @@ -0,0 +1,12 @@ +contract Test { + struct MyStructName1 { + address addr; + uint256 count; + MyStructName2 x; + } + struct MyStructName2 { + MyStructName1 x; + } +} +// ---- +// TypeError: Recursive struct definition. diff --git a/test/libsolidity/syntaxTests/structs/recursion/struct_definition_not_really_recursive.sol b/test/libsolidity/syntaxTests/structs/recursion/struct_definition_not_really_recursive.sol new file mode 100644 index 000000000..6ec4ee01b --- /dev/null +++ b/test/libsolidity/syntaxTests/structs/recursion/struct_definition_not_really_recursive.sol @@ -0,0 +1,4 @@ +contract Test { + struct S1 { uint a; } + struct S2 { S1 x; S1 y; } +} diff --git a/test/libsolidity/syntaxTests/structs/recursion/struct_definition_recursion_via_mapping.sol b/test/libsolidity/syntaxTests/structs/recursion/struct_definition_recursion_via_mapping.sol new file mode 100644 index 000000000..926981b3b --- /dev/null +++ b/test/libsolidity/syntaxTests/structs/recursion/struct_definition_recursion_via_mapping.sol @@ -0,0 +1,7 @@ +contract Test { + struct MyStructName1 { + address addr; + uint256 count; + mapping(uint => MyStructName1) x; + } +} From 5bdadff0d8a9c32745dd46aa639283718613efdc Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 15 Mar 2018 19:07:25 +0100 Subject: [PATCH 079/197] Fix detection of recursive structs. --- Changelog.md | 1 + libsolidity/ast/Types.cpp | 5 +++++ .../recursion/multi_struct_composition.sol | 15 +++++++++++++++ .../structs/recursion/parallel_structs.sol | 15 +++++++++++++++ 4 files changed, 36 insertions(+) create mode 100644 test/libsolidity/syntaxTests/structs/recursion/multi_struct_composition.sol create mode 100644 test/libsolidity/syntaxTests/structs/recursion/parallel_structs.sol diff --git a/Changelog.md b/Changelog.md index 70e987515..63c991444 100644 --- a/Changelog.md +++ b/Changelog.md @@ -16,6 +16,7 @@ Bugfixes: * Commandline interface: Support ``--evm-version constantinople`` properly. * DocString Parser: Fix error message for empty descriptions. * Standard JSON: Support ``constantinople`` as ``evmVersion`` properly. + * Type Checker: Fix detection of recursive structs. * Type System: Improve error message when attempting to shift by a fractional amount. * Type System: Make external library functions accessible. * Type System: Prevent encoding of weird types. diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp index 42fd1c3da..42f6b5ca5 100644 --- a/libsolidity/ast/Types.cpp +++ b/libsolidity/ast/Types.cpp @@ -1972,9 +1972,12 @@ bool StructType::recursive() const if (!m_recursive.is_initialized()) { set structsSeen; + set structsProcessed; function check = [&](StructType const* t) -> bool { StructDefinition const* str = &t->structDefinition(); + if (structsProcessed.count(str)) + return false; if (structsSeen.count(str)) return true; structsSeen.insert(str); @@ -1987,6 +1990,8 @@ bool StructType::recursive() const if (check(innerStruct)) return true; } + structsSeen.erase(str); + structsProcessed.insert(str); return false; }; m_recursive = check(this); diff --git a/test/libsolidity/syntaxTests/structs/recursion/multi_struct_composition.sol b/test/libsolidity/syntaxTests/structs/recursion/multi_struct_composition.sol new file mode 100644 index 000000000..9a1c22f18 --- /dev/null +++ b/test/libsolidity/syntaxTests/structs/recursion/multi_struct_composition.sol @@ -0,0 +1,15 @@ +pragma experimental ABIEncoderV2; + +contract C { + struct T { U u; V v; } + + struct U { W w; } + + struct V { W w; } + + struct W { uint x; } + + function f(T) public pure { } +} +// ---- +// Warning: 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 new file mode 100644 index 000000000..d4ad088dc --- /dev/null +++ b/test/libsolidity/syntaxTests/structs/recursion/parallel_structs.sol @@ -0,0 +1,15 @@ +pragma experimental ABIEncoderV2; + +contract TestContract +{ + struct SubStruct { + uint256 id; + } + struct TestStruct { + SubStruct subStruct1; + SubStruct subStruct2; + } + function addTestStruct(TestStruct) public pure {} +} +// ---- +// Warning: Experimental features are turned on. Do not use experimental features on live deployments. From eb5b18e8145d7d3971c450ba220f322e5f66d45c Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 15 Mar 2018 19:53:29 +0100 Subject: [PATCH 080/197] Generalize cycle detection. --- libdevcore/Algorithms.h | 76 ++++++++++++++++++++++++ libsolidity/analysis/PostTypeChecker.cpp | 36 ++++++----- libsolidity/analysis/PostTypeChecker.h | 5 +- libsolidity/ast/Types.cpp | 22 ++----- 4 files changed, 104 insertions(+), 35 deletions(-) create mode 100644 libdevcore/Algorithms.h diff --git a/libdevcore/Algorithms.h b/libdevcore/Algorithms.h new file mode 100644 index 000000000..b25406684 --- /dev/null +++ b/libdevcore/Algorithms.h @@ -0,0 +1,76 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ +#pragma once + + +#include +#include + +namespace dev +{ + +/** + * Detector for cycles in directed graphs. It returns the first + * vertex on the path towards a cycle or a nullptr if there is + * no reachable cycle starting from a given vertex. + */ +template +class CycleDetector +{ +public: + /// 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 _visit): + m_visit(std::move(_visit)) + { } + + /// Recursively perform cycle detection starting + /// (or continuing) with @param _vertex + /// @returns the first vertex on the path towards a cycle from @a _vertex + /// or nullptr if no cycle is reachable from @a _vertex. + V const* run(V const& _vertex) + { + if (m_firstCycleVertex) + return m_firstCycleVertex; + if (m_processed.count(&_vertex)) + return nullptr; + else if (m_processing.count(&_vertex)) + return m_firstCycleVertex = &_vertex; + m_processing.insert(&_vertex); + + m_depth++; + m_visit(_vertex, *this); + m_depth--; + if (m_firstCycleVertex && m_depth == 1) + m_firstCycleVertex = &_vertex; + + m_processing.erase(&_vertex); + m_processed.insert(&_vertex); + return m_firstCycleVertex; + } + +private: + std::function m_visit; + std::set m_processing; + std::set m_processed; + size_t m_depth = 0; + V const* m_firstCycleVertex = nullptr; +}; + +} diff --git a/libsolidity/analysis/PostTypeChecker.cpp b/libsolidity/analysis/PostTypeChecker.cpp index fbc72e527..19d0b708d 100644 --- a/libsolidity/analysis/PostTypeChecker.cpp +++ b/libsolidity/analysis/PostTypeChecker.cpp @@ -21,6 +21,8 @@ #include #include +#include + #include #include @@ -47,7 +49,7 @@ void PostTypeChecker::endVisit(ContractDefinition const&) { solAssert(!m_currentConstVariable, ""); for (auto declaration: m_constVariables) - if (auto identifier = findCycle(declaration)) + if (auto identifier = findCycle(*declaration)) m_errorReporter.typeError( declaration->location(), "The value of the constant " + declaration->name() + @@ -87,20 +89,24 @@ bool PostTypeChecker::visit(Identifier const& _identifier) return true; } -VariableDeclaration const* PostTypeChecker::findCycle( - VariableDeclaration const* _startingFrom, - set const& _seen -) +VariableDeclaration const* PostTypeChecker::findCycle(VariableDeclaration const& _startingFrom) { - if (_seen.count(_startingFrom)) - return _startingFrom; - else if (m_constVariableDependencies.count(_startingFrom)) + auto visitor = [&](VariableDeclaration const& _variable, CycleDetector& _cycleDetector) { - set seen(_seen); - seen.insert(_startingFrom); - for (auto v: m_constVariableDependencies[_startingFrom]) - if (findCycle(v, seen)) - return v; - } - return nullptr; + // 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. + vector dependencies( + m_constVariableDependencies[&_variable].begin(), + m_constVariableDependencies[&_variable].end() + ); + sort(dependencies.begin(), dependencies.end(), [](VariableDeclaration const* _a, VariableDeclaration const* _b) -> bool + { + return _a->id() < _b->id(); + }); + for (auto v: dependencies) + if (_cycleDetector.run(*v)) + return; + }; + return CycleDetector(visitor).run(_startingFrom); } diff --git a/libsolidity/analysis/PostTypeChecker.h b/libsolidity/analysis/PostTypeChecker.h index bafc1ae65..4f9dac6e7 100644 --- a/libsolidity/analysis/PostTypeChecker.h +++ b/libsolidity/analysis/PostTypeChecker.h @@ -55,10 +55,7 @@ private: virtual bool visit(Identifier const& _identifier) override; - VariableDeclaration const* findCycle( - VariableDeclaration const* _startingFrom, - std::set const& _seen = std::set{} - ); + VariableDeclaration const* findCycle(VariableDeclaration const& _startingFrom); ErrorReporter& m_errorReporter; diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp index 42f6b5ca5..ac1d3b01b 100644 --- a/libsolidity/ast/Types.cpp +++ b/libsolidity/ast/Types.cpp @@ -28,6 +28,7 @@ #include #include #include +#include #include #include @@ -1971,30 +1972,19 @@ bool StructType::recursive() const { if (!m_recursive.is_initialized()) { - set structsSeen; - set structsProcessed; - function check = [&](StructType const* t) -> bool + auto visitor = [&](StructDefinition const& _struct, CycleDetector& _cycleDetector) { - StructDefinition const* str = &t->structDefinition(); - if (structsProcessed.count(str)) - return false; - if (structsSeen.count(str)) - return true; - structsSeen.insert(str); - for (ASTPointer const& variable: str->members()) + for (ASTPointer const& variable: _struct.members()) { Type const* memberType = variable->annotation().type.get(); while (dynamic_cast(memberType)) memberType = dynamic_cast(memberType)->baseType().get(); if (StructType const* innerStruct = dynamic_cast(memberType)) - if (check(innerStruct)) - return true; + if (_cycleDetector.run(innerStruct->structDefinition())) + return; } - structsSeen.erase(str); - structsProcessed.insert(str); - return false; }; - m_recursive = check(this); + m_recursive = (CycleDetector(visitor).run(structDefinition()) != nullptr); } return *m_recursive; } From 8fdbd19a05c976908172d0f776a0f96837449683 Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 15 Mar 2018 19:54:05 +0100 Subject: [PATCH 081/197] Extract constant cycle tests. --- .../SolidityNameAndTypeResolution.cpp | 32 ------------------- .../constants/cyclic_dependency_1.sol | 5 +++ .../constants/cyclic_dependency_2.sol | 10 ++++++ .../constants/cyclic_dependency_3.sol | 11 +++++++ .../constants/cyclic_dependency_4.sol | 6 ++++ 5 files changed, 32 insertions(+), 32 deletions(-) create mode 100644 test/libsolidity/syntaxTests/constants/cyclic_dependency_1.sol create mode 100644 test/libsolidity/syntaxTests/constants/cyclic_dependency_2.sol create mode 100644 test/libsolidity/syntaxTests/constants/cyclic_dependency_3.sol create mode 100644 test/libsolidity/syntaxTests/constants/cyclic_dependency_4.sol diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index 2a03392e4..d10cbfbf6 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -6260,38 +6260,6 @@ BOOST_AUTO_TEST_CASE(address_methods) CHECK_SUCCESS(text); } -BOOST_AUTO_TEST_CASE(cyclic_dependency_for_constants) -{ - char const* text = R"( - contract C { - uint constant a = a; - } - )"; - CHECK_ERROR(text, TypeError, "cyclic dependency via a"); - text = R"( - contract C { - uint constant a = b * c; - uint constant b = 7; - uint constant c = b + uint(keccak256(d)); - uint constant d = 2 + a; - } - )"; - CHECK_ERROR_ALLOW_MULTI(text, TypeError, (std::vector{ - "a has a cyclic dependency via c", - "c has a cyclic dependency via d", - "d has a cyclic dependency via a" - })); - text = R"( - contract C { - uint constant a = b * c; - uint constant b = 7; - uint constant c = 4 + uint(keccak256(d)); - uint constant d = 2 + b; - } - )"; - CHECK_SUCCESS(text); -} - BOOST_AUTO_TEST_CASE(interface) { char const* text = R"( diff --git a/test/libsolidity/syntaxTests/constants/cyclic_dependency_1.sol b/test/libsolidity/syntaxTests/constants/cyclic_dependency_1.sol new file mode 100644 index 000000000..2b6aa0883 --- /dev/null +++ b/test/libsolidity/syntaxTests/constants/cyclic_dependency_1.sol @@ -0,0 +1,5 @@ +contract C { + uint constant a = a; +} +// ---- +// TypeError: The value of the constant a has a cyclic dependency via a. diff --git a/test/libsolidity/syntaxTests/constants/cyclic_dependency_2.sol b/test/libsolidity/syntaxTests/constants/cyclic_dependency_2.sol new file mode 100644 index 000000000..461979f85 --- /dev/null +++ b/test/libsolidity/syntaxTests/constants/cyclic_dependency_2.sol @@ -0,0 +1,10 @@ +contract C { + uint constant a = b * c; + uint constant b = 7; + uint constant c = b + uint(keccak256(d)); + uint constant d = 2 + a; +} +// ---- +// TypeError: The value of the constant a has a cyclic dependency via c. +// TypeError: The value of the constant c has a cyclic dependency via d. +// TypeError: The value of the constant d has a cyclic dependency via a. diff --git a/test/libsolidity/syntaxTests/constants/cyclic_dependency_3.sol b/test/libsolidity/syntaxTests/constants/cyclic_dependency_3.sol new file mode 100644 index 000000000..f63be05e3 --- /dev/null +++ b/test/libsolidity/syntaxTests/constants/cyclic_dependency_3.sol @@ -0,0 +1,11 @@ +contract C { + uint constant x = a; + uint constant a = b * c; + uint constant b = c; + uint constant c = b; +} +// ---- +// TypeError: The value of the constant x has a cyclic dependency via a. +// TypeError: The value of the constant a has a cyclic dependency via b. +// TypeError: The value of the constant b has a cyclic dependency via c. +// TypeError: The value of the constant c has a cyclic dependency via b. diff --git a/test/libsolidity/syntaxTests/constants/cyclic_dependency_4.sol b/test/libsolidity/syntaxTests/constants/cyclic_dependency_4.sol new file mode 100644 index 000000000..f01cb98ee --- /dev/null +++ b/test/libsolidity/syntaxTests/constants/cyclic_dependency_4.sol @@ -0,0 +1,6 @@ +contract C { + uint constant a = b * c; + uint constant b = 7; + uint constant c = 4 + uint(keccak256(d)); + uint constant d = 2 + b; +} \ No newline at end of file From 8cbb1d34a58ea3c3eb96758e776c780fb76c23c4 Mon Sep 17 00:00:00 2001 From: chriseth Date: Tue, 3 Apr 2018 17:34:38 +0200 Subject: [PATCH 082/197] Build on tag in Circle. --- circle.yml | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/circle.yml b/circle.yml index 2b34b1aa9..96e62f7b3 100644 --- a/circle.yml +++ b/circle.yml @@ -1,3 +1,10 @@ +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: /.*/ + version: 2 jobs: build_emscripten: @@ -157,15 +164,18 @@ workflows: version: 2 build_all: jobs: - - build_emscripten + - 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 + - build_x86: *build_on_tags - test_x86: + <<: *build_on_tags requires: - build_x86 - - docs + - docs: *build_on_tags From 3b008c7af21b622d0e90a72b31de0799223c9df3 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Mon, 12 Feb 2018 13:45:33 +0000 Subject: [PATCH 083/197] Require node 8 for Javascript tests --- .travis.yml | 6 +++--- circle.yml | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.travis.yml b/.travis.yml index ed4584ef5..8aeda86aa 100644 --- a/.travis.yml +++ b/.travis.yml @@ -84,12 +84,12 @@ matrix: sudo: required compiler: gcc node_js: - - "7" + - "8" services: - docker before_install: - - nvm install 7 - - nvm use 7 + - nvm install 8 + - nvm use 8 - docker pull trzeci/emscripten:sdk-tag-1.35.4-64bit env: - SOLC_EMSCRIPTEN=On diff --git a/circle.yml b/circle.yml index 2b34b1aa9..c131962f3 100644 --- a/circle.yml +++ b/circle.yml @@ -48,7 +48,7 @@ jobs: export NVM_DIR="/usr/local/nvm" [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" # This loads nvm nvm --version - nvm install 6 + nvm install 8 node --version npm --version - run: @@ -72,7 +72,7 @@ jobs: export NVM_DIR="/usr/local/nvm" [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" # This loads nvm nvm --version - nvm install 7 + nvm install 8 node --version npm --version - run: From d664a599e68291166c47fcece464cb8d0af31df8 Mon Sep 17 00:00:00 2001 From: Daniel Kirchner Date: Thu, 1 Mar 2018 18:39:01 +0100 Subject: [PATCH 084/197] Constructors are defined using the ``constructor`` keyword. --- Changelog.md | 1 + libsolidity/analysis/SyntaxChecker.cpp | 17 +++++++- libsolidity/ast/AST.h | 3 +- libsolidity/parsing/Parser.cpp | 26 ++++++++++--- libsolidity/parsing/Parser.h | 1 + .../SolidityNameAndTypeResolution.cpp | 39 ++++++++++++++++++- .../base_arguments_empty_parentheses.sol | 2 +- .../inheritance/too_few_base_arguments.sol | 4 +- 8 files changed, 80 insertions(+), 13 deletions(-) diff --git a/Changelog.md b/Changelog.md index 34c3b0e98..b9619c279 100644 --- a/Changelog.md +++ b/Changelog.md @@ -7,6 +7,7 @@ Features: * Interfaces: Allow overriding external functions in interfaces with public in an implementing contract. * Optimizer: Optimize across ``mload`` if ``msize()`` is not used. * Syntax Checker: Issue warning for empty structs (or error as experimental 0.5.0 feature). + * General: Introduce new constructor syntax using the ``constructor`` keyword as experimental 0.5.0 feature. Bugfixes: * Code Generator: Allow ``block.blockhash`` without being called. diff --git a/libsolidity/analysis/SyntaxChecker.cpp b/libsolidity/analysis/SyntaxChecker.cpp index b595c4d1c..343b4ba81 100644 --- a/libsolidity/analysis/SyntaxChecker.cpp +++ b/libsolidity/analysis/SyntaxChecker.cpp @@ -216,7 +216,22 @@ bool SyntaxChecker::visit(FunctionDefinition const& _function) if (v050 && _function.noVisibilitySpecified()) m_errorReporter.syntaxError(_function.location(), "No visibility specified."); - + + if (_function.isOldStyleConstructor()) + { + if (v050) + 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." + ); + } return true; } diff --git a/libsolidity/ast/AST.h b/libsolidity/ast/AST.h index 9c67d3549..56bb412c9 100644 --- a/libsolidity/ast/AST.h +++ b/libsolidity/ast/AST.h @@ -607,7 +607,8 @@ public: StateMutability stateMutability() const { return m_stateMutability; } bool isConstructor() const { return m_isConstructor; } - bool isFallback() const { return name().empty(); } + 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> const& modifiers() const { return m_functionModifiers; } std::vector> const& returnParameters() const { return m_returnParameters->parameters(); } diff --git a/libsolidity/parsing/Parser.cpp b/libsolidity/parsing/Parser.cpp index 8c97f55fd..e5cc69d89 100644 --- a/libsolidity/parsing/Parser.cpp +++ b/libsolidity/parsing/Parser.cpp @@ -238,7 +238,10 @@ ASTPointer Parser::parseContractDefinition(Token::Value _exp Token::Value currentTokenValue = m_scanner->currentToken(); if (currentTokenValue == Token::RBrace) break; - else if (currentTokenValue == Token::Function) + else if ( + currentTokenValue == Token::Function || + (currentTokenValue == Token::Identifier && m_scanner->currentLiteral() == "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())); @@ -333,9 +336,19 @@ Parser::FunctionHeaderParserResult Parser::parseFunctionHeader(bool _forceEmptyN { RecursionGuard recursionGuard(*this); FunctionHeaderParserResult result; - expectToken(Token::Function); - if (_forceEmptyName || m_scanner->currentToken() == Token::LParen) - result.name = make_shared(); // anonymous function + + if (m_scanner->currentToken() == Token::Function) + // In case of old style constructors, i.e. functions with the same name as the contract, + // this is set to true later in parseFunctionDefinitionOrFunctionTypeStateVariable. + result.isConstructor = false; + else if (m_scanner->currentToken() == Token::Identifier && m_scanner->currentLiteral() == "constructor") + result.isConstructor = true; + else + solAssert(false, "Function or constructor expected."); + m_scanner->next(); + + if (result.isConstructor || _forceEmptyName || m_scanner->currentToken() == Token::LParen) + result.name = make_shared(); else result.name = expectIdentifierToken(); VarDeclParserOptions options; @@ -426,12 +439,13 @@ ASTPointer Parser::parseFunctionDefinitionOrFunctionTypeStateVariable(A } else m_scanner->next(); // just consume the ';' - bool const c_isConstructor = (_contractName && *header.name == *_contractName); + if (_contractName && *header.name == *_contractName) + header.isConstructor = true; return nodeFactory.createNode( header.name, header.visibility, header.stateMutability, - c_isConstructor, + header.isConstructor, docstring, header.parameters, header.modifiers, diff --git a/libsolidity/parsing/Parser.h b/libsolidity/parsing/Parser.h index 3f780af90..2d0e52e18 100644 --- a/libsolidity/parsing/Parser.h +++ b/libsolidity/parsing/Parser.h @@ -56,6 +56,7 @@ private: /// This struct is shared for parsing a function header and a function type. struct FunctionHeaderParserResult { + bool isConstructor; ASTPointer name; ASTPointer parameters; ASTPointer returnParameters; diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index dcdc15199..874cc0a63 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -962,6 +962,41 @@ BOOST_AUTO_TEST_CASE(base_constructor_arguments_override) CHECK_SUCCESS(text); } +BOOST_AUTO_TEST_CASE(new_constructor_syntax) +{ + char const* text = R"( + contract A { constructor() public {} } + )"; + CHECK_SUCCESS(text); + + text = R"( + pragma experimental "v0.5.0"; + contract A { constructor() public {} } + )"; + CHECK_SUCCESS(text); +} + +BOOST_AUTO_TEST_CASE(old_constructor_syntax) +{ + char const* text = R"( + contract A { function A() public {} } + )"; + CHECK_WARNING( + text, + "Defining constructors as functions with the same name as the contract is deprecated." + ); + + text = R"( + pragma experimental "v0.5.0"; + contract A { function A() public {} } + )"; + CHECK_ERROR( + text, + SyntaxError, + "Functions are not allowed to have the same name as the contract." + ); +} + BOOST_AUTO_TEST_CASE(implicit_derived_to_base_conversion) { char const* text = R"( @@ -6916,7 +6951,7 @@ BOOST_AUTO_TEST_CASE(shadowing_builtins_ignores_constructor) { char const* text = R"( contract C { - function C() public {} + constructor() public {} } )"; CHECK_SUCCESS_NO_WARNINGS(text); @@ -7328,7 +7363,7 @@ BOOST_AUTO_TEST_CASE(using_this_in_constructor) { char const* text = R"( contract C { - function C() public { + constructor() public { this.f(); } function f() pure public { diff --git a/test/libsolidity/syntaxTests/inheritance/base_arguments_empty_parentheses.sol b/test/libsolidity/syntaxTests/inheritance/base_arguments_empty_parentheses.sol index 76df0657f..9607ed60c 100644 --- a/test/libsolidity/syntaxTests/inheritance/base_arguments_empty_parentheses.sol +++ b/test/libsolidity/syntaxTests/inheritance/base_arguments_empty_parentheses.sol @@ -1,5 +1,5 @@ contract Base { - function Base(uint) public {} + constructor(uint) public {} } contract Derived is Base(2) { } contract Derived2 is Base(), Derived() { } diff --git a/test/libsolidity/syntaxTests/inheritance/too_few_base_arguments.sol b/test/libsolidity/syntaxTests/inheritance/too_few_base_arguments.sol index 82aba3088..45a0770f3 100644 --- a/test/libsolidity/syntaxTests/inheritance/too_few_base_arguments.sol +++ b/test/libsolidity/syntaxTests/inheritance/too_few_base_arguments.sol @@ -1,9 +1,9 @@ contract Base { - function Base(uint, uint) public {} + constructor(uint, uint) public {} } contract Derived is Base(2) { } contract Derived2 is Base { - function Derived2() Base(2) public { } + constructor() Base(2) public { } } // ---- // TypeError: Wrong argument count for constructor call: 1 arguments given but expected 2. From 07c74ef9241b1fe4fdec5cf4bd97e2c1aedb8d0d Mon Sep 17 00:00:00 2001 From: bitshift Date: Fri, 2 Mar 2018 16:44:35 +0100 Subject: [PATCH 085/197] Updates docs to new constructor syntax. --- docs/contracts.rst | 32 ++++++++++++++++++++++++++------ 1 file changed, 26 insertions(+), 6 deletions(-) diff --git a/docs/contracts.rst b/docs/contracts.rst index 8cc4f6b2b..c4eda8dcf 100644 --- a/docs/contracts.rst +++ b/docs/contracts.rst @@ -40,7 +40,7 @@ This means that cyclic creation dependencies are impossible. :: - pragma solidity ^0.4.16; + pragma solidity ^0.4.20; // should actually be 0.4.21 contract OwnedToken { // TokenCreator is a contract type that is defined below. @@ -52,7 +52,7 @@ This means that cyclic creation dependencies are impossible. // This is the constructor which registers the // creator and the assigned name. - function OwnedToken(bytes32 _name) public { + 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, @@ -976,9 +976,30 @@ virtual method lookup. Constructors ============ -A constructor is an optional function with the same name as the contract which is executed upon contract creation. +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``. +:: + + pragma solidity ^0.4.20; // should actually be 0.4.21 + + contract A { + uint public a; + + constructor(uint _a) internal { + a = _a; + } + } + + contract B is A(1) { + constructor() public {} + } + +A constructor set as ``internal`` causes the contract to be marked as :ref:`abstract `. + +.. note :: + Prior to version 0.4.21, constructors were defined as functions with the same name as the contract. This syntax is now deprecated. + :: pragma solidity ^0.4.11; @@ -995,7 +1016,6 @@ Constructor functions can be either ``public`` or ``internal``. function B() public {} } -A constructor set as ``internal`` causes the contract to be marked as :ref:`abstract `. .. index:: ! base;constructor @@ -1009,11 +1029,11 @@ the base constructors. This can be done in two ways:: contract Base { uint x; - function Base(uint _x) public { x = _x; } + constructor(uint _x) public { x = _x; } } contract Derived is Base(7) { - function Derived(uint _y) Base(_y * _y) public { + constructor(uint _y) Base(_y * _y) public { } } From c98464db0607f62aa791aa1f7559a76623e254e7 Mon Sep 17 00:00:00 2001 From: Daniel Kirchner Date: Mon, 5 Mar 2018 16:35:31 +0100 Subject: [PATCH 086/197] Remove redundant test and enforce success without warnings. --- test/libsolidity/SolidityNameAndTypeResolution.cpp | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index 874cc0a63..b6596327c 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -967,13 +967,7 @@ BOOST_AUTO_TEST_CASE(new_constructor_syntax) char const* text = R"( contract A { constructor() public {} } )"; - CHECK_SUCCESS(text); - - text = R"( - pragma experimental "v0.5.0"; - contract A { constructor() public {} } - )"; - CHECK_SUCCESS(text); + CHECK_SUCCESS_NO_WARNINGS(text); } BOOST_AUTO_TEST_CASE(old_constructor_syntax) From f855c78a0839d6f569cb88eda290439b69a680e3 Mon Sep 17 00:00:00 2001 From: Daniel Kirchner Date: Tue, 6 Mar 2018 17:37:47 +0100 Subject: [PATCH 087/197] Update version pragma and use new constructor syntax in std/ contracts. --- docs/contracts.rst | 6 +++--- std/StandardToken.sol | 4 ++-- std/owned.sol | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/docs/contracts.rst b/docs/contracts.rst index c4eda8dcf..e3f5bbace 100644 --- a/docs/contracts.rst +++ b/docs/contracts.rst @@ -40,7 +40,7 @@ This means that cyclic creation dependencies are impossible. :: - pragma solidity ^0.4.20; // should actually be 0.4.21 + pragma solidity >0.4.21; contract OwnedToken { // TokenCreator is a contract type that is defined below. @@ -981,7 +981,7 @@ Constructor functions can be either ``public`` or ``internal``. :: - pragma solidity ^0.4.20; // should actually be 0.4.21 + pragma solidity >0.4.21; contract A { uint public a; @@ -998,7 +998,7 @@ Constructor functions can be either ``public`` or ``internal``. A constructor set as ``internal`` causes the contract to be marked as :ref:`abstract `. .. note :: - Prior to version 0.4.21, constructors were defined as functions with the same name as the contract. This syntax is now deprecated. + Prior to version 0.4.22, constructors were defined as functions with the same name as the contract. This syntax is now deprecated. :: diff --git a/std/StandardToken.sol b/std/StandardToken.sol index 1b218d67d..5afc9747c 100644 --- a/std/StandardToken.sol +++ b/std/StandardToken.sol @@ -1,4 +1,4 @@ -pragma solidity ^0.4.0; +pragma solidity >0.4.21; import "./Token.sol"; @@ -8,7 +8,7 @@ contract StandardToken is Token { mapping (address => mapping (address => uint256)) m_allowance; - function StandardToken(address _initialOwner, uint256 _supply) public { + constructor(address _initialOwner, uint256 _supply) public { supply = _supply; balance[_initialOwner] = _supply; } diff --git a/std/owned.sol b/std/owned.sol index ee9860d34..8e1d59172 100644 --- a/std/owned.sol +++ b/std/owned.sol @@ -1,4 +1,4 @@ -pragma solidity ^0.4.0; +pragma solidity >0.4.21; contract owned { address owner; @@ -9,7 +9,7 @@ contract owned { } } - function owned() public { + constructor() public { owner = msg.sender; } } From e2dac9ed397c29bfe426912c28ef2d419b2324c8 Mon Sep 17 00:00:00 2001 From: Daniel Kirchner Date: Wed, 7 Mar 2018 16:06:09 +0100 Subject: [PATCH 088/197] Set header.isConstructor for old style constructors in parseFunctionHeader as well. --- libsolidity/parsing/Parser.cpp | 16 +++++++++++----- libsolidity/parsing/Parser.h | 6 +++++- 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/libsolidity/parsing/Parser.cpp b/libsolidity/parsing/Parser.cpp index e5cc69d89..6ae66eee4 100644 --- a/libsolidity/parsing/Parser.cpp +++ b/libsolidity/parsing/Parser.cpp @@ -332,14 +332,18 @@ StateMutability Parser::parseStateMutability(Token::Value _token) return stateMutability; } -Parser::FunctionHeaderParserResult Parser::parseFunctionHeader(bool _forceEmptyName, bool _allowModifiers) +Parser::FunctionHeaderParserResult Parser::parseFunctionHeader( + bool _forceEmptyName, + bool _allowModifiers, + ASTString const* _contractName +) { RecursionGuard recursionGuard(*this); FunctionHeaderParserResult result; if (m_scanner->currentToken() == Token::Function) // In case of old style constructors, i.e. functions with the same name as the contract, - // this is set to true later in parseFunctionDefinitionOrFunctionTypeStateVariable. + // this is set to true below. result.isConstructor = false; else if (m_scanner->currentToken() == Token::Identifier && m_scanner->currentLiteral() == "constructor") result.isConstructor = true; @@ -351,6 +355,10 @@ Parser::FunctionHeaderParserResult Parser::parseFunctionHeader(bool _forceEmptyN result.name = make_shared(); else result.name = expectIdentifierToken(); + + if (!result.name->empty() && _contractName && *result.name == *_contractName) + result.isConstructor = true; + VarDeclParserOptions options; options.allowLocationSpecifier = true; result.parameters = parseParameterList(options); @@ -420,7 +428,7 @@ ASTPointer Parser::parseFunctionDefinitionOrFunctionTypeStateVariable(A if (m_scanner->currentCommentLiteral() != "") docstring = make_shared(m_scanner->currentCommentLiteral()); - FunctionHeaderParserResult header = parseFunctionHeader(false, true); + FunctionHeaderParserResult header = parseFunctionHeader(false, true, _contractName); if ( !header.modifiers.empty() || @@ -439,8 +447,6 @@ ASTPointer Parser::parseFunctionDefinitionOrFunctionTypeStateVariable(A } else m_scanner->next(); // just consume the ';' - if (_contractName && *header.name == *_contractName) - header.isConstructor = true; return nodeFactory.createNode( header.name, header.visibility, diff --git a/libsolidity/parsing/Parser.h b/libsolidity/parsing/Parser.h index 2d0e52e18..eb120a614 100644 --- a/libsolidity/parsing/Parser.h +++ b/libsolidity/parsing/Parser.h @@ -74,7 +74,11 @@ private: ASTPointer parseInheritanceSpecifier(); Declaration::Visibility parseVisibilitySpecifier(Token::Value _token); StateMutability parseStateMutability(Token::Value _token); - FunctionHeaderParserResult parseFunctionHeader(bool _forceEmptyName, bool _allowModifiers); + FunctionHeaderParserResult parseFunctionHeader( + bool _forceEmptyName, + bool _allowModifiers, + ASTString const* _contractName = nullptr + ); ASTPointer parseFunctionDefinitionOrFunctionTypeStateVariable(ASTString const* _contractName); ASTPointer parseFunctionDefinition(ASTString const* _contractName); ASTPointer parseStructDefinition(); From 8f66390f56c866f70ab6617dac1f0713ace2d7ff Mon Sep 17 00:00:00 2001 From: Daniel Kirchner Date: Fri, 9 Mar 2018 14:23:48 +0100 Subject: [PATCH 089/197] Set isConstructor to false unconditionally and update to true later for constructors. --- libsolidity/parsing/Parser.cpp | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/libsolidity/parsing/Parser.cpp b/libsolidity/parsing/Parser.cpp index 6ae66eee4..3dbd4c8f6 100644 --- a/libsolidity/parsing/Parser.cpp +++ b/libsolidity/parsing/Parser.cpp @@ -341,13 +341,11 @@ Parser::FunctionHeaderParserResult Parser::parseFunctionHeader( RecursionGuard recursionGuard(*this); FunctionHeaderParserResult result; - if (m_scanner->currentToken() == Token::Function) - // In case of old style constructors, i.e. functions with the same name as the contract, - // this is set to true below. - result.isConstructor = false; - else if (m_scanner->currentToken() == Token::Identifier && m_scanner->currentLiteral() == "constructor") + result.isConstructor = false; + + if (m_scanner->currentToken() == Token::Identifier && m_scanner->currentLiteral() == "constructor") result.isConstructor = true; - else + else if (m_scanner->currentToken() != Token::Function) solAssert(false, "Function or constructor expected."); m_scanner->next(); From 3ae326139a505bed877d5b9ac9b4b3ed84496c3d Mon Sep 17 00:00:00 2001 From: Daniel Kirchner Date: Fri, 9 Mar 2018 14:25:57 +0100 Subject: [PATCH 090/197] Document absence of constructors. --- docs/contracts.rst | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/contracts.rst b/docs/contracts.rst index e3f5bbace..9ae802093 100644 --- a/docs/contracts.rst +++ b/docs/contracts.rst @@ -977,7 +977,9 @@ virtual method lookup. 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``. +Constructor functions can be either ``public`` or ``internal``. If there is no constructor, the contract will assume the +default constructor: ``contructor() public {}``. + :: From 17bcabb6cfb42a3983ecb79768d44622507ce292 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Thu, 29 Mar 2018 12:10:39 +0100 Subject: [PATCH 091/197] Remove useless SWAP1 in front of commutative operations --- Changelog.md | 1 + libevmasm/PeepholeOptimiser.cpp | 21 +++++++++++++- test/libevmasm/Optimiser.cpp | 51 +++++++++++++++++++++++++++++++++ 3 files changed, 72 insertions(+), 1 deletion(-) diff --git a/Changelog.md b/Changelog.md index 34c3b0e98..cdfdf6aed 100644 --- a/Changelog.md +++ b/Changelog.md @@ -5,6 +5,7 @@ Features: * Commandline interface: Error when missing or inaccessible file detected. Suppress it with the ``--ignore-missing`` flag. * General: Support accessing dynamic return data in post-byzantium EVMs. * Interfaces: Allow overriding external functions in interfaces with public in an implementing contract. + * Optimizer: Remove useless ``SWAP1`` instruction preceding a commutative instruction (such as ``ADD``, ``MUL``, etc). * Optimizer: Optimize across ``mload`` if ``msize()`` is not used. * Syntax Checker: Issue warning for empty structs (or error as experimental 0.5.0 feature). diff --git a/libevmasm/PeepholeOptimiser.cpp b/libevmasm/PeepholeOptimiser.cpp index 168d11090..306465453 100644 --- a/libevmasm/PeepholeOptimiser.cpp +++ b/libevmasm/PeepholeOptimiser.cpp @@ -154,6 +154,25 @@ struct DoublePush: SimplePeepholeOptimizerMethod } }; +struct CommutativeSwap: SimplePeepholeOptimizerMethod +{ + static bool applySimple(AssemblyItem const& _swap, AssemblyItem const& _op, std::back_insert_iterator _out) + { + // Remove SWAP1 if following instruction is commutative + if ( + _swap.type() == Operation && + _swap.instruction() == Instruction::SWAP1 && + SemanticInformation::isCommutativeOperation(_op) + ) + { + *_out = _op; + return true; + } + else + return false; + } +}; + struct JumpToNext: SimplePeepholeOptimizerMethod { static size_t applySimple( @@ -260,7 +279,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(), JumpToNext(), UnreachableCode(), TagConjunctions(), Identity()); + applyMethods(state, PushPop(), OpPop(), DoublePush(), DoubleSwap(), CommutativeSwap(), JumpToNext(), UnreachableCode(), TagConjunctions(), 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/test/libevmasm/Optimiser.cpp b/test/libevmasm/Optimiser.cpp index 28f6b8c0f..6b588e753 100644 --- a/test/libevmasm/Optimiser.cpp +++ b/test/libevmasm/Optimiser.cpp @@ -858,6 +858,57 @@ BOOST_AUTO_TEST_CASE(peephole_pop_calldatasize) BOOST_CHECK(items.empty()); } +BOOST_AUTO_TEST_CASE(peephole_commutative_swap1) +{ + AssemblyItems items{ + u256(1), + u256(2), + Instruction::SWAP1, + Instruction::ADD, + u256(4), + u256(5) + }; + AssemblyItems expectation{ + u256(1), + u256(2), + Instruction::ADD, + u256(4), + u256(5) + }; + PeepholeOptimiser peepOpt(items); + BOOST_REQUIRE(peepOpt.optimise()); + BOOST_CHECK_EQUAL_COLLECTIONS( + items.begin(), items.end(), + expectation.begin(), expectation.end() + ); +} + +BOOST_AUTO_TEST_CASE(peephole_noncommutative_swap1) +{ + AssemblyItems items{ + u256(1), + u256(2), + Instruction::SWAP1, + Instruction::SUB, + u256(4), + u256(5) + }; + AssemblyItems expectation{ + u256(1), + u256(2), + Instruction::SWAP1, + Instruction::SUB, + u256(4), + u256(5) + }; + 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{ From be6051beadcc54705f35738d37af017d654c2c01 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Tue, 3 Apr 2018 18:05:06 +0200 Subject: [PATCH 092/197] Test multiple instructions with the (non)commutative peephole optimiser --- test/libevmasm/Optimiser.cpp | 99 ++++++++++++++++++++++-------------- 1 file changed, 62 insertions(+), 37 deletions(-) diff --git a/test/libevmasm/Optimiser.cpp b/test/libevmasm/Optimiser.cpp index 6b588e753..b622b4fb9 100644 --- a/test/libevmasm/Optimiser.cpp +++ b/test/libevmasm/Optimiser.cpp @@ -860,53 +860,78 @@ BOOST_AUTO_TEST_CASE(peephole_pop_calldatasize) BOOST_AUTO_TEST_CASE(peephole_commutative_swap1) { - AssemblyItems items{ - u256(1), - u256(2), - Instruction::SWAP1, + vector ops{ Instruction::ADD, - u256(4), - u256(5) + Instruction::MUL, + Instruction::EQ, + Instruction::AND, + Instruction::OR, + Instruction::XOR }; - AssemblyItems expectation{ - u256(1), - u256(2), - Instruction::ADD, - u256(4), - u256(5) - }; - PeepholeOptimiser peepOpt(items); - BOOST_REQUIRE(peepOpt.optimise()); - BOOST_CHECK_EQUAL_COLLECTIONS( + for (Instruction const op: ops) + { + AssemblyItems items{ + u256(1), + u256(2), + Instruction::SWAP1, + op, + u256(4), + u256(5) + }; + AssemblyItems expectation{ + u256(1), + u256(2), + op, + u256(4), + u256(5) + }; + PeepholeOptimiser peepOpt(items); + BOOST_REQUIRE(peepOpt.optimise()); + BOOST_CHECK_EQUAL_COLLECTIONS( items.begin(), items.end(), - expectation.begin(), expectation.end() - ); + expectation.begin(), expectation.end() + ); + } } BOOST_AUTO_TEST_CASE(peephole_noncommutative_swap1) { - AssemblyItems items{ - u256(1), - u256(2), - Instruction::SWAP1, + // NOTE: not comprehensive + vector ops{ Instruction::SUB, - u256(4), - u256(5) + Instruction::DIV, + Instruction::SDIV, + Instruction::MOD, + Instruction::SMOD, + Instruction::EXP, + Instruction::LT, + Instruction::GT }; - AssemblyItems expectation{ - u256(1), - u256(2), - Instruction::SWAP1, - Instruction::SUB, - u256(4), - u256(5) - }; - PeepholeOptimiser peepOpt(items); - BOOST_REQUIRE(!peepOpt.optimise()); - BOOST_CHECK_EQUAL_COLLECTIONS( + for (Instruction const op: ops) + { + AssemblyItems items{ + u256(1), + u256(2), + Instruction::SWAP1, + op, + u256(4), + u256(5) + }; + AssemblyItems expectation{ + u256(1), + u256(2), + Instruction::SWAP1, + op, + u256(4), + u256(5) + }; + PeepholeOptimiser peepOpt(items); + BOOST_REQUIRE(!peepOpt.optimise()); + BOOST_CHECK_EQUAL_COLLECTIONS( items.begin(), items.end(), - expectation.begin(), expectation.end() - ); + expectation.begin(), expectation.end() + ); + } } BOOST_AUTO_TEST_CASE(jumpdest_removal) From 6f9644add18b27363a423e2c7ccb0e578ba800bc Mon Sep 17 00:00:00 2001 From: Daniel Kirchner Date: Tue, 3 Apr 2018 12:05:26 +0200 Subject: [PATCH 093/197] SyntaxTests: extend syntax tests and isoltest to support parser errors and compiler exceptions. --- test/libsolidity/AnalysisFramework.cpp | 28 ++++---- test/libsolidity/AnalysisFramework.h | 7 +- test/libsolidity/SyntaxTest.cpp | 98 ++++++++------------------ test/libsolidity/SyntaxTest.h | 31 ++++---- test/tools/isoltest.cpp | 65 ++++++++++------- 5 files changed, 104 insertions(+), 125 deletions(-) diff --git a/test/libsolidity/AnalysisFramework.cpp b/test/libsolidity/AnalysisFramework.cpp index 4538757de..72b867673 100644 --- a/test/libsolidity/AnalysisFramework.cpp +++ b/test/libsolidity/AnalysisFramework.cpp @@ -56,12 +56,23 @@ AnalysisFramework::parseAnalyseAndReturnError( m_compiler.analyze(); + ErrorList errors = filterErrors(m_compiler.errors(), _reportWarnings); + if (errors.size() > 1 && !_allowMultipleErrors) + BOOST_FAIL("Multiple errors found: " + formatErrors()); + + return make_pair(&m_compiler.ast(""), std::move(errors)); +} + +ErrorList AnalysisFramework::filterErrors(ErrorList const& _errorList, bool _includeWarnings) const +{ ErrorList errors; - for (auto const& currentError: m_compiler.errors()) + for (auto const& currentError: _errorList) { solAssert(currentError->comment(), ""); if (currentError->type() == Error::Type::Warning) { + if (!_includeWarnings) + continue; bool ignoreWarning = false; for (auto const& filter: m_warningsToFilter) if (currentError->comment()->find(filter) == 0) @@ -73,17 +84,10 @@ AnalysisFramework::parseAnalyseAndReturnError( continue; } - if (_reportWarnings || (currentError->type() != Error::Type::Warning)) - { - if (!_allowMultipleErrors && !errors.empty()) - { - BOOST_FAIL("Multiple errors found: " + formatErrors()); - } - errors.emplace_back(std::move(currentError)); - } + errors.emplace_back(currentError); } - return make_pair(&m_compiler.ast(""), errors); + return errors; } SourceUnit const* AnalysisFramework::parseAndAnalyse(string const& _source) @@ -110,7 +114,7 @@ ErrorList AnalysisFramework::expectError(std::string const& _source, bool _warni return sourceAndErrors.second; } -string AnalysisFramework::formatErrors() +string AnalysisFramework::formatErrors() const { string message; for (auto const& error: m_compiler.errors()) @@ -118,7 +122,7 @@ string AnalysisFramework::formatErrors() return message; } -string AnalysisFramework::formatError(Error const& _error) +string AnalysisFramework::formatError(Error const& _error) const { return SourceReferenceFormatter::formatExceptionInformation( _error, diff --git a/test/libsolidity/AnalysisFramework.h b/test/libsolidity/AnalysisFramework.h index 6ecf4a5aa..05490a42a 100644 --- a/test/libsolidity/AnalysisFramework.h +++ b/test/libsolidity/AnalysisFramework.h @@ -57,8 +57,8 @@ protected: bool success(std::string const& _source); ErrorList expectError(std::string const& _source, bool _warning = false, bool _allowMultiple = false); - std::string formatErrors(); - std::string formatError(Error const& _error); + std::string formatErrors() const; + std::string formatError(Error const& _error) const; static ContractDefinition const* retrieveContractByName(SourceUnit const& _source, std::string const& _name); static FunctionTypePointer retrieveFunctionBySignature( @@ -66,6 +66,9 @@ protected: std::string const& _signature ); + // filter out the warnings in m_warningsToFilter or all warnings if _includeWarnings is false + ErrorList filterErrors(ErrorList const& _errorList, bool _includeWarnings) const; + std::vector m_warningsToFilter = {"This is a pre-release compiler version"}; dev::solidity::CompilerStack m_compiler; }; diff --git a/test/libsolidity/SyntaxTest.cpp b/test/libsolidity/SyntaxTest.cpp index ca0511384..329543bfb 100644 --- a/test/libsolidity/SyntaxTest.cpp +++ b/test/libsolidity/SyntaxTest.cpp @@ -16,6 +16,7 @@ */ #include +#include #include #include #include @@ -59,93 +60,52 @@ SyntaxTest::SyntaxTest(string const& _filename) bool SyntaxTest::run(ostream& _stream, string const& _linePrefix, bool const _formatted) { - m_errorList = parseAnalyseAndReturnError(m_source, true, true, true).second; - if (!matchesExpectations(m_errorList)) + m_compiler.reset(); + m_compiler.addSource("", "pragma solidity >=0.0;\n" + m_source); + m_compiler.setEVMVersion(dev::test::Options::get().evmVersion()); + + if (m_compiler.parse()) + m_compiler.analyze(); + + for (auto const& currentError: filterErrors(m_compiler.errors(), true)) + m_errorList.emplace_back(SyntaxTestError{currentError->typeName(), errorMessage(*currentError)}); + + if (m_expectations != m_errorList) { - std::string nextIndentLevel = _linePrefix + " "; + string nextIndentLevel = _linePrefix + " "; FormattedScope(_stream, _formatted, {BOLD, CYAN}) << _linePrefix << "Expected result:" << endl; - printExpected(_stream, nextIndentLevel, _formatted); + printErrorList(_stream, m_expectations, nextIndentLevel, _formatted); FormattedScope(_stream, _formatted, {BOLD, CYAN}) << _linePrefix << "Obtained result:\n"; - printErrorList(_stream, m_errorList, nextIndentLevel, false, false, _formatted); + printErrorList(_stream, m_errorList, nextIndentLevel, _formatted); return false; } return true; } -void SyntaxTest::printExpected(ostream& _stream, string const& _linePrefix, bool const _formatted) const -{ - if (m_expectations.empty()) - FormattedScope(_stream, _formatted, {BOLD, GREEN}) << _linePrefix << "Success" << endl; - else - for (auto const& expectation: m_expectations) - { - FormattedScope(_stream, _formatted, {BOLD, expectation.type == "Warning" ? YELLOW : RED}) << - _linePrefix << expectation.type << ": "; - _stream << expectation.message << endl; - } -} - void SyntaxTest::printErrorList( ostream& _stream, - ErrorList const& _errorList, + vector const& _errorList, string const& _linePrefix, - bool const _ignoreWarnings, - bool const _lineNumbers, bool const _formatted -) const +) { if (_errorList.empty()) FormattedScope(_stream, _formatted, {BOLD, GREEN}) << _linePrefix << "Success" << endl; else for (auto const& error: _errorList) { - bool isWarning = (error->type() == Error::Type::Warning); - if (isWarning && _ignoreWarnings) continue; - { - FormattedScope scope(_stream, _formatted, {BOLD, isWarning ? YELLOW : RED}); + FormattedScope scope(_stream, _formatted, {BOLD, (error.type == "Warning") ? YELLOW : RED}); _stream << _linePrefix; - if (_lineNumbers) - { - int line = offsetToLineNumber( - boost::get_error_info(*error)->start - ); - if (line >= 0) - _stream << "(" << line << "): "; - } - _stream << error->typeName() << ": "; + _stream << error.type << ": "; } - _stream << errorMessage(*error) << endl; + _stream << error.message << endl; } } -int SyntaxTest::offsetToLineNumber(int _location) const +string SyntaxTest::errorMessage(Exception const& _e) { - // parseAnalyseAndReturnError(...) prepends a version pragma - _location -= strlen("pragma solidity >=0.0;\n"); - if (_location < 0 || static_cast(_location) >= m_source.size()) - return -1; - else - return 1 + std::count(m_source.begin(), m_source.begin() + _location, '\n'); -} - -bool SyntaxTest::matchesExpectations(ErrorList const& _errorList) const -{ - if (_errorList.size() != m_expectations.size()) - return false; - else - for (size_t i = 0; i < _errorList.size(); i++) - if ( - (_errorList[i]->typeName() != m_expectations[i].type) || - (errorMessage(*_errorList[i]) != m_expectations[i].message) - ) - return false; - return true; -} - -string SyntaxTest::errorMessage(Error const& _e) -{ - if (_e.comment()) + if (_e.comment() && !_e.comment()->empty()) return boost::replace_all_copy(*_e.comment(), "\n", "\\n"); else return "NONE"; @@ -164,9 +124,9 @@ string SyntaxTest::parseSource(istream& _stream) return source; } -vector SyntaxTest::parseExpectations(istream& _stream) +vector SyntaxTest::parseExpectations(istream& _stream) { - vector expectations; + vector expectations; string line; while (getline(_stream, line)) { @@ -188,7 +148,7 @@ vector SyntaxTest::parseExpectations(istream& _stream) skipWhitespace(it, line.end()); string errorMessage(it, line.end()); - expectations.emplace_back(SyntaxTestExpectation{move(errorType), move(errorMessage)}); + expectations.emplace_back(SyntaxTestError{move(errorType), move(errorMessage)}); } return expectations; } @@ -239,9 +199,11 @@ int SyntaxTest::registerTests( _suite.add(make_test_case( [fullpath] { - std::stringstream errorStream; - if (!SyntaxTest(fullpath.string()).run(errorStream)) - BOOST_ERROR("Test expectation mismatch.\n" + errorStream.str()); + 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(), diff --git a/test/libsolidity/SyntaxTest.h b/test/libsolidity/SyntaxTest.h index cb6ee05cd..dddd86ef4 100644 --- a/test/libsolidity/SyntaxTest.h +++ b/test/libsolidity/SyntaxTest.h @@ -36,10 +36,14 @@ namespace solidity namespace test { -struct SyntaxTestExpectation +struct SyntaxTestError { std::string type; std::string message; + bool operator==(SyntaxTestError const& _rhs) const + { + return type == _rhs.type && message == _rhs.message; + } }; @@ -50,21 +54,16 @@ public: bool run(std::ostream& _stream, std::string const& _linePrefix = "", bool const _formatted = false); - std::vector const& expectations() const { return m_expectations; } + std::vector const& expectations() const { return m_expectations; } std::string const& source() const { return m_source; } - ErrorList const& errorList() const { return m_errorList; } - ErrorList const& compilerErrors() const { return m_compiler.errors(); } + std::vector const& errorList() const { return m_errorList; } - void printExpected(std::ostream& _stream, std::string const& _linePrefix, bool const _formatted = false) const; - - void printErrorList( + static void printErrorList( std::ostream& _stream, - ErrorList const& _errors, + std::vector const& _errors, std::string const& _linePrefix, - bool const _ignoreWarnings, - bool const _lineNumbers, bool const _formatted = false - ) const; + ); static int registerTests( boost::unit_test::test_suite& _suite, @@ -72,16 +71,14 @@ public: boost::filesystem::path const& _path ); static bool isTestFilename(boost::filesystem::path const& _filename); + static std::string errorMessage(Exception const& _e); private: - bool matchesExpectations(ErrorList const& _errors) const; - static std::string errorMessage(Error const& _e); static std::string parseSource(std::istream& _stream); - static std::vector parseExpectations(std::istream& _stream); - int offsetToLineNumber(int _location) const; + static std::vector parseExpectations(std::istream& _stream); std::string m_source; - std::vector m_expectations; - ErrorList m_errorList; + std::vector m_expectations; + std::vector m_errorList; }; } diff --git a/test/tools/isoltest.cpp b/test/tools/isoltest.cpp index 5efec4211..07df8f60a 100644 --- a/test/tools/isoltest.cpp +++ b/test/tools/isoltest.cpp @@ -55,8 +55,8 @@ public: { Success, Failure, - ParserError, - InputOutputError + InputOutputError, + Exception }; Result process(); @@ -76,7 +76,7 @@ private: Quit }; - Request handleResponse(bool const _parserError); + Request handleResponse(bool const _exception); void printContract() const; @@ -100,7 +100,6 @@ void SyntaxTestTool::printContract() const SyntaxTestTool::Result SyntaxTestTool::process() { bool success; - bool parserError = false; std::stringstream outputMessages; (FormattedScope(cout, m_formatted, {BOLD}) << m_name << ": ").flush(); @@ -119,10 +118,35 @@ SyntaxTestTool::Result SyntaxTestTool::process() { success = m_test->run(outputMessages, " ", m_formatted); } - catch (...) + catch(CompilerError const& _e) { - success = false; - parserError = true; + 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) + { + 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; + return Result::Exception; + } + catch(...) + { + FormattedScope(cout, m_formatted, {BOLD, RED}) << + "Unknown Exception" << endl; + return Result::Exception; } if (success) @@ -137,25 +161,14 @@ SyntaxTestTool::Result SyntaxTestTool::process() FormattedScope(cout, m_formatted, {BOLD, CYAN}) << " Contract:" << endl; printContract(); - if (parserError) - { - cout << " "; - FormattedScope(cout, m_formatted, {INVERSE, RED}) << "Parsing failed:" << endl; - m_test->printErrorList(cout, m_test->compilerErrors(), " ", true, true, m_formatted); - cout << endl; - return Result::ParserError; - } - else - { - cout << outputMessages.str() << endl; - return Result::Failure; - } + cout << outputMessages.str() << endl; + return Result::Failure; } } -SyntaxTestTool::Request SyntaxTestTool::handleResponse(bool const _parserError) +SyntaxTestTool::Request SyntaxTestTool::handleResponse(bool const _exception) { - if (_parserError) + if (_exception) cout << "(e)dit/(s)kip/(q)uit? "; else cout << "(e)dit/(u)pdate expectations/(s)kip/(q)uit? "; @@ -169,7 +182,7 @@ SyntaxTestTool::Request SyntaxTestTool::handleResponse(bool const _parserError) cout << endl; return Request::Skip; case 'u': - if (_parserError) + if (_exception) break; else { @@ -178,7 +191,7 @@ SyntaxTestTool::Request SyntaxTestTool::handleResponse(bool const _parserError) file << m_test->source(); file << "// ----" << endl; if (!m_test->errorList().empty()) - m_test->printErrorList(file, m_test->errorList(), "// ", false, false, false); + m_test->printErrorList(file, m_test->errorList(), "// ", false); return Request::Rerun; } case 'e': @@ -231,8 +244,8 @@ SyntaxTestStats SyntaxTestTool::processPath( switch(result) { case Result::Failure: - case Result::ParserError: - switch(testTool.handleResponse(result == Result::ParserError)) + case Result::Exception: + switch(testTool.handleResponse(result == Result::Exception)) { case Request::Quit: return { successCount, runCount }; From 87ad337ae0c055c21476ef9961e52a9d7d3072bb Mon Sep 17 00:00:00 2001 From: Daniel Kirchner Date: Tue, 3 Apr 2018 14:41:53 +0200 Subject: [PATCH 094/197] Adds parsing sub-directory in syntaxTests and moves two example tests from SolidityParser.cpp to test contracts. --- test/libsolidity/SolidityParser.cpp | 20 ------------------- .../missing_variable_name_in_declaration.sol | 5 +++++ .../syntaxTests/parsing/smoke_test.sol | 4 ++++ 3 files changed, 9 insertions(+), 20 deletions(-) create mode 100644 test/libsolidity/syntaxTests/parsing/missing_variable_name_in_declaration.sol create mode 100644 test/libsolidity/syntaxTests/parsing/smoke_test.sol diff --git a/test/libsolidity/SolidityParser.cpp b/test/libsolidity/SolidityParser.cpp index 4e862f608..93e6bcaa4 100644 --- a/test/libsolidity/SolidityParser.cpp +++ b/test/libsolidity/SolidityParser.cpp @@ -112,26 +112,6 @@ while(0) BOOST_AUTO_TEST_SUITE(SolidityParser) -BOOST_AUTO_TEST_CASE(smoke_test) -{ - char const* text = R"( - contract test { - uint256 stateVariable1; - } - )"; - BOOST_CHECK(successParse(text)); -} - -BOOST_AUTO_TEST_CASE(missing_variable_name_in_declaration) -{ - char const* text = R"( - contract test { - uint256 ; - } - )"; - CHECK_PARSE_ERROR(text, "Expected identifier"); -} - BOOST_AUTO_TEST_CASE(empty_function) { char const* text = R"( diff --git a/test/libsolidity/syntaxTests/parsing/missing_variable_name_in_declaration.sol b/test/libsolidity/syntaxTests/parsing/missing_variable_name_in_declaration.sol new file mode 100644 index 000000000..c03fd97d1 --- /dev/null +++ b/test/libsolidity/syntaxTests/parsing/missing_variable_name_in_declaration.sol @@ -0,0 +1,5 @@ +contract test { + uint256 ; +} +// ---- +// ParserError: Expected identifier, got 'Semicolon' diff --git a/test/libsolidity/syntaxTests/parsing/smoke_test.sol b/test/libsolidity/syntaxTests/parsing/smoke_test.sol new file mode 100644 index 000000000..d328b1675 --- /dev/null +++ b/test/libsolidity/syntaxTests/parsing/smoke_test.sol @@ -0,0 +1,4 @@ +contract test { + uint256 stateVariable1; +} +// ---- From 86c5d6aaadf16e823ad7793e90a564cda2a10a4c Mon Sep 17 00:00:00 2001 From: Roman Date: Wed, 4 Apr 2018 09:02:58 +0300 Subject: [PATCH 095/197] hash256 -> bytes32 fix in misleading note As it was described here: https://ethereum.stackexchange.com/questions/44628/understanding-low-level-interface-to-logs/44629?noredirect=1#comment52316_44629 --- docs/contracts.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/contracts.rst b/docs/contracts.rst index 9ae802093..f8a44fb3b 100644 --- a/docs/contracts.rst +++ b/docs/contracts.rst @@ -803,7 +803,7 @@ as topics. The event call above can be performed in the same way as } where the long hexadecimal number is equal to -``keccak256("Deposit(address,hash256,uint256)")``, the signature of the event. +``keccak256("Deposit(address,bytes32,uint256)")``, the signature of the event. Additional Resources for Understanding Events ============================================== From fa92380af05219cc604ddb1a9a9fadcfe76097c8 Mon Sep 17 00:00:00 2001 From: chriseth Date: Wed, 4 Apr 2018 12:26:34 +0200 Subject: [PATCH 096/197] Add scripts to build the eth binary via docker. --- scripts/cpp-ethereum/build.sh | 17 +++++++++++++++++ scripts/cpp-ethereum/eth_artful.docker | 7 +++++++ scripts/cpp-ethereum/eth_trusty.docker | 7 +++++++ 3 files changed, 31 insertions(+) create mode 100755 scripts/cpp-ethereum/build.sh create mode 100644 scripts/cpp-ethereum/eth_artful.docker create mode 100644 scripts/cpp-ethereum/eth_trusty.docker diff --git a/scripts/cpp-ethereum/build.sh b/scripts/cpp-ethereum/build.sh new file mode 100755 index 000000000..23ed12906 --- /dev/null +++ b/scripts/cpp-ethereum/build.sh @@ -0,0 +1,17 @@ +#!/usr/bin/env sh + +# Script to build the eth binary from latest develop +# for ubuntu trusty and ubuntu artful. +# Requires docker. + +set -e + +REPO_ROOT="$(dirname "$0")"/../.. + +for rel in artful trusty +do + docker build -t eth_$rel -f "$REPO_ROOT"/scripts/cpp-ethereum/eth_$rel.docker . + tmp_container=$(docker create eth_$rel sh) + echo "Built eth ($rel) at $REPO_ROOT/build/eth_$rel" + docker cp ${tmp_container}:/build/eth/eth "$REPO_ROOT"/build/eth_$rel +done \ No newline at end of file diff --git a/scripts/cpp-ethereum/eth_artful.docker b/scripts/cpp-ethereum/eth_artful.docker new file mode 100644 index 000000000..7ce9faae8 --- /dev/null +++ b/scripts/cpp-ethereum/eth_artful.docker @@ -0,0 +1,7 @@ +FROM ubuntu:artful + +RUN apt update +RUN apt -y install libleveldb-dev cmake g++ git +RUN git clone --recursive https://github.com/ethereum/cpp-ethereum --branch develop --single-branch --depth 1 +RUN mkdir /build && cd /build && cmake /cpp-ethereum -DCMAKE_BUILD_TYPE=RelWithDebInfo -DTOOLS=Off -DTESTS=Off +RUN cd /build && make eth diff --git a/scripts/cpp-ethereum/eth_trusty.docker b/scripts/cpp-ethereum/eth_trusty.docker new file mode 100644 index 000000000..0c4d7a02c --- /dev/null +++ b/scripts/cpp-ethereum/eth_trusty.docker @@ -0,0 +1,7 @@ +FROM ubuntu:trusty + +RUN apt update +RUN apt -y install libleveldb-dev cmake g++ git +RUN git clone --recursive https://github.com/ethereum/cpp-ethereum --branch develop --single-branch --depth 1 +RUN mkdir /build && cd /build && cmake /cpp-ethereum -DCMAKE_BUILD_TYPE=RelWithDebInfo -DTOOLS=Off -DTESTS=Off +RUN cd /build && make eth From 0cbe55005de79b0f7c5c770d50c3eb87df019789 Mon Sep 17 00:00:00 2001 From: chriseth Date: Tue, 13 Mar 2018 15:21:38 +0100 Subject: [PATCH 097/197] Create empty dynamic memory arrays more efficiently. --- docs/assembly.rst | 5 ++++ docs/miscellaneous.rst | 11 ++++--- libsolidity/codegen/CompilerUtils.cpp | 30 ++++++++++++++----- libsolidity/codegen/CompilerUtils.h | 7 +++++ test/libsolidity/JSONCompiler.cpp | 8 ++--- test/libsolidity/SolidityEndToEndTest.cpp | 36 ++++++++++++++++++++++- test/libsolidity/SolidityOptimizer.cpp | 24 +++++++++++++++ test/libsolidity/StandardCompiler.cpp | 6 ++-- 8 files changed, 107 insertions(+), 20 deletions(-) diff --git a/docs/assembly.rst b/docs/assembly.rst index cf9bf840f..705cd1b84 100644 --- a/docs/assembly.rst +++ b/docs/assembly.rst @@ -647,6 +647,11 @@ 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. +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. + 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 diff --git a/docs/miscellaneous.rst b/docs/miscellaneous.rst index 01154854c..20400aa26 100644 --- a/docs/miscellaneous.rst +++ b/docs/miscellaneous.rst @@ -64,12 +64,15 @@ The position of ``data[4][9].b`` is at ``keccak256(uint256(9) . keccak256(uint25 Layout in Memory **************** -Solidity reserves three 256-bit slots: +Solidity reserves four 32 byte slots: -- 0 - 64: scratch space for hashing methods -- 64 - 96: currently allocated memory size (aka. free memory pointer) +- ``0x00`` - ``0x3f``: scratch space for hashing methods +- ``0x40`` - ``0x5f``: currently allocated memory size (aka. free memory pointer) +- ``0x60`` - ``0x7f``: zero slot -Scratch space can be used between statements (ie. within inline assembly). +Scratch space can be used between statements (ie. 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). diff --git a/libsolidity/codegen/CompilerUtils.cpp b/libsolidity/codegen/CompilerUtils.cpp index deaef017a..79aef7b06 100644 --- a/libsolidity/codegen/CompilerUtils.cpp +++ b/libsolidity/codegen/CompilerUtils.cpp @@ -21,6 +21,7 @@ */ #include + #include #include #include @@ -39,11 +40,17 @@ namespace solidity const unsigned CompilerUtils::dataStartOffset = 4; const size_t CompilerUtils::freeMemoryPointer = 64; +const size_t CompilerUtils::zeroPointer = CompilerUtils::freeMemoryPointer + 32; +const size_t CompilerUtils::generalPurposeMemoryStart = CompilerUtils::zeroPointer + 32; const unsigned CompilerUtils::identityContractAddress = 4; +static_assert(CompilerUtils::freeMemoryPointer >= 64, "Free memory pointer must not overlap with scratch area."); +static_assert(CompilerUtils::zeroPointer >= CompilerUtils::freeMemoryPointer + 32, "Zero pointer must not overlap with free memory pointer."); +static_assert(CompilerUtils::generalPurposeMemoryStart >= CompilerUtils::zeroPointer + 32, "General purpose memory must not overlap with zero area."); + void CompilerUtils::initialiseFreeMemoryPointer() { - m_context << u256(freeMemoryPointer + 32); + m_context << u256(generalPurposeMemoryStart); storeFreeMemoryPointer(); } @@ -1051,6 +1058,13 @@ void CompilerUtils::pushZeroValue(Type const& _type) return; } solAssert(referenceType->location() == DataLocation::Memory, ""); + if (auto arrayType = dynamic_cast(&_type)) + if (arrayType->isDynamicallySized()) + { + // Push a memory location that is (hopefully) always zero. + pushZeroPointer(); + return; + } TypePointer type = _type.shared_from_this(); m_context.callLowLevelFunction( @@ -1071,13 +1085,8 @@ void CompilerUtils::pushZeroValue(Type const& _type) } else if (auto arrayType = dynamic_cast(type.get())) { - if (arrayType->isDynamicallySized()) - { - // zero length - _context << u256(0); - utils.storeInMemoryDynamic(IntegerType(256)); - } - else if (arrayType->length() > 0) + solAssert(!arrayType->isDynamicallySized(), ""); + if (arrayType->length() > 0) { _context << arrayType->length() << Instruction::SWAP1; // stack: items_to_do memory_pos @@ -1094,6 +1103,11 @@ void CompilerUtils::pushZeroValue(Type const& _type) ); } +void CompilerUtils::pushZeroPointer() +{ + m_context << u256(zeroPointer); +} + void CompilerUtils::moveToStackVariable(VariableDeclaration const& _variable) { unsigned const stackPosition = m_context.baseToCurrentStackOffset(m_context.baseStackOffsetOfVariable(_variable)); diff --git a/libsolidity/codegen/CompilerUtils.h b/libsolidity/codegen/CompilerUtils.h index 389673ef0..a32c5c6e1 100644 --- a/libsolidity/codegen/CompilerUtils.h +++ b/libsolidity/codegen/CompilerUtils.h @@ -210,6 +210,9 @@ public: /// Creates a zero-value for the given type and puts it onto the stack. This might allocate /// memory for memory references. void pushZeroValue(Type const& _type); + /// Pushes a pointer to the stack that points to a (potentially shared) location in memory + /// that always contains a zero. It is not allowed to write there. + void pushZeroPointer(); /// Moves the value that is at the top of the stack to a stack variable. void moveToStackVariable(VariableDeclaration const& _variable); @@ -255,6 +258,10 @@ public: /// Position of the free-memory-pointer in memory; static const size_t freeMemoryPointer; + /// Position of the memory slot that is always zero. + static const size_t zeroPointer; + /// Starting offset for memory available to the user (aka the contract). + static const size_t generalPurposeMemoryStart; private: /// Address of the precompiled identity contract. diff --git a/test/libsolidity/JSONCompiler.cpp b/test/libsolidity/JSONCompiler.cpp index aed0a3706..cdcc22a65 100644 --- a/test/libsolidity/JSONCompiler.cpp +++ b/test/libsolidity/JSONCompiler.cpp @@ -111,12 +111,12 @@ BOOST_AUTO_TEST_CASE(basic_compilation) BOOST_CHECK(contract["bytecode"].isString()); BOOST_CHECK_EQUAL( dev::test::bytecodeSansMetadata(contract["bytecode"].asString()), - "60606040523415600e57600080fd5b603580601b6000396000f3006060604052600080fd00" + "60806040523415600e57600080fd5b603580601b6000396000f3006080604052600080fd00" ); BOOST_CHECK(contract["runtimeBytecode"].isString()); BOOST_CHECK_EQUAL( dev::test::bytecodeSansMetadata(contract["runtimeBytecode"].asString()), - "6060604052600080fd00" + "6080604052600080fd00" ); BOOST_CHECK(contract["functionHashes"].isObject()); BOOST_CHECK(contract["gasEstimates"].isObject()); @@ -153,12 +153,12 @@ BOOST_AUTO_TEST_CASE(single_compilation) BOOST_CHECK(contract["bytecode"].isString()); BOOST_CHECK_EQUAL( dev::test::bytecodeSansMetadata(contract["bytecode"].asString()), - "60606040523415600e57600080fd5b603580601b6000396000f3006060604052600080fd00" + "60806040523415600e57600080fd5b603580601b6000396000f3006080604052600080fd00" ); BOOST_CHECK(contract["runtimeBytecode"].isString()); BOOST_CHECK_EQUAL( dev::test::bytecodeSansMetadata(contract["runtimeBytecode"].asString()), - "6060604052600080fd00" + "6080604052600080fd00" ); BOOST_CHECK(contract["functionHashes"].isObject()); BOOST_CHECK(contract["gasEstimates"].isObject()); diff --git a/test/libsolidity/SolidityEndToEndTest.cpp b/test/libsolidity/SolidityEndToEndTest.cpp index 38d3ce4df..b58ebee45 100644 --- a/test/libsolidity/SolidityEndToEndTest.cpp +++ b/test/libsolidity/SolidityEndToEndTest.cpp @@ -7687,7 +7687,6 @@ BOOST_AUTO_TEST_CASE(create_memory_array_allocation_size) ABI_CHECK(callContractFunction("f()"), encodeArgs(0x40, 0x40, 0x20 + 256)); } - BOOST_AUTO_TEST_CASE(memory_arrays_of_various_sizes) { // Computes binomial coefficients the chinese way @@ -7710,6 +7709,41 @@ BOOST_AUTO_TEST_CASE(memory_arrays_of_various_sizes) ABI_CHECK(callContractFunction("f(uint256,uint256)", encodeArgs(u256(9), u256(5))), encodeArgs(u256(70))); } +BOOST_AUTO_TEST_CASE(create_multiple_dynamic_arrays) +{ + char const* sourceCode = R"( + contract C { + function f() returns (uint) { + uint[][] memory x = new uint[][](42); + assert(x[0].length == 0); + x[0] = new uint[](1); + x[0][0] = 1; + assert(x[4].length == 0); + x[4] = new uint[](1); + x[4][0] = 2; + assert(x[10].length == 0); + x[10] = new uint[](1); + x[10][0] = 44; + uint[][] memory y = new uint[][](24); + assert(y[0].length == 0); + y[0] = new uint[](1); + y[0][0] = 1; + assert(y[4].length == 0); + y[4] = new uint[](1); + y[4][0] = 2; + assert(y[10].length == 0); + y[10] = new uint[](1); + y[10][0] = 88; + if ((x[0][0] == y[0][0]) && (x[4][0] == y[4][0]) && (x[10][0] == 44) && (y[10][0] == 88)) + return 7; + return 0; + } + } + )"; + compileAndRun(sourceCode, 0, "C"); + ABI_CHECK(callContractFunction("f()"), encodeArgs(u256(7))); +} + BOOST_AUTO_TEST_CASE(memory_overwrite) { char const* sourceCode = R"( diff --git a/test/libsolidity/SolidityOptimizer.cpp b/test/libsolidity/SolidityOptimizer.cpp index cf4550c71..a0df76f33 100644 --- a/test/libsolidity/SolidityOptimizer.cpp +++ b/test/libsolidity/SolidityOptimizer.cpp @@ -93,8 +93,10 @@ public: { m_contractAddress = m_nonOptimizedContract; bytes nonOptimizedOutput = callContractFunction(_sig, _arguments...); + m_gasUsedNonOptimized = m_gasUsed; m_contractAddress = m_optimizedContract; bytes optimizedOutput = callContractFunction(_sig, _arguments...); + m_gasUsedOptimized = m_gasUsed; BOOST_CHECK_MESSAGE(!optimizedOutput.empty(), "No optimized output for " + _sig); BOOST_CHECK_MESSAGE(!nonOptimizedOutput.empty(), "No un-optimized output for " + _sig); BOOST_CHECK_MESSAGE(nonOptimizedOutput == optimizedOutput, "Computed values do not match." @@ -120,6 +122,8 @@ public: } protected: + u256 m_gasUsedOptimized; + u256 m_gasUsedNonOptimized; bytes m_nonOptimizedBytecode; bytes m_optimizedBytecode; Address m_optimizedContract; @@ -584,6 +588,26 @@ BOOST_AUTO_TEST_CASE(invalid_state_at_control_flow_join) compareVersions("test()"); } +BOOST_AUTO_TEST_CASE(init_empty_dynamic_arrays) +{ + // This is not so much an optimizer test, but rather a test + // that allocating empty arrays is implemented efficiently. + // In particular, initializing a dynamic memory array does + // not use any memory. + char const* sourceCode = R"( + contract Test { + function f() pure returns (uint r) { + uint[][] memory x = new uint[][](20000); + return x.length; + } + } + )"; + compileBothVersions(sourceCode); + compareVersions("f()"); + BOOST_CHECK_LE(m_gasUsedNonOptimized, 1900000); + BOOST_CHECK_LE(1600000, m_gasUsedNonOptimized); +} + BOOST_AUTO_TEST_CASE(optimise_multi_stores) { char const* sourceCode = R"( diff --git a/test/libsolidity/StandardCompiler.cpp b/test/libsolidity/StandardCompiler.cpp index dd6eb7c40..b285a2a0e 100644 --- a/test/libsolidity/StandardCompiler.cpp +++ b/test/libsolidity/StandardCompiler.cpp @@ -261,14 +261,14 @@ BOOST_AUTO_TEST_CASE(basic_compilation) BOOST_CHECK(contract["evm"]["bytecode"]["object"].isString()); BOOST_CHECK_EQUAL( dev::test::bytecodeSansMetadata(contract["evm"]["bytecode"]["object"].asString()), - "60606040523415600e57600080fd5b603580601b6000396000f3006060604052600080fd00" + "60806040523415600e57600080fd5b603580601b6000396000f3006080604052600080fd00" ); BOOST_CHECK(contract["evm"]["assembly"].isString()); BOOST_CHECK(contract["evm"]["assembly"].asString().find( - " /* \"fileA\":0:14 contract A { } */\n mstore(0x40, 0x60)\n jumpi(tag_1, iszero(callvalue))\n" + " /* \"fileA\":0:14 contract A { } */\n mstore(0x40, 0x80)\n jumpi(tag_1, iszero(callvalue))\n" " 0x0\n dup1\n revert\ntag_1:\n dataSize(sub_0)\n dup1\n dataOffset(sub_0)\n 0x0\n codecopy\n 0x0\n" " return\nstop\n\nsub_0: assembly {\n /* \"fileA\":0:14 contract A { } */\n" - " mstore(0x40, 0x60)\n 0x0\n dup1\n revert\n\n" + " mstore(0x40, 0x80)\n 0x0\n dup1\n revert\n\n" " auxdata: 0xa165627a7a7230582") == 0); BOOST_CHECK(contract["evm"]["gasEstimates"].isObject()); BOOST_CHECK_EQUAL( From 0f7e18780f9afb1552feaf2100bde70aace7b2bf Mon Sep 17 00:00:00 2001 From: chriseth Date: Wed, 4 Apr 2018 15:07:29 +0200 Subject: [PATCH 098/197] Clarify code state of contracts under construction. --- docs/introduction-to-smart-contracts.rst | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/docs/introduction-to-smart-contracts.rst b/docs/introduction-to-smart-contracts.rst index 56f0fe3e5..84b1fff80 100644 --- a/docs/introduction-to-smart-contracts.rst +++ b/docs/introduction-to-smart-contracts.rst @@ -326,7 +326,13 @@ EVM bytecode and executed. The output 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 -returns that code. +returns that code when executed. + +.. note:: + While a contract is being created, its code is still empty. + Because of that, you should not call back into the + contract under construction until its constructor has + finished executing. .. index:: ! gas, ! gas price From 65f18a18de410b46844892e69b0578a61669aac1 Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 8 Mar 2018 15:38:14 +0100 Subject: [PATCH 099/197] More specific push implementation. --- Changelog.md | 1 + libsolidity/codegen/ArrayUtils.cpp | 49 ++++++++++++++++ libsolidity/codegen/ArrayUtils.h | 6 ++ libsolidity/codegen/ExpressionCompiler.cpp | 29 +++++----- test/libsolidity/SolidityEndToEndTest.cpp | 65 ++++++++++++++++++++++ test/libsolidity/SolidityOptimizer.cpp | 4 +- 6 files changed, 139 insertions(+), 15 deletions(-) diff --git a/Changelog.md b/Changelog.md index 9618dfa74..0db5df960 100644 --- a/Changelog.md +++ b/Changelog.md @@ -2,6 +2,7 @@ Features: * Code Generator: Initialize arrays without using ``msize()``. + * Code Generator: More specialized and thus optimized implementation for ``x.push(...)`` * Commandline interface: Error when missing or inaccessible file detected. Suppress it with the ``--ignore-missing`` flag. * General: Support accessing dynamic return data in post-byzantium EVMs. * Interfaces: Allow overriding external functions in interfaces with public in an implementing contract. diff --git a/libsolidity/codegen/ArrayUtils.cpp b/libsolidity/codegen/ArrayUtils.cpp index 4703fc1f6..0fe66d2d9 100644 --- a/libsolidity/codegen/ArrayUtils.cpp +++ b/libsolidity/codegen/ArrayUtils.cpp @@ -774,6 +774,55 @@ void ArrayUtils::resizeDynamicArray(ArrayType const& _typeIn) const ); } +void ArrayUtils::incrementDynamicArraySize(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()) + { + // We almost always just add 2 (length of byte arrays is shifted left by one) + // except for the case where we transition from a short byte array + // to a long byte array, there we have to copy. + // This happens if the length is exactly 31, which means that the + // lowest-order byte (we actually use a mask with fewer bits) must + // be (31*2+0) = 62 + + m_context.appendInlineAssembly(R"({ + let data := sload(ref) + let shifted_length := and(data, 63) + // We have to copy if length is exactly 31, because that marks + // the transition between in-place and out-of-place storage. + switch shifted_length + case 62 + { + mstore(0, ref) + let data_area := keccak256(0, 0x20) + sstore(data_area, and(data, not(0xff))) + // New length is 32, encoded as (32 * 2 + 1) + sstore(ref, 65) + // Replace ref variable by new length + ref := 32 + } + default + { + sstore(ref, add(data, 2)) + // Replace ref variable by new length + if iszero(and(data, 1)) { data := shifted_length } + ref := add(div(data, 2), 1) + } + })", {"ref"}); + } + else + m_context.appendInlineAssembly(R"({ + let new_length := add(sload(ref), 1) + sstore(ref, new_length) + ref := new_length + })", {"ref"}); +} + void ArrayUtils::clearStorageLoop(TypePointer const& _type) const { m_context.callLowLevelFunction( diff --git a/libsolidity/codegen/ArrayUtils.h b/libsolidity/codegen/ArrayUtils.h index f3ddc4ee8..99786397d 100644 --- a/libsolidity/codegen/ArrayUtils.h +++ b/libsolidity/codegen/ArrayUtils.h @@ -67,6 +67,12 @@ public: /// Stack pre: reference (excludes byte offset) new_length /// Stack post: void resizeDynamicArray(ArrayType const& _type) const; + /// Increments the size of a dynamic array by one. + /// Does not touch the new data element. In case of a byte array, this might move the + /// data. + /// Stack pre: reference (excludes byte offset) + /// Stack post: new_length + void incrementDynamicArraySize(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/ExpressionCompiler.cpp b/libsolidity/codegen/ExpressionCompiler.cpp index 76aa68438..57d49ac66 100644 --- a/libsolidity/codegen/ExpressionCompiler.cpp +++ b/libsolidity/codegen/ExpressionCompiler.cpp @@ -821,24 +821,27 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) function.kind() == FunctionType::Kind::ArrayPush ? make_shared(DataLocation::Storage, paramType) : make_shared(DataLocation::Storage); - // get the current length - ArrayUtils(m_context).retrieveLength(*arrayType); - m_context << Instruction::DUP1; - // stack: ArrayReference currentLength currentLength - m_context << u256(1) << Instruction::ADD; - // stack: ArrayReference currentLength newLength - m_context << Instruction::DUP3 << Instruction::DUP2; - ArrayUtils(m_context).resizeDynamicArray(*arrayType); - m_context << Instruction::SWAP2 << Instruction::SWAP1; - // stack: newLength ArrayReference oldLength - ArrayUtils(m_context).accessIndex(*arrayType, false); - // stack: newLength storageSlot slotOffset + // stack: ArrayReference arguments[0]->accept(*this); + TypePointer const& argType = arguments[0]->annotation().type; + // stack: ArrayReference argValue + utils().moveToStackTop(argType->sizeOnStack(), 1); + // stack: argValue ArrayReference + m_context << Instruction::DUP1; + ArrayUtils(m_context).incrementDynamicArraySize(*arrayType); + // stack: argValue ArrayReference newLength + m_context << Instruction::SWAP1; + // stack: argValue newLength ArrayReference + m_context << u256(1) << Instruction::DUP3 << Instruction::SUB; + // stack: argValue newLength ArrayReference (newLength-1) + ArrayUtils(m_context).accessIndex(*arrayType, false); + // stack: argValue newLength storageSlot slotOffset + utils().moveToStackTop(3, argType->sizeOnStack()); // stack: newLength storageSlot slotOffset argValue TypePointer type = arguments[0]->annotation().type->closestTemporaryType(arrayType->baseType()); solAssert(type, ""); - utils().convertType(*arguments[0]->annotation().type, *type); + utils().convertType(*argType, *type); utils().moveToStackTop(1 + type->sizeOnStack()); utils().moveToStackTop(1 + type->sizeOnStack()); // stack: newLength argValue storageSlot slotOffset diff --git a/test/libsolidity/SolidityEndToEndTest.cpp b/test/libsolidity/SolidityEndToEndTest.cpp index b58ebee45..07aa437ed 100644 --- a/test/libsolidity/SolidityEndToEndTest.cpp +++ b/test/libsolidity/SolidityEndToEndTest.cpp @@ -4884,6 +4884,48 @@ BOOST_AUTO_TEST_CASE(array_push) ABI_CHECK(callContractFunction("test()"), encodeArgs(5, 4, 3, 3)); } +BOOST_AUTO_TEST_CASE(array_push_struct) +{ + char const* sourceCode = R"( + contract c { + struct S { uint16 a; uint16 b; uint16[3] c; uint16[] d; } + S[] data; + function test() returns (uint16, uint16, uint16, uint16) { + S memory s; + s.a = 2; + s.b = 3; + s.c[2] = 4; + s.d = new uint16[](4); + s.d[2] = 5; + data.push(s); + return (data[0].a, data[0].b, data[0].c[2], data[0].d[2]); + } + } + )"; + compileAndRun(sourceCode); + ABI_CHECK(callContractFunction("test()"), encodeArgs(2, 3, 4, 5)); +} + +BOOST_AUTO_TEST_CASE(array_push_packed_array) +{ + char const* sourceCode = R"( + contract c { + uint80[] x; + function test() returns (uint80, uint80, uint80, uint80) { + x.push(1); + x.push(2); + x.push(3); + x.push(4); + x.push(5); + x.length = 4; + return (x[0], x[1], x[2], x[3]); + } + } + )"; + compileAndRun(sourceCode); + ABI_CHECK(callContractFunction("test()"), encodeArgs(1, 2, 3, 4)); +} + BOOST_AUTO_TEST_CASE(byte_array_push) { char const* sourceCode = R"( @@ -4904,6 +4946,29 @@ BOOST_AUTO_TEST_CASE(byte_array_push) ABI_CHECK(callContractFunction("test()"), encodeArgs(false)); } +BOOST_AUTO_TEST_CASE(byte_array_push_transition) +{ + // Tests transition between short and long encoding + char const* sourceCode = R"( + contract c { + bytes data; + function test() returns (uint) { + for (uint 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++) + if (data[i - 1] != byte(i)) return 0x1000000 + i; + return 0; + } + } + )"; + compileAndRun(sourceCode); + ABI_CHECK(callContractFunction("test()"), encodeArgs(0)); +} + BOOST_AUTO_TEST_CASE(external_array_args) { char const* sourceCode = R"( diff --git a/test/libsolidity/SolidityOptimizer.cpp b/test/libsolidity/SolidityOptimizer.cpp index a0df76f33..5326feaf0 100644 --- a/test/libsolidity/SolidityOptimizer.cpp +++ b/test/libsolidity/SolidityOptimizer.cpp @@ -627,8 +627,8 @@ BOOST_AUTO_TEST_CASE(optimise_multi_stores) )"; compileBothVersions(sourceCode); compareVersions("f()"); - BOOST_CHECK_EQUAL(numInstructions(m_nonOptimizedBytecode, Instruction::SSTORE), 13); - BOOST_CHECK_EQUAL(numInstructions(m_optimizedBytecode, Instruction::SSTORE), 11); + BOOST_CHECK_EQUAL(numInstructions(m_nonOptimizedBytecode, Instruction::SSTORE), 9); + BOOST_CHECK_EQUAL(numInstructions(m_optimizedBytecode, Instruction::SSTORE), 8); } BOOST_AUTO_TEST_SUITE_END() From 02ea0e547f3faa687f9c986ca9a0201b995846c0 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Wed, 4 Apr 2018 15:28:15 +0200 Subject: [PATCH 100/197] Replace comparison operators with opposites if preceded by SWAP1 --- Changelog.md | 1 + libevmasm/PeepholeOptimiser.cpp | 28 ++++++++++++++++++++- test/libevmasm/Optimiser.cpp | 43 +++++++++++++++++++++++++++++---- 3 files changed, 66 insertions(+), 6 deletions(-) diff --git a/Changelog.md b/Changelog.md index 9618dfa74..1d8542910 100644 --- a/Changelog.md +++ b/Changelog.md @@ -6,6 +6,7 @@ Features: * General: Support accessing dynamic return data in post-byzantium EVMs. * Interfaces: Allow overriding external functions in interfaces with public in an implementing contract. * Optimizer: Remove useless ``SWAP1`` instruction preceding a commutative instruction (such as ``ADD``, ``MUL``, etc). + * Optimizer: Replace comparison operators (``LT``, ``GT``, etc) with opposites if preceded by ``SWAP1``, e.g. ``SWAP1 LT`` is replaced with ``GT``. * Optimizer: Optimize across ``mload`` if ``msize()`` is not used. * Syntax Checker: Issue warning for empty structs (or error as experimental 0.5.0 feature). * General: Introduce new constructor syntax using the ``constructor`` keyword as experimental 0.5.0 feature. diff --git a/libevmasm/PeepholeOptimiser.cpp b/libevmasm/PeepholeOptimiser.cpp index 306465453..8a39de247 100644 --- a/libevmasm/PeepholeOptimiser.cpp +++ b/libevmasm/PeepholeOptimiser.cpp @@ -173,6 +173,32 @@ struct CommutativeSwap: SimplePeepholeOptimizerMethod } }; +struct SwapComparison: SimplePeepholeOptimizerMethod +{ + static bool applySimple(AssemblyItem const& _swap, AssemblyItem const& _op, std::back_insert_iterator _out) + { + map swappableOps{ + { Instruction::LT, Instruction::GT }, + { Instruction::GT, Instruction::LT }, + { Instruction::SLT, Instruction::SGT }, + { Instruction::SGT, Instruction::SLT } + }; + + if ( + _swap.type() == Operation && + _swap.instruction() == Instruction::SWAP1 && + _op.type() == Operation && + swappableOps.count(_op.instruction()) + ) + { + *_out = swappableOps.at(_op.instruction()); + return true; + } + else + return false; + } +}; + struct JumpToNext: SimplePeepholeOptimizerMethod { static size_t applySimple( @@ -279,7 +305,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(), JumpToNext(), UnreachableCode(), TagConjunctions(), Identity()); + applyMethods(state, PushPop(), OpPop(), DoublePush(), DoubleSwap(), CommutativeSwap(), SwapComparison(), JumpToNext(), UnreachableCode(), TagConjunctions(), 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/test/libevmasm/Optimiser.cpp b/test/libevmasm/Optimiser.cpp index b622b4fb9..089be45d4 100644 --- a/test/libevmasm/Optimiser.cpp +++ b/test/libevmasm/Optimiser.cpp @@ -888,7 +888,7 @@ BOOST_AUTO_TEST_CASE(peephole_commutative_swap1) PeepholeOptimiser peepOpt(items); BOOST_REQUIRE(peepOpt.optimise()); BOOST_CHECK_EQUAL_COLLECTIONS( - items.begin(), items.end(), + items.begin(), items.end(), expectation.begin(), expectation.end() ); } @@ -903,9 +903,7 @@ BOOST_AUTO_TEST_CASE(peephole_noncommutative_swap1) Instruction::SDIV, Instruction::MOD, Instruction::SMOD, - Instruction::EXP, - Instruction::LT, - Instruction::GT + Instruction::EXP }; for (Instruction const op: ops) { @@ -928,7 +926,42 @@ BOOST_AUTO_TEST_CASE(peephole_noncommutative_swap1) PeepholeOptimiser peepOpt(items); BOOST_REQUIRE(!peepOpt.optimise()); BOOST_CHECK_EQUAL_COLLECTIONS( - items.begin(), items.end(), + items.begin(), items.end(), + expectation.begin(), expectation.end() + ); + } +} + +BOOST_AUTO_TEST_CASE(peephole_swap_comparison) +{ + map swappableOps{ + { Instruction::LT, Instruction::GT }, + { Instruction::GT, Instruction::LT }, + { Instruction::SLT, Instruction::SGT }, + { Instruction::SGT, Instruction::SLT } + }; + + for (auto const& op: swappableOps) + { + AssemblyItems items{ + u256(1), + u256(2), + Instruction::SWAP1, + op.first, + u256(4), + u256(5) + }; + AssemblyItems expectation{ + u256(1), + u256(2), + op.second, + u256(4), + u256(5) + }; + PeepholeOptimiser peepOpt(items); + BOOST_REQUIRE(peepOpt.optimise()); + BOOST_CHECK_EQUAL_COLLECTIONS( + items.begin(), items.end(), expectation.begin(), expectation.end() ); } From d662622b25ab737887d0c002aec76fc3bbbaf909 Mon Sep 17 00:00:00 2001 From: Kevin Florenzano Date: Thu, 5 Apr 2018 17:12:48 +0900 Subject: [PATCH 101/197] Variable assignment wording change --- docs/control-structures.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/control-structures.rst b/docs/control-structures.rst index cdc2d20d4..40070a207 100644 --- a/docs/control-structures.rst +++ b/docs/control-structures.rst @@ -288,7 +288,7 @@ Solidity internally allows tuple types, i.e. a list of objects of potentially di uint x; bool b; uint y; - // These pre-existing variables can then be assigned to the tuple values + // Tuple values can be assigned to these pre-existing variables (x, b, y) = f(); // Common trick to swap values -- does not work for non-value storage types. (x, y) = (y, x); From 8dc9113e370a2edd42213c2c0cca811a3dbd8dd4 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Thu, 5 Apr 2018 10:41:43 +0200 Subject: [PATCH 102/197] Add end-to-end test for SwapComparison and CommutativeSwap peephole optimisers --- test/libsolidity/SolidityEndToEndTest.cpp | 44 +++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/test/libsolidity/SolidityEndToEndTest.cpp b/test/libsolidity/SolidityEndToEndTest.cpp index b58ebee45..4b4fc2afb 100644 --- a/test/libsolidity/SolidityEndToEndTest.cpp +++ b/test/libsolidity/SolidityEndToEndTest.cpp @@ -11032,6 +11032,50 @@ 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_SUITE_END() } From 96eff0ff6abc614cb44a01137dfd0df1ef750088 Mon Sep 17 00:00:00 2001 From: Daniel Kirchner Date: Wed, 4 Apr 2018 20:53:23 +0200 Subject: [PATCH 103/197] Error when using empty parenthesis for base class constructors that require arguments. --- Changelog.md | 1 + libsolidity/analysis/TypeChecker.cpp | 62 ++++++++++++------- libsolidity/ast/AST.h | 11 ++-- libsolidity/ast/ASTJsonConverter.cpp | 2 +- libsolidity/ast/AST_accept.h | 6 +- libsolidity/codegen/ContractCompiler.cpp | 4 +- libsolidity/parsing/Parser.cpp | 6 +- .../base_arguments_empty_parentheses.sol | 3 +- .../base_arguments_empty_parentheses_V050.sol | 9 +++ .../base_arguments_no_parentheses.sol | 5 ++ 10 files changed, 73 insertions(+), 36 deletions(-) create mode 100644 test/libsolidity/syntaxTests/inheritance/base_arguments_empty_parentheses_V050.sol create mode 100644 test/libsolidity/syntaxTests/inheritance/base_arguments_no_parentheses.sol diff --git a/Changelog.md b/Changelog.md index 9618dfa74..87e849c3b 100644 --- a/Changelog.md +++ b/Changelog.md @@ -9,6 +9,7 @@ Features: * Optimizer: Optimize across ``mload`` if ``msize()`` is not used. * Syntax Checker: Issue warning for empty structs (or error as experimental 0.5.0 feature). * General: Introduce new constructor syntax using the ``constructor`` keyword as experimental 0.5.0 feature. + * Inheritance: Error when using empty parenthesis for base class constructors that require arguments as experimental 0.5.0 feature. Bugfixes: * Code Generator: Allow ``block.blockhash`` without being called. diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index 620dfca48..a252742d9 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -320,7 +320,7 @@ void TypeChecker::checkContractAbstractConstructors(ContractDefinition const& _c { auto baseContract = dynamic_cast(&dereference(base->name())); solAssert(baseContract, ""); - if (!base->arguments().empty()) + if (base->arguments() && !base->arguments()->empty()) argumentsNeeded.erase(baseContract); } } @@ -506,30 +506,46 @@ void TypeChecker::endVisit(InheritanceSpecifier const& _inheritance) // Interfaces do not have constructors, so there are zero parameters. parameterTypes = ContractType(*base).newExpressionType()->parameterTypes(); - if (!arguments.empty() && parameterTypes.size() != arguments.size()) + if (arguments) { - m_errorReporter.typeError( - _inheritance.location(), - "Wrong argument count for constructor call: " + - toString(arguments.size()) + - " arguments given but expected " + - toString(parameterTypes.size()) + - "." - ); - return; - } + bool v050 = m_scope->sourceUnit().annotation().experimentalFeatures.count(ExperimentalFeature::V050); - for (size_t i = 0; i < arguments.size(); ++i) - if (!type(*arguments[i])->isImplicitlyConvertibleTo(*parameterTypes[i])) - m_errorReporter.typeError( - arguments[i]->location(), - "Invalid type for argument in constructor call. " - "Invalid implicit conversion from " + - type(*arguments[i])->toString() + - " to " + - parameterTypes[i]->toString() + - " requested." - ); + 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; + } + } + for (size_t i = 0; i < arguments->size(); ++i) + if (!type(*(*arguments)[i])->isImplicitlyConvertibleTo(*parameterTypes[i])) + m_errorReporter.typeError( + (*arguments)[i]->location(), + "Invalid type for argument in constructor call. " + "Invalid implicit conversion from " + + type(*(*arguments)[i])->toString() + + " to " + + parameterTypes[i]->toString() + + " requested." + ); + } } void TypeChecker::endVisit(UsingForDirective const& _usingFor) diff --git a/libsolidity/ast/AST.h b/libsolidity/ast/AST.h index 56bb412c9..bc85349b9 100644 --- a/libsolidity/ast/AST.h +++ b/libsolidity/ast/AST.h @@ -425,19 +425,22 @@ public: InheritanceSpecifier( SourceLocation const& _location, ASTPointer const& _baseName, - std::vector> _arguments + std::unique_ptr>> _arguments ): - ASTNode(_location), m_baseName(_baseName), m_arguments(_arguments) {} + ASTNode(_location), m_baseName(_baseName), m_arguments(std::move(_arguments)) {} virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTConstVisitor& _visitor) const override; UserDefinedTypeName const& name() const { return *m_baseName; } - std::vector> const& arguments() const { return m_arguments; } + // Returns nullptr if no argument list was given (``C``). + // If an argument list is given (``C(...)``), the arguments are returned + // as a vector of expressions. Note that this vector can be empty (``C()``). + std::vector> const* arguments() const { return m_arguments.get(); } private: ASTPointer m_baseName; - std::vector> m_arguments; + std::unique_ptr>> m_arguments; }; /** diff --git a/libsolidity/ast/ASTJsonConverter.cpp b/libsolidity/ast/ASTJsonConverter.cpp index 4fef67c3e..94932eca9 100644 --- a/libsolidity/ast/ASTJsonConverter.cpp +++ b/libsolidity/ast/ASTJsonConverter.cpp @@ -268,7 +268,7 @@ bool ASTJsonConverter::visit(InheritanceSpecifier const& _node) { setJsonNode(_node, "InheritanceSpecifier", { make_pair("baseName", toJson(_node.name())), - make_pair("arguments", toJson(_node.arguments())) + make_pair("arguments", _node.arguments() ? toJson(*_node.arguments()) : Json::Value(Json::arrayValue)) }); return false; } diff --git a/libsolidity/ast/AST_accept.h b/libsolidity/ast/AST_accept.h index 70ee997e1..dac414fc5 100644 --- a/libsolidity/ast/AST_accept.h +++ b/libsolidity/ast/AST_accept.h @@ -94,7 +94,8 @@ void InheritanceSpecifier::accept(ASTVisitor& _visitor) if (_visitor.visit(*this)) { m_baseName->accept(_visitor); - listAccept(m_arguments, _visitor); + if (m_arguments) + listAccept(*m_arguments, _visitor); } _visitor.endVisit(*this); } @@ -104,7 +105,8 @@ void InheritanceSpecifier::accept(ASTConstVisitor& _visitor) const if (_visitor.visit(*this)) { m_baseName->accept(_visitor); - listAccept(m_arguments, _visitor); + if (m_arguments) + listAccept(*m_arguments, _visitor); } _visitor.endVisit(*this); } diff --git a/libsolidity/codegen/ContractCompiler.cpp b/libsolidity/codegen/ContractCompiler.cpp index ebd9139a7..d3a7e4ead 100644 --- a/libsolidity/codegen/ContractCompiler.cpp +++ b/libsolidity/codegen/ContractCompiler.cpp @@ -157,8 +157,8 @@ void ContractCompiler::appendInitAndConstructorCode(ContractDefinition const& _c ); solAssert(baseContract, ""); - if (!m_baseArguments.count(baseContract->constructor()) && !base->arguments().empty()) - m_baseArguments[baseContract->constructor()] = &base->arguments(); + if (!m_baseArguments.count(baseContract->constructor()) && base->arguments() && !base->arguments()->empty()) + m_baseArguments[baseContract->constructor()] = base->arguments(); } } // Initialization of state variables in base-to-derived order. diff --git a/libsolidity/parsing/Parser.cpp b/libsolidity/parsing/Parser.cpp index 3dbd4c8f6..9a7731d84 100644 --- a/libsolidity/parsing/Parser.cpp +++ b/libsolidity/parsing/Parser.cpp @@ -286,17 +286,17 @@ ASTPointer Parser::parseInheritanceSpecifier() RecursionGuard recursionGuard(*this); ASTNodeFactory nodeFactory(*this); ASTPointer name(parseUserDefinedTypeName()); - vector> arguments; + unique_ptr>> arguments; if (m_scanner->currentToken() == Token::LParen) { m_scanner->next(); - arguments = parseFunctionCallListArguments(); + arguments.reset(new vector>(parseFunctionCallListArguments())); nodeFactory.markEndPosition(); expectToken(Token::RParen); } else nodeFactory.setEndPositionFromNode(name); - return nodeFactory.createNode(name, arguments); + return nodeFactory.createNode(name, std::move(arguments)); } Declaration::Visibility Parser::parseVisibilitySpecifier(Token::Value _token) diff --git a/test/libsolidity/syntaxTests/inheritance/base_arguments_empty_parentheses.sol b/test/libsolidity/syntaxTests/inheritance/base_arguments_empty_parentheses.sol index 9607ed60c..b3fbd04ac 100644 --- a/test/libsolidity/syntaxTests/inheritance/base_arguments_empty_parentheses.sol +++ b/test/libsolidity/syntaxTests/inheritance/base_arguments_empty_parentheses.sol @@ -3,4 +3,5 @@ contract Base { } contract Derived is Base(2) { } contract Derived2 is Base(), Derived() { } -contract Derived3 is Base, Derived {} +// ---- +// Warning: Wrong argument count for constructor call: 0 arguments given but expected 1. diff --git a/test/libsolidity/syntaxTests/inheritance/base_arguments_empty_parentheses_V050.sol b/test/libsolidity/syntaxTests/inheritance/base_arguments_empty_parentheses_V050.sol new file mode 100644 index 000000000..b37286347 --- /dev/null +++ b/test/libsolidity/syntaxTests/inheritance/base_arguments_empty_parentheses_V050.sol @@ -0,0 +1,9 @@ +pragma experimental "v0.5.0"; + +contract Base { + constructor(uint) public {} +} +contract Derived is Base(2) { } +contract Derived2 is Base(), Derived() { } +// ---- +// TypeError: Wrong argument count for constructor call: 0 arguments given but expected 1. diff --git a/test/libsolidity/syntaxTests/inheritance/base_arguments_no_parentheses.sol b/test/libsolidity/syntaxTests/inheritance/base_arguments_no_parentheses.sol new file mode 100644 index 000000000..24cca8f0b --- /dev/null +++ b/test/libsolidity/syntaxTests/inheritance/base_arguments_no_parentheses.sol @@ -0,0 +1,5 @@ +contract Base { + constructor(uint) public {} +} +contract Derived is Base(2) { } +contract Derived2 is Base, Derived {} From 7f232358bb6e40d96b83e732ecdad882a8ed972b Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Thu, 5 Apr 2018 11:59:31 +0200 Subject: [PATCH 104/197] Show JSON error if jsonParseStrict failed in soltest --- test/RPCSession.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/test/RPCSession.cpp b/test/RPCSession.cpp index 03b1341cb..c4a38f52e 100644 --- a/test/RPCSession.cpp +++ b/test/RPCSession.cpp @@ -337,7 +337,9 @@ Json::Value RPCSession::rpcCall(string const& _methodName, vector const& BOOST_TEST_MESSAGE("Reply: " + reply); Json::Value result; - BOOST_REQUIRE(jsonParseStrict(reply, result)); + string errorMsg; + if (!jsonParseStrict(reply, result, &errorMsg)) + BOOST_REQUIRE_MESSAGE(false, errorMsg); if (result.isMember("error")) { From ac6a30442f4096d1d3b0ca97b0f1d086c1056bcd Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Thu, 5 Apr 2018 11:55:46 +0200 Subject: [PATCH 105/197] Support constantinople in soltest --- test/RPCSession.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/RPCSession.cpp b/test/RPCSession.cpp index 03b1341cb..7d4b89241 100644 --- a/test/RPCSession.cpp +++ b/test/RPCSession.cpp @@ -226,6 +226,8 @@ void RPCSession::test_setChainParams(vector const& _accounts) forks += "\"EIP158ForkBlock\": \"0x00\",\n"; if (test::Options::get().evmVersion() >= solidity::EVMVersion::byzantium()) forks += "\"byzantiumForkBlock\": \"0x00\",\n"; + if (test::Options::get().evmVersion() >= solidity::EVMVersion::constantinople()) + forks += "\"constantinopleForkBlock\": \"0x00\",\n"; static string const c_configString = R"( { "sealEngine": "NoProof", From 8d087d1889826271a78ce537b8d1a2ceb11574dd Mon Sep 17 00:00:00 2001 From: Leonardo Alt Date: Thu, 5 Apr 2018 12:20:41 +0200 Subject: [PATCH 106/197] [SMTChecker] Removing usage of UFs to access SSA indices --- libsolidity/formal/SymbolicBoolVariable.cpp | 6 +++++- libsolidity/formal/SymbolicBoolVariable.h | 3 +++ libsolidity/formal/SymbolicIntVariable.cpp | 6 +++++- libsolidity/formal/SymbolicIntVariable.h | 3 +++ libsolidity/formal/SymbolicVariable.cpp | 4 ++-- libsolidity/formal/SymbolicVariable.h | 8 ++------ 6 files changed, 20 insertions(+), 10 deletions(-) diff --git a/libsolidity/formal/SymbolicBoolVariable.cpp b/libsolidity/formal/SymbolicBoolVariable.cpp index e5c56e461..5cf22d7db 100644 --- a/libsolidity/formal/SymbolicBoolVariable.cpp +++ b/libsolidity/formal/SymbolicBoolVariable.cpp @@ -30,7 +30,11 @@ SymbolicBoolVariable::SymbolicBoolVariable( SymbolicVariable(_decl, _interface) { solAssert(m_declaration.type()->category() == Type::Category::Bool, ""); - m_expression = make_shared(m_interface.newFunction(uniqueSymbol(), smt::Sort::Int, smt::Sort::Bool)); +} + +smt::Expression SymbolicBoolVariable::valueAtSequence(int _seq) const +{ + return m_interface.newBool(uniqueSymbol(_seq)); } void SymbolicBoolVariable::setZeroValue(int _seq) diff --git a/libsolidity/formal/SymbolicBoolVariable.h b/libsolidity/formal/SymbolicBoolVariable.h index 3510b7703..678f97d93 100644 --- a/libsolidity/formal/SymbolicBoolVariable.h +++ b/libsolidity/formal/SymbolicBoolVariable.h @@ -41,6 +41,9 @@ public: 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 index eb7b1c175..5e71fdcc5 100644 --- a/libsolidity/formal/SymbolicIntVariable.cpp +++ b/libsolidity/formal/SymbolicIntVariable.cpp @@ -30,7 +30,11 @@ SymbolicIntVariable::SymbolicIntVariable( SymbolicVariable(_decl, _interface) { solAssert(m_declaration.type()->category() == Type::Category::Integer, ""); - m_expression = make_shared(m_interface.newFunction(uniqueSymbol(), smt::Sort::Int, smt::Sort::Int)); +} + +smt::Expression SymbolicIntVariable::valueAtSequence(int _seq) const +{ + return m_interface.newInteger(uniqueSymbol(_seq)); } void SymbolicIntVariable::setZeroValue(int _seq) diff --git a/libsolidity/formal/SymbolicIntVariable.h b/libsolidity/formal/SymbolicIntVariable.h index eb36b8996..d591e8db6 100644 --- a/libsolidity/formal/SymbolicIntVariable.h +++ b/libsolidity/formal/SymbolicIntVariable.h @@ -44,6 +44,9 @@ public: 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/SymbolicVariable.cpp b/libsolidity/formal/SymbolicVariable.cpp index d59b55b10..caefa3a39 100644 --- a/libsolidity/formal/SymbolicVariable.cpp +++ b/libsolidity/formal/SymbolicVariable.cpp @@ -32,9 +32,9 @@ SymbolicVariable::SymbolicVariable( { } -string SymbolicVariable::uniqueSymbol() const +string SymbolicVariable::uniqueSymbol(int _seq) const { - return m_declaration.name() + "_" + to_string(m_declaration.id()); + return m_declaration.name() + "_" + to_string(m_declaration.id()) + "_" + to_string(_seq); } diff --git a/libsolidity/formal/SymbolicVariable.h b/libsolidity/formal/SymbolicVariable.h index 75eb9fa5c..e4e4ea8d4 100644 --- a/libsolidity/formal/SymbolicVariable.h +++ b/libsolidity/formal/SymbolicVariable.h @@ -46,7 +46,7 @@ public: return valueAtSequence(_seq); } - std::string uniqueSymbol() const; + std::string uniqueSymbol(int _seq) const; /// Sets the var to the default value of its type. virtual void setZeroValue(int _seq) = 0; @@ -55,13 +55,9 @@ public: virtual void setUnknownValue(int _seq) = 0; protected: - smt::Expression valueAtSequence(int _seq) const - { - return (*m_expression)(_seq); - } + virtual smt::Expression valueAtSequence(int _seq) const = 0; Declaration const& m_declaration; - std::shared_ptr m_expression = nullptr; smt::SolverInterface& m_interface; }; From 251e4cff587b2b02371545984e127b66fb5e3f50 Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 5 Apr 2018 11:36:16 +0200 Subject: [PATCH 107/197] Document use of AFL. --- docs/contributing.rst | 51 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/docs/contributing.rst b/docs/contributing.rst index 7c199d533..6717a8b92 100644 --- a/docs/contributing.rst +++ b/docs/contributing.rst @@ -170,6 +170,57 @@ and re-run the test. It will now pass again: 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. + +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 +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. + +We mainly use `AFL `_ for fuzzing. You need to download and +build AFL manually. Next, build Solidity (or just the ``solfuzzer`` binary) with AFL as your compiler: + +:: + + cd build + # if needed + make clean + 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 +to find errors. You can either copy some files from the syntax tests or extract test files +from the documentation or the other tests: + +:: + + mkdir /tmp/test_cases + cd /tmp/test_cases + # extract from tests: + path/to/solidity/scripts/isolate_tests.py path/to/solidity/test/libsolidity/SolidityEndToEndTest.cpp + # extract from documentation: + path/to/solidity/scripts/isolate_tests.py path/to/solidity/docs docs + +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 +that result in similar behaviour of the binary. + +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``. +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 ======== From 5b1c0506faf865d5ff056bec8094211f0fa90600 Mon Sep 17 00:00:00 2001 From: chriseth Date: Sat, 30 Dec 2017 13:45:39 +0100 Subject: [PATCH 108/197] Allow ``memory`` suffix for internal elementary type parsing. --- libsolidity/ast/Types.cpp | 15 +++++++++++++-- libsolidity/ast/Types.h | 1 + libsolidity/parsing/Token.cpp | 2 +- test/libsolidity/SolidityTypes.cpp | 1 + 4 files changed, 16 insertions(+), 3 deletions(-) diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp index ac1d3b01b..de359ec6f 100644 --- a/libsolidity/ast/Types.cpp +++ b/libsolidity/ast/Types.cpp @@ -233,11 +233,22 @@ 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); - return fromElementaryTypeName(ElementaryTypeNameToken(token, firstNum, secondNum)); + tie(token, firstNum, secondNum) = Token::fromIdentifierOrKeyword(name); + auto t = fromElementaryTypeName(ElementaryTypeNameToken(token, firstNum, secondNum)); + if (auto* ref = dynamic_cast(t.get())) + return ref->copyForLocation(location, true); + else + return t; } TypePointer Type::forLiteral(Literal const& _literal) diff --git a/libsolidity/ast/Types.h b/libsolidity/ast/Types.h index 2c392705d..aa46520f6 100644 --- a/libsolidity/ast/Types.h +++ b/libsolidity/ast/Types.h @@ -150,6 +150,7 @@ 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. static TypePointer fromElementaryTypeName(std::string const& _name); /// @} diff --git a/libsolidity/parsing/Token.cpp b/libsolidity/parsing/Token.cpp index 9cec0303a..5ce743161 100644 --- a/libsolidity/parsing/Token.cpp +++ b/libsolidity/parsing/Token.cpp @@ -53,7 +53,7 @@ namespace solidity void ElementaryTypeNameToken::assertDetails(Token::Value _baseType, unsigned const& _first, unsigned const& _second) { - solAssert(Token::isElementaryTypeName(_baseType), ""); + solAssert(Token::isElementaryTypeName(_baseType), "Expected elementary type name: " + string(Token::toString(_baseType))); if (_baseType == Token::BytesM) { solAssert(_second == 0, "There should not be a second size argument to type bytesM."); diff --git a/test/libsolidity/SolidityTypes.cpp b/test/libsolidity/SolidityTypes.cpp index bc9f2fe17..738b24bc1 100644 --- a/test/libsolidity/SolidityTypes.cpp +++ b/test/libsolidity/SolidityTypes.cpp @@ -123,6 +123,7 @@ BOOST_AUTO_TEST_CASE(type_identifiers) 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("string")->identifier(), "t_string_storage_ptr"); ArrayType largeintArray(DataLocation::Memory, Type::fromElementaryTypeName("int128"), u256("2535301200456458802993406410752")); BOOST_CHECK_EQUAL(largeintArray.identifier(), "t_array$_t_int128_$2535301200456458802993406410752_memory_ptr"); From 6a2c30e4ffacc4f0cdee321d76cf3f92f41bd25b Mon Sep 17 00:00:00 2001 From: chriseth Date: Wed, 4 Apr 2018 12:26:34 +0200 Subject: [PATCH 109/197] Fix trusty build script for eth. --- scripts/cpp-ethereum/eth_trusty.docker | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/scripts/cpp-ethereum/eth_trusty.docker b/scripts/cpp-ethereum/eth_trusty.docker index 0c4d7a02c..5cfb59f7e 100644 --- a/scripts/cpp-ethereum/eth_trusty.docker +++ b/scripts/cpp-ethereum/eth_trusty.docker @@ -1,7 +1,13 @@ FROM ubuntu:trusty -RUN apt update -RUN apt -y install libleveldb-dev cmake g++ git +RUN apt-get update +RUN apt-get -y install software-properties-common python-software-properties +RUN add-apt-repository ppa:ubuntu-toolchain-r/test +RUN apt-get update +RUN apt-get -y install gcc libleveldb-dev git curl make gcc-7 g++-7 +RUN ln -sf /usr/bin/gcc-7 /usr/bin/gcc +RUN ln -sf /usr/bin/g++-7 /usr/bin/g++ RUN git clone --recursive https://github.com/ethereum/cpp-ethereum --branch develop --single-branch --depth 1 -RUN mkdir /build && cd /build && cmake /cpp-ethereum -DCMAKE_BUILD_TYPE=RelWithDebInfo -DTOOLS=Off -DTESTS=Off +RUN ./cpp-ethereum/scripts/install_cmake.sh +RUN mkdir /build && cd /build && ~/.local/bin/cmake /cpp-ethereum -DCMAKE_BUILD_TYPE=RelWithDebInfo -DTOOLS=Off -DTESTS=Off RUN cd /build && make eth From 3c64313e91c6cb56918db0bab4e5432ef9a2940c Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 5 Apr 2018 19:00:32 +0200 Subject: [PATCH 110/197] Use new eth binaries. --- scripts/tests.sh | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/scripts/tests.sh b/scripts/tests.sh index 37ffafc28..2e40f8cb8 100755 --- a/scripts/tests.sh +++ b/scripts/tests.sh @@ -61,13 +61,13 @@ function download_eth() mkdir -p /tmp/test if grep -i trusty /etc/lsb-release >/dev/null 2>&1 then - # built from 1ecff3cac12f0fbbeea3e645f331d5ac026b24d3 at 2018-03-06 - ETH_BINARY=eth_byzantium_trusty - ETH_HASH="5432ea81c150e8a3547615bf597cd6dce9e1e27b" + # built from 5ac09111bd0b6518365fe956e1bdb97a2db82af1 at 2018-04-05 + ETH_BINARY=eth_2018-04-05_trusty + ETH_HASH="1e5e178b005e5b51f9d347df4452875ba9b53cc6" else - # built from ?? at 2018-02-13 ? - ETH_BINARY=eth_byzantium_artful - ETH_HASH="e527dd3e3dc17b983529dd7dcfb74a0d3a5aed4e" + # built from 5ac09111bd0b6518365fe956e1bdb97a2db82af1 at 2018-04-05 + ETH_BINARY=eth_2018-04-05_artful + ETH_HASH="eb2d0df022753bb2b442ba73e565a9babf6828d6" 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" From f39f36f2c7f38ecc8c171447de4c65c8cb968640 Mon Sep 17 00:00:00 2001 From: Sergiusz Bazanski Date: Tue, 3 Oct 2017 18:48:53 +0100 Subject: [PATCH 111/197] Fix file missing error message on imports. Trying to convert an import path into a Boost canonical path causes boost to throw an exception if the given file does not exist. Thus, instead of geting to the 'File not found' error, we instead got into the cath-all handler for 'Unknown exception in read callback'. This change rearranges the file checks to happen before we create a canonical Boost path. It also drive-by removes the unnecessary 'else' block, as the body of the if is a guard-like return block. --- solc/CommandLineInterface.cpp | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/solc/CommandLineInterface.cpp b/solc/CommandLineInterface.cpp index 8f81e7994..93203de60 100644 --- a/solc/CommandLineInterface.cpp +++ b/solc/CommandLineInterface.cpp @@ -700,7 +700,13 @@ bool CommandLineInterface::processInput() try { auto path = boost::filesystem::path(_path); + if (!boost::filesystem::exists(path)) + return ReadCallback::Result{false, "File not found."}; + auto canonicalPath = boost::filesystem::canonical(path); + if (!boost::filesystem::is_regular_file(canonicalPath)) + return ReadCallback::Result{false, "Not a valid file."}; + bool isAllowed = false; for (auto const& allowedDir: m_allowedDirectories) { @@ -716,16 +722,10 @@ bool CommandLineInterface::processInput() } if (!isAllowed) return ReadCallback::Result{false, "File outside of allowed directories."}; - else if (!boost::filesystem::exists(path)) - return ReadCallback::Result{false, "File not found."}; - else if (!boost::filesystem::is_regular_file(canonicalPath)) - return ReadCallback::Result{false, "Not a valid file."}; - else - { - auto contents = dev::readFileAsString(canonicalPath.string()); - m_sourceCodes[path.string()] = contents; - return ReadCallback::Result{true, contents}; - } + + auto contents = dev::readFileAsString(canonicalPath.string()); + m_sourceCodes[path.string()] = contents; + return ReadCallback::Result{true, contents}; } catch (Exception const& _exception) { From d5f40c141b203eb12c4d6fa97418b1a8f0f789bd Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Tue, 12 Dec 2017 08:54:33 +0000 Subject: [PATCH 112/197] Limit the number of errors output in a single run to 256 --- Changelog.md | 1 + libsolidity/interface/ErrorReporter.cpp | 14 + libsolidity/interface/ErrorReporter.h | 2 + .../more_than_256_declarationerrors.sol | 524 ++++++++++++++++++ .../more_than_256_syntaxerrors.sol | 524 ++++++++++++++++++ 5 files changed, 1065 insertions(+) create mode 100644 test/libsolidity/syntaxTests/more_than_256_declarationerrors.sol create mode 100644 test/libsolidity/syntaxTests/more_than_256_syntaxerrors.sol diff --git a/Changelog.md b/Changelog.md index d6860bdf9..53038a1a8 100644 --- a/Changelog.md +++ b/Changelog.md @@ -4,6 +4,7 @@ Features: * Code Generator: Initialize arrays without using ``msize()``. * Code Generator: More specialized and thus optimized implementation for ``x.push(...)`` * Commandline interface: Error when missing or inaccessible file detected. Suppress it with the ``--ignore-missing`` flag. + * General: Limit the number of errors output in a single run to 256. * General: Support accessing dynamic return data in post-byzantium EVMs. * Interfaces: Allow overriding external functions in interfaces with public in an implementing contract. * Optimizer: Remove useless ``SWAP1`` instruction preceding a commutative instruction (such as ``ADD``, ``MUL``, etc). diff --git a/libsolidity/interface/ErrorReporter.cpp b/libsolidity/interface/ErrorReporter.cpp index e6171756c..f7260d51d 100644 --- a/libsolidity/interface/ErrorReporter.cpp +++ b/libsolidity/interface/ErrorReporter.cpp @@ -61,6 +61,8 @@ void ErrorReporter::warning( void ErrorReporter::error(Error::Type _type, SourceLocation const& _location, string const& _description) { + abortIfExcessive(); + auto err = make_shared(_type); *err << errinfo_sourceLocation(_location) << @@ -71,6 +73,8 @@ void ErrorReporter::error(Error::Type _type, SourceLocation const& _location, st void ErrorReporter::error(Error::Type _type, SourceLocation const& _location, SecondarySourceLocation const& _secondaryLocation, string const& _description) { + abortIfExcessive(); + auto err = make_shared(_type); *err << errinfo_sourceLocation(_location) << @@ -80,6 +84,16 @@ void ErrorReporter::error(Error::Type _type, SourceLocation const& _location, Se m_errorList.push_back(err); } +void ErrorReporter::abortIfExcessive() +{ + if (m_errorList.size() > 256) + { + auto err = make_shared(Error::Type::Warning); + *err << errinfo_comment("There are more than 256 errors. Aborting."); + m_errorList.push_back(err); + BOOST_THROW_EXCEPTION(FatalError()); + } +} void ErrorReporter::fatalError(Error::Type _type, SourceLocation const& _location, string const& _description) { diff --git a/libsolidity/interface/ErrorReporter.h b/libsolidity/interface/ErrorReporter.h index a87db21d3..6b3dc221e 100644 --- a/libsolidity/interface/ErrorReporter.h +++ b/libsolidity/interface/ErrorReporter.h @@ -102,6 +102,8 @@ private: SourceLocation const& _location = SourceLocation(), std::string const& _description = std::string()); + void abortIfExcessive(); + ErrorList& m_errorList; }; diff --git a/test/libsolidity/syntaxTests/more_than_256_declarationerrors.sol b/test/libsolidity/syntaxTests/more_than_256_declarationerrors.sol new file mode 100644 index 000000000..363059775 --- /dev/null +++ b/test/libsolidity/syntaxTests/more_than_256_declarationerrors.sol @@ -0,0 +1,524 @@ +contract C { + function f() { + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + b = 5; + } +} +// ---- +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: Undeclared identifier. +// DeclarationError: 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 new file mode 100644 index 000000000..66e185fae --- /dev/null +++ b/test/libsolidity/syntaxTests/more_than_256_syntaxerrors.sol @@ -0,0 +1,524 @@ +contract C { + function f() { + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + continue; + } +} +// ---- +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// SyntaxError: "continue" has to be in a "for" or "while" loop. +// Warning: There are more than 256 errors. Aborting. From e8be0e61b395c61a836e018861cc3fdec10f6a8a Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Thu, 5 Apr 2018 14:23:36 +0200 Subject: [PATCH 113/197] Catch FatalError in CompilerStack::analysis to cover all the analysis tests --- libsolidity/analysis/TypeChecker.cpp | 12 +----------- libsolidity/interface/CompilerStack.cpp | 10 ++++++++++ 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index a252742d9..ce2771ca5 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -60,17 +60,7 @@ bool typeSupportedByOldABIEncoder(Type const& _type) bool TypeChecker::checkTypeRequirements(ASTNode const& _contract) { - try - { - _contract.accept(*this); - } - catch (FatalError const&) - { - // We got a fatal error which required to stop further type checking, but we can - // continue normally from here. - if (m_errorReporter.errors().empty()) - throw; // Something is weird here, rather throw again. - } + _contract.accept(*this); return Error::containsOnlyWarnings(m_errorReporter.errors()); } diff --git a/libsolidity/interface/CompilerStack.cpp b/libsolidity/interface/CompilerStack.cpp index eacfca9c8..e7cdb742b 100644 --- a/libsolidity/interface/CompilerStack.cpp +++ b/libsolidity/interface/CompilerStack.cpp @@ -164,6 +164,8 @@ bool CompilerStack::analyze() resolveImports(); bool noErrors = true; + + try { SyntaxChecker syntaxChecker(m_errorReporter); for (Source const* source: m_sourceOrder) if (!syntaxChecker.checkSyntax(*source->ast)) @@ -245,6 +247,14 @@ bool CompilerStack::analyze() smtChecker.analyze(*source->ast); } + } + catch(FatalError const&) + { + if (m_errorReporter.errors().empty()) + throw; // Something is weird here, rather throw again. + noErrors = false; + } + if (noErrors) { m_stackState = AnalysisSuccessful; From 3730f68d4b856ba66d2ccf117800c4441e204a99 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Thu, 5 Apr 2018 14:25:51 +0200 Subject: [PATCH 114/197] reindent --- libsolidity/interface/CompilerStack.cpp | 139 ++++++++++++------------ 1 file changed, 69 insertions(+), 70 deletions(-) diff --git a/libsolidity/interface/CompilerStack.cpp b/libsolidity/interface/CompilerStack.cpp index e7cdb742b..4ff14aa27 100644 --- a/libsolidity/interface/CompilerStack.cpp +++ b/libsolidity/interface/CompilerStack.cpp @@ -166,87 +166,86 @@ bool CompilerStack::analyze() bool noErrors = true; try { - SyntaxChecker syntaxChecker(m_errorReporter); - for (Source const* source: m_sourceOrder) - if (!syntaxChecker.checkSyntax(*source->ast)) - noErrors = false; + SyntaxChecker syntaxChecker(m_errorReporter); + for (Source const* source: m_sourceOrder) + if (!syntaxChecker.checkSyntax(*source->ast)) + noErrors = false; - DocStringAnalyser docStringAnalyser(m_errorReporter); - for (Source const* source: m_sourceOrder) - if (!docStringAnalyser.analyseDocStrings(*source->ast)) - noErrors = false; + DocStringAnalyser docStringAnalyser(m_errorReporter); + for (Source const* source: m_sourceOrder) + if (!docStringAnalyser.analyseDocStrings(*source->ast)) + noErrors = false; - m_globalContext = make_shared(); - NameAndTypeResolver resolver(m_globalContext->declarations(), m_scopes, m_errorReporter); - for (Source const* source: m_sourceOrder) - if (!resolver.registerDeclarations(*source->ast)) - return false; + m_globalContext = make_shared(); + NameAndTypeResolver resolver(m_globalContext->declarations(), m_scopes, m_errorReporter); + for (Source const* source: m_sourceOrder) + if (!resolver.registerDeclarations(*source->ast)) + return false; - map sourceUnitsByName; - for (auto& source: m_sources) - sourceUnitsByName[source.first] = source.second.ast.get(); - for (Source const* source: m_sourceOrder) - if (!resolver.performImports(*source->ast, sourceUnitsByName)) - return false; + map sourceUnitsByName; + for (auto& source: m_sources) + sourceUnitsByName[source.first] = source.second.ast.get(); + for (Source const* source: m_sourceOrder) + if (!resolver.performImports(*source->ast, sourceUnitsByName)) + return false; - for (Source const* source: m_sourceOrder) - for (ASTPointer const& node: source->ast->nodes()) - if (ContractDefinition* contract = dynamic_cast(node.get())) - { - m_globalContext->setCurrentContract(*contract); - if (!resolver.updateDeclaration(*m_globalContext->currentThis())) return false; - if (!resolver.updateDeclaration(*m_globalContext->currentSuper())) return false; - if (!resolver.resolveNamesAndTypes(*contract)) return false; + for (Source const* source: m_sourceOrder) + for (ASTPointer const& node: source->ast->nodes()) + if (ContractDefinition* contract = dynamic_cast(node.get())) + { + m_globalContext->setCurrentContract(*contract); + if (!resolver.updateDeclaration(*m_globalContext->currentThis())) return false; + if (!resolver.updateDeclaration(*m_globalContext->currentSuper())) return false; + if (!resolver.resolveNamesAndTypes(*contract)) return false; - // Note that we now reference contracts by their fully qualified names, and - // thus contracts can only conflict if declared in the same source file. This - // already causes a double-declaration error elsewhere, so we do not report - // an error here and instead silently drop any additional contracts we find. + // Note that we now reference contracts by their fully qualified names, and + // thus contracts can only conflict if declared in the same source file. This + // 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; - } + if (m_contracts.find(contract->fullyQualifiedName()) == m_contracts.end()) + m_contracts[contract->fullyQualifiedName()].contract = contract; + } - TypeChecker typeChecker(m_evmVersion, m_errorReporter); - for (Source const* source: m_sourceOrder) - for (ASTPointer const& node: source->ast->nodes()) - if (ContractDefinition* contract = dynamic_cast(node.get())) - if (!typeChecker.checkTypeRequirements(*contract)) + TypeChecker typeChecker(m_evmVersion, m_errorReporter); + for (Source const* source: m_sourceOrder) + for (ASTPointer const& node: source->ast->nodes()) + if (ContractDefinition* contract = dynamic_cast(node.get())) + if (!typeChecker.checkTypeRequirements(*contract)) + noErrors = false; + + if (noErrors) + { + PostTypeChecker postTypeChecker(m_errorReporter); + for (Source const* source: m_sourceOrder) + if (!postTypeChecker.check(*source->ast)) noErrors = false; + } - if (noErrors) - { - PostTypeChecker postTypeChecker(m_errorReporter); - for (Source const* source: m_sourceOrder) - if (!postTypeChecker.check(*source->ast)) + if (noErrors) + { + StaticAnalyzer staticAnalyzer(m_errorReporter); + for (Source const* source: m_sourceOrder) + if (!staticAnalyzer.analyze(*source->ast)) + noErrors = false; + } + + if (noErrors) + { + vector> ast; + for (Source const* source: m_sourceOrder) + ast.push_back(source->ast); + + if (!ViewPureChecker(ast, m_errorReporter).check()) noErrors = false; - } - - if (noErrors) - { - StaticAnalyzer staticAnalyzer(m_errorReporter); - for (Source const* source: m_sourceOrder) - if (!staticAnalyzer.analyze(*source->ast)) - noErrors = false; - } - - if (noErrors) - { - vector> ast; - for (Source const* source: m_sourceOrder) - ast.push_back(source->ast); - - if (!ViewPureChecker(ast, m_errorReporter).check()) - noErrors = false; - } - - if (noErrors) - { - SMTChecker smtChecker(m_errorReporter, m_smtQuery); - for (Source const* source: m_sourceOrder) - smtChecker.analyze(*source->ast); - } + } + if (noErrors) + { + SMTChecker smtChecker(m_errorReporter, m_smtQuery); + for (Source const* source: m_sourceOrder) + smtChecker.analyze(*source->ast); + } } catch(FatalError const&) { From 0812d1189ad3d6ba6338ef7dc39aa61005d731e4 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Thu, 5 Apr 2018 15:14:31 +0200 Subject: [PATCH 115/197] Ignore warnings when limited errors to 256 --- libsolidity/interface/ErrorReporter.cpp | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/libsolidity/interface/ErrorReporter.cpp b/libsolidity/interface/ErrorReporter.cpp index f7260d51d..436cb64fb 100644 --- a/libsolidity/interface/ErrorReporter.cpp +++ b/libsolidity/interface/ErrorReporter.cpp @@ -61,7 +61,8 @@ void ErrorReporter::warning( void ErrorReporter::error(Error::Type _type, SourceLocation const& _location, string const& _description) { - abortIfExcessive(); + if (_type != Error::Type::Warning) + abortIfExcessive(); auto err = make_shared(_type); *err << @@ -73,7 +74,8 @@ void ErrorReporter::error(Error::Type _type, SourceLocation const& _location, st void ErrorReporter::error(Error::Type _type, SourceLocation const& _location, SecondarySourceLocation const& _secondaryLocation, string const& _description) { - abortIfExcessive(); + if (_type != Error::Type::Warning) + abortIfExcessive(); auto err = make_shared(_type); *err << @@ -86,7 +88,12 @@ void ErrorReporter::error(Error::Type _type, SourceLocation const& _location, Se void ErrorReporter::abortIfExcessive() { - if (m_errorList.size() > 256) + unsigned errorCount = 0; + for (auto const& error: m_errorList) + if (error->type() != Error::Type::Warning) + errorCount++; + + if (errorCount > 256) { auto err = make_shared(Error::Type::Warning); *err << errinfo_comment("There are more than 256 errors. Aborting."); From 43d2954de83af5f64f526fd36f1cd5c3b5299498 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Thu, 5 Apr 2018 15:34:03 +0200 Subject: [PATCH 116/197] Do not abort excessive warnings, just ignore them. --- libsolidity/interface/ErrorReporter.cpp | 46 +++++++++++++++++-------- libsolidity/interface/ErrorReporter.h | 9 ++++- 2 files changed, 39 insertions(+), 16 deletions(-) diff --git a/libsolidity/interface/ErrorReporter.cpp b/libsolidity/interface/ErrorReporter.cpp index 436cb64fb..368e25e06 100644 --- a/libsolidity/interface/ErrorReporter.cpp +++ b/libsolidity/interface/ErrorReporter.cpp @@ -61,8 +61,8 @@ void ErrorReporter::warning( void ErrorReporter::error(Error::Type _type, SourceLocation const& _location, string const& _description) { - if (_type != Error::Type::Warning) - abortIfExcessive(); + if (checkForExcessiveErrors(_type)) + return; auto err = make_shared(_type); *err << @@ -74,8 +74,8 @@ void ErrorReporter::error(Error::Type _type, SourceLocation const& _location, st void ErrorReporter::error(Error::Type _type, SourceLocation const& _location, SecondarySourceLocation const& _secondaryLocation, string const& _description) { - if (_type != Error::Type::Warning) - abortIfExcessive(); + if (checkForExcessiveErrors(_type)) + return; auto err = make_shared(_type); *err << @@ -86,20 +86,36 @@ void ErrorReporter::error(Error::Type _type, SourceLocation const& _location, Se m_errorList.push_back(err); } -void ErrorReporter::abortIfExcessive() +bool ErrorReporter::checkForExcessiveErrors(Error::Type _type) { - unsigned errorCount = 0; - for (auto const& error: m_errorList) - if (error->type() != Error::Type::Warning) - errorCount++; - - if (errorCount > 256) + if (_type == Error::Type::Warning) { - auto err = make_shared(Error::Type::Warning); - *err << errinfo_comment("There are more than 256 errors. Aborting."); - m_errorList.push_back(err); - BOOST_THROW_EXCEPTION(FatalError()); + m_warningCount++; + + if (m_warningCount == c_maxWarningsAllowed) + { + auto err = make_shared(Error::Type::Warning); + *err << errinfo_comment("There are more than 256 warnings. Ignoring the rest."); + m_errorList.push_back(err); + } + + if (m_warningCount >= c_maxWarningsAllowed) + return true; } + else + { + m_errorCount++; + + if (m_errorCount > c_maxErrorsAllowed) + { + auto err = make_shared(Error::Type::Warning); + *err << errinfo_comment("There are more than 256 errors. Aborting."); + m_errorList.push_back(err); + BOOST_THROW_EXCEPTION(FatalError()); + } + } + + return false; } void ErrorReporter::fatalError(Error::Type _type, SourceLocation const& _location, string const& _description) diff --git a/libsolidity/interface/ErrorReporter.h b/libsolidity/interface/ErrorReporter.h index 6b3dc221e..d1a0030fa 100644 --- a/libsolidity/interface/ErrorReporter.h +++ b/libsolidity/interface/ErrorReporter.h @@ -102,9 +102,16 @@ private: SourceLocation const& _location = SourceLocation(), std::string const& _description = std::string()); - void abortIfExcessive(); + // @returns true if error shouldn't be stored + bool checkForExcessiveErrors(Error::Type _type); ErrorList& m_errorList; + + unsigned m_errorCount = 0; + unsigned m_warningCount = 0; + + const unsigned c_maxWarningsAllowed = 256; + const unsigned c_maxErrorsAllowed = 256; }; From a38418f0d9c9698c537c88152f53f6a65f99580c Mon Sep 17 00:00:00 2001 From: chriseth Date: Fri, 2 Feb 2018 15:24:56 +0100 Subject: [PATCH 117/197] Common subexpression eliminator. --- .../CommonSubexpressionEliminator.cpp | 48 +++++++++ .../optimiser/CommonSubexpressionEliminator.h | 45 ++++++++ test/libjulia/CommonSubexpression.cpp | 102 ++++++++++++++++++ 3 files changed, 195 insertions(+) create mode 100644 libjulia/optimiser/CommonSubexpressionEliminator.cpp create mode 100644 libjulia/optimiser/CommonSubexpressionEliminator.h create mode 100644 test/libjulia/CommonSubexpression.cpp diff --git a/libjulia/optimiser/CommonSubexpressionEliminator.cpp b/libjulia/optimiser/CommonSubexpressionEliminator.cpp new file mode 100644 index 000000000..229bd35eb --- /dev/null +++ b/libjulia/optimiser/CommonSubexpressionEliminator.cpp @@ -0,0 +1,48 @@ +/*( + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ +/** + * Optimisation stage that replaces expressions known to be the current value of a variable + * in scope by a reference to that variable. + */ + +#include + +#include +#include + +#include + +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) + { + solAssert(var.second, ""); + 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 new file mode 100644 index 000000000..a8ca3abba --- /dev/null +++ b/libjulia/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 . +*/ +/** + * 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 + +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/test/libjulia/CommonSubexpression.cpp b/test/libjulia/CommonSubexpression.cpp new file mode 100644 index 000000000..8a575c48b --- /dev/null +++ b/test/libjulia/CommonSubexpression.cpp @@ -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 . +*/ +/** + * Unit tests for the common subexpression eliminator optimizer stage. + */ + +#include + +#include + +#include + +#include + +#include +#include + +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() From 49567b3f4c216b3345cd5e6c2f2a9a3b27b54fb6 Mon Sep 17 00:00:00 2001 From: Robbie Ferguson Date: Tue, 27 Feb 2018 21:48:16 +1100 Subject: [PATCH 118/197] Update Delegatecall Description in Docs The previous description did not include the fact that the storage locations of the two contracts must align up until the storage variable(s) affected in order for the called contract to successfully write to the caller's storage. If they are misaligned, delegatecall will silently fail. This is difficult to debug without underlying knowledge of how delegatecall works, and clarity in the docs would certainly be helpful. --- docs/units-and-global-variables.rst | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/docs/units-and-global-variables.rst b/docs/units-and-global-variables.rst index 2571f20aa..e7f41ed15 100644 --- a/docs/units-and-global-variables.rst +++ b/docs/units-and-global-variables.rst @@ -168,6 +168,13 @@ For more information, see the section on :ref:`address`. 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. +.. note:: + If storage 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. From b2753aa05307d8142a319212c5fdd9a3c7f383fe Mon Sep 17 00:00:00 2001 From: Daniel Kirchner Date: Fri, 6 Apr 2018 18:10:26 +0200 Subject: [PATCH 119/197] Static Analyzer: Fix non-deterministic order of unused variable warnings. --- Changelog.md | 1 + libsolidity/analysis/StaticAnalyzer.cpp | 14 +++++++------- libsolidity/analysis/StaticAnalyzer.h | 4 +++- 3 files changed, 11 insertions(+), 8 deletions(-) diff --git a/Changelog.md b/Changelog.md index d6860bdf9..d1e199b7e 100644 --- a/Changelog.md +++ b/Changelog.md @@ -27,6 +27,7 @@ Bugfixes: * Type System: Improve error message when attempting to shift by a fractional amount. * Type System: Make external library functions accessible. * Type System: Prevent encoding of weird types. + * Static Analyzer: Fix non-deterministic order of unused variable warnings. ### 0.4.21 (2018-03-07) diff --git a/libsolidity/analysis/StaticAnalyzer.cpp b/libsolidity/analysis/StaticAnalyzer.cpp index d96f87484..33b0e2965 100644 --- a/libsolidity/analysis/StaticAnalyzer.cpp +++ b/libsolidity/analysis/StaticAnalyzer.cpp @@ -78,13 +78,13 @@ void StaticAnalyzer::endVisit(FunctionDefinition const&) for (auto const& var: m_localVarUseCount) if (var.second == 0) { - if (var.first->isCallableParameter()) + if (var.first.second->isCallableParameter()) m_errorReporter.warning( - var.first->location(), + var.first.second->location(), "Unused function parameter. Remove or comment out the variable name to silence this warning." ); else - m_errorReporter.warning(var.first->location(), "Unused local variable."); + m_errorReporter.warning(var.first.second->location(), "Unused local variable."); } m_localVarUseCount.clear(); @@ -97,7 +97,7 @@ bool StaticAnalyzer::visit(Identifier const& _identifier) { solAssert(!var->name().empty(), ""); if (var->isLocalVariable()) - m_localVarUseCount[var] += 1; + m_localVarUseCount[make_pair(var->id(), var)] += 1; } return true; } @@ -109,7 +109,7 @@ bool StaticAnalyzer::visit(VariableDeclaration const& _variable) solAssert(_variable.isLocalVariable(), ""); if (_variable.name() != "") // This is not a no-op, the entry might pre-exist. - m_localVarUseCount[&_variable] += 0; + m_localVarUseCount[make_pair(_variable.id(), &_variable)] += 0; } else if (_variable.isStateVariable()) { @@ -132,7 +132,7 @@ bool StaticAnalyzer::visit(Return const& _return) if (m_currentFunction && _return.expression()) for (auto const& var: m_currentFunction->returnParameters()) if (!var->name().empty()) - m_localVarUseCount[var.get()] += 1; + m_localVarUseCount[make_pair(var->id(), var.get())] += 1; return true; } @@ -224,7 +224,7 @@ bool StaticAnalyzer::visit(InlineAssembly const& _inlineAssembly) { solAssert(!var->name().empty(), ""); if (var->isLocalVariable()) - m_localVarUseCount[var] += 1; + m_localVarUseCount[make_pair(var->id(), var)] += 1; } } diff --git a/libsolidity/analysis/StaticAnalyzer.h b/libsolidity/analysis/StaticAnalyzer.h index 124c4e7ca..0a806bbdb 100644 --- a/libsolidity/analysis/StaticAnalyzer.h +++ b/libsolidity/analysis/StaticAnalyzer.h @@ -77,7 +77,9 @@ private: bool m_nonPayablePublic = false; /// Number of uses of each (named) local variable in a function, counter is initialized with zero. - std::map m_localVarUseCount; + /// Pairs of AST ids and pointers are used as keys to ensure a deterministic order + /// when traversing. + std::map, int> m_localVarUseCount; FunctionDefinition const* m_currentFunction = nullptr; From 4e037281acaf74c907f68e6227d6aa1b8847c78d Mon Sep 17 00:00:00 2001 From: Federico Bond Date: Mon, 11 Dec 2017 18:00:15 -0300 Subject: [PATCH 120/197] Error on duplicated super constructor calls --- Changelog.md | 1 + docs/contracts.rst | 14 +++++--- libsolidity/analysis/StaticAnalyzer.cpp | 32 +++++++++++++++++++ libsolidity/analysis/StaticAnalyzer.h | 1 + test/libsolidity/SolidityEndToEndTest.cpp | 8 ++--- .../duplicated_super_constructor_call.sol | 4 +++ ...uplicated_super_constructor_call_empty.sol | 4 +++ 7 files changed, 55 insertions(+), 9 deletions(-) create mode 100644 test/libsolidity/syntaxTests/inheritance/duplicated_super_constructor_call.sol create mode 100644 test/libsolidity/syntaxTests/inheritance/duplicated_super_constructor_call_empty.sol diff --git a/Changelog.md b/Changelog.md index d1e199b7e..94703f7dc 100644 --- a/Changelog.md +++ b/Changelog.md @@ -28,6 +28,7 @@ Bugfixes: * Type System: Make external library functions accessible. * Type System: Prevent encoding of weird types. * Static Analyzer: Fix non-deterministic order of unused variable warnings. + * Static Analyzer: Error on duplicated super constructor calls. ### 0.4.21 (2018-03-07) diff --git a/docs/contracts.rst b/docs/contracts.rst index f8a44fb3b..0dd9845c0 100644 --- a/docs/contracts.rst +++ b/docs/contracts.rst @@ -1034,9 +1034,12 @@ the base constructors. This can be done in two ways:: constructor(uint _x) public { x = _x; } } - contract Derived is Base(7) { - constructor(uint _y) Base(_y * _y) public { - } + contract Derived1 is Base(7) { + constructor(uint _y) public {} + } + + 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 @@ -1046,8 +1049,9 @@ 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. If, as in this silly example, both places -are used, the modifier-style argument takes precedence. +derived contract. Arguments have to be given either in the +inheritance list or in modifier-style in the derived constuctor. +Specifying arguments in both places is an error. .. index:: ! inheritance;multiple, ! linearization, ! C3 linearization diff --git a/libsolidity/analysis/StaticAnalyzer.cpp b/libsolidity/analysis/StaticAnalyzer.cpp index 33b0e2965..6c70ba6bf 100644 --- a/libsolidity/analysis/StaticAnalyzer.cpp +++ b/libsolidity/analysis/StaticAnalyzer.cpp @@ -90,6 +90,38 @@ void StaticAnalyzer::endVisit(FunctionDefinition const&) m_localVarUseCount.clear(); } +bool modifierOverridesInheritanceSpecifier( + ContractDefinition const* _contract, + ModifierInvocation const& _modifier, + InheritanceSpecifier const& _specifier +) +{ + auto parent = _specifier.name().annotation().referencedDeclaration; + return _contract == parent && (!_specifier.arguments().empty() || _modifier.arguments().empty()); +} + +bool StaticAnalyzer::visit(ModifierInvocation const& _modifier) +{ + if (!m_constructor) + return true; + + if (auto contract = dynamic_cast(_modifier.name()->annotation().referencedDeclaration)) + for (auto const& specifier: m_currentContract->baseContracts()) + if (modifierOverridesInheritanceSpecifier(contract, _modifier, *specifier)) + { + SecondarySourceLocation ssl; + ssl.append("Overriden constructor call is here:", specifier->location()); + + m_errorReporter.declarationError( + _modifier.location(), + ssl, + "Duplicated super constructor call." + ); + } + + return true; +} + bool StaticAnalyzer::visit(Identifier const& _identifier) { if (m_currentFunction) diff --git a/libsolidity/analysis/StaticAnalyzer.h b/libsolidity/analysis/StaticAnalyzer.h index 0a806bbdb..e68325bc4 100644 --- a/libsolidity/analysis/StaticAnalyzer.h +++ b/libsolidity/analysis/StaticAnalyzer.h @@ -57,6 +57,7 @@ private: virtual bool visit(FunctionDefinition const& _function) override; virtual void endVisit(FunctionDefinition const& _function) override; + virtual bool visit(ModifierInvocation const& _modifier) override; virtual bool visit(ExpressionStatement const& _statement) override; virtual bool visit(VariableDeclaration const& _variable) override; diff --git a/test/libsolidity/SolidityEndToEndTest.cpp b/test/libsolidity/SolidityEndToEndTest.cpp index beeae7860..5ed53a2f4 100644 --- a/test/libsolidity/SolidityEndToEndTest.cpp +++ b/test/libsolidity/SolidityEndToEndTest.cpp @@ -5191,7 +5191,7 @@ BOOST_AUTO_TEST_CASE(pass_dynamic_arguments_to_the_base) } uint public m_i; } - contract Derived is Base(2) { + contract Derived is Base { function Derived(uint i) Base(i) {} } @@ -5211,10 +5211,10 @@ BOOST_AUTO_TEST_CASE(pass_dynamic_arguments_to_the_base_base) } uint public m_i; } - contract Base1 is Base(3) { + contract Base1 is Base { function Base1(uint k) Base(k*k) {} } - contract Derived is Base(3), Base1(2) { + contract Derived is Base, Base1 { function Derived(uint i) Base(i) Base1(i) {} } @@ -5235,7 +5235,7 @@ BOOST_AUTO_TEST_CASE(pass_dynamic_arguments_to_the_base_base_with_gap) uint public m_i; } contract Base1 is Base(3) {} - contract Derived is Base(2), Base1 { + contract Derived is Base, Base1 { function Derived(uint i) Base(i) {} } contract Final is Derived(4) { diff --git a/test/libsolidity/syntaxTests/inheritance/duplicated_super_constructor_call.sol b/test/libsolidity/syntaxTests/inheritance/duplicated_super_constructor_call.sol new file mode 100644 index 000000000..95df10402 --- /dev/null +++ b/test/libsolidity/syntaxTests/inheritance/duplicated_super_constructor_call.sol @@ -0,0 +1,4 @@ +contract A { constructor(uint) public { } } +contract B is A(2) { constructor() A(3) public { } } +// ---- +// DeclarationError: Duplicated super constructor call. diff --git a/test/libsolidity/syntaxTests/inheritance/duplicated_super_constructor_call_empty.sol b/test/libsolidity/syntaxTests/inheritance/duplicated_super_constructor_call_empty.sol new file mode 100644 index 000000000..f8024ad6e --- /dev/null +++ b/test/libsolidity/syntaxTests/inheritance/duplicated_super_constructor_call_empty.sol @@ -0,0 +1,4 @@ +contract A { constructor() public { } } +contract B is A { constructor() A() public { } } +// ---- +// DeclarationError: Duplicated super constructor call. From b8fdb666e235bb6b19f11dba7740227026111598 Mon Sep 17 00:00:00 2001 From: Daniel Kirchner Date: Wed, 4 Apr 2018 12:28:22 +0200 Subject: [PATCH 121/197] Allow duplicated constructor calls, if no arguments; support for multiple inheritance; backwards compatibility. # tmp --- Changelog.md | 2 +- libsolidity/analysis/StaticAnalyzer.cpp | 52 +++++++++++-------- ...mpty_duplicated_super_constructor_call.sol | 3 ++ .../base_arguments_multiple_inheritance.sol | 9 ++++ .../duplicated_ancestor_constructor_call.sol | 5 ++ ...licated_ancestor_constructor_call_V050.sol | 7 +++ .../duplicated_super_constructor_call.sol | 2 +- ...duplicated_super_constructor_call_V050.sol | 6 +++ ...uplicated_super_constructor_call_empty.sol | 4 -- ...uplicated_super_constructor_call_multi.sol | 7 +++ 10 files changed, 70 insertions(+), 27 deletions(-) create mode 100644 test/libsolidity/syntaxTests/inheritance/allow_empty_duplicated_super_constructor_call.sol create mode 100644 test/libsolidity/syntaxTests/inheritance/base_arguments_multiple_inheritance.sol create mode 100644 test/libsolidity/syntaxTests/inheritance/duplicated_ancestor_constructor_call.sol create mode 100644 test/libsolidity/syntaxTests/inheritance/duplicated_ancestor_constructor_call_V050.sol create mode 100644 test/libsolidity/syntaxTests/inheritance/duplicated_super_constructor_call_V050.sol delete mode 100644 test/libsolidity/syntaxTests/inheritance/duplicated_super_constructor_call_empty.sol create mode 100644 test/libsolidity/syntaxTests/inheritance/duplicated_super_constructor_call_multi.sol diff --git a/Changelog.md b/Changelog.md index 94703f7dc..3b8cba1d3 100644 --- a/Changelog.md +++ b/Changelog.md @@ -9,6 +9,7 @@ Features: * Optimizer: Remove useless ``SWAP1`` instruction preceding a commutative instruction (such as ``ADD``, ``MUL``, etc). * Optimizer: Replace comparison operators (``LT``, ``GT``, etc) with opposites if preceded by ``SWAP1``, e.g. ``SWAP1 LT`` is replaced with ``GT``. * Optimizer: Optimize across ``mload`` if ``msize()`` is not used. + * Static Analyzer: Error on duplicated super constructor calls as experimental 0.5.0 feature. * Syntax Checker: Issue warning for empty structs (or error as experimental 0.5.0 feature). * General: Introduce new constructor syntax using the ``constructor`` keyword as experimental 0.5.0 feature. * Inheritance: Error when using empty parenthesis for base class constructors that require arguments as experimental 0.5.0 feature. @@ -28,7 +29,6 @@ Bugfixes: * Type System: Make external library functions accessible. * Type System: Prevent encoding of weird types. * Static Analyzer: Fix non-deterministic order of unused variable warnings. - * Static Analyzer: Error on duplicated super constructor calls. ### 0.4.21 (2018-03-07) diff --git a/libsolidity/analysis/StaticAnalyzer.cpp b/libsolidity/analysis/StaticAnalyzer.cpp index 6c70ba6bf..700c8a716 100644 --- a/libsolidity/analysis/StaticAnalyzer.cpp +++ b/libsolidity/analysis/StaticAnalyzer.cpp @@ -90,33 +90,43 @@ void StaticAnalyzer::endVisit(FunctionDefinition const&) m_localVarUseCount.clear(); } -bool modifierOverridesInheritanceSpecifier( - ContractDefinition const* _contract, - ModifierInvocation const& _modifier, - InheritanceSpecifier const& _specifier -) -{ - auto parent = _specifier.name().annotation().referencedDeclaration; - return _contract == parent && (!_specifier.arguments().empty() || _modifier.arguments().empty()); -} - bool StaticAnalyzer::visit(ModifierInvocation const& _modifier) { if (!m_constructor) return true; - if (auto contract = dynamic_cast(_modifier.name()->annotation().referencedDeclaration)) - for (auto const& specifier: m_currentContract->baseContracts()) - if (modifierOverridesInheritanceSpecifier(contract, _modifier, *specifier)) - { - SecondarySourceLocation ssl; - ssl.append("Overriden constructor call is here:", specifier->location()); + bool const v050 = m_currentContract->sourceUnit().annotation().experimentalFeatures.count(ExperimentalFeature::V050); - m_errorReporter.declarationError( - _modifier.location(), - ssl, - "Duplicated super constructor call." - ); + if (auto contract = dynamic_cast(_modifier.name()->annotation().referencedDeclaration)) + for (auto const& base: m_currentContract->annotation().linearizedBaseContracts) + for (auto const& specifier: base->baseContracts()) + { + Declaration const* parent = specifier->name().annotation().referencedDeclaration; + if (contract == parent && !specifier->arguments().empty()) + { + if (v050) + { + SecondarySourceLocation ssl; + ssl.append("Second constructor call is here:", specifier->location()); + + m_errorReporter.declarationError( + _modifier.location(), + ssl, + "Duplicated super constructor call." + ); + } + else + { + SecondarySourceLocation ssl; + ssl.append("Overridden constructor call is here:", specifier->location()); + + m_errorReporter.warning( + _modifier.location(), + "Duplicated super constructor calls are deprecated.", + ssl + ); + } + } } return true; diff --git a/test/libsolidity/syntaxTests/inheritance/allow_empty_duplicated_super_constructor_call.sol b/test/libsolidity/syntaxTests/inheritance/allow_empty_duplicated_super_constructor_call.sol new file mode 100644 index 000000000..1f580b1d4 --- /dev/null +++ b/test/libsolidity/syntaxTests/inheritance/allow_empty_duplicated_super_constructor_call.sol @@ -0,0 +1,3 @@ +contract A { constructor() public { } } +contract B1 is A { constructor() A() public { } } +contract B2 is A { constructor() A public { } } diff --git a/test/libsolidity/syntaxTests/inheritance/base_arguments_multiple_inheritance.sol b/test/libsolidity/syntaxTests/inheritance/base_arguments_multiple_inheritance.sol new file mode 100644 index 000000000..f63d0f027 --- /dev/null +++ b/test/libsolidity/syntaxTests/inheritance/base_arguments_multiple_inheritance.sol @@ -0,0 +1,9 @@ +contract Base { + constructor(uint) public { } +} +contract Base1 is Base(3) {} +contract Derived is Base, Base1 { + constructor(uint i) Base(i) public {} +} +// ---- +// Warning: Duplicated super constructor calls are deprecated. diff --git a/test/libsolidity/syntaxTests/inheritance/duplicated_ancestor_constructor_call.sol b/test/libsolidity/syntaxTests/inheritance/duplicated_ancestor_constructor_call.sol new file mode 100644 index 000000000..97f3f8ff2 --- /dev/null +++ b/test/libsolidity/syntaxTests/inheritance/duplicated_ancestor_constructor_call.sol @@ -0,0 +1,5 @@ +contract A { constructor(uint) public { } } +contract B is A(2) { constructor() public { } } +contract C is B { constructor() A(3) public { } } +// ---- +// Warning: Duplicated super constructor calls are deprecated. diff --git a/test/libsolidity/syntaxTests/inheritance/duplicated_ancestor_constructor_call_V050.sol b/test/libsolidity/syntaxTests/inheritance/duplicated_ancestor_constructor_call_V050.sol new file mode 100644 index 000000000..933c9087a --- /dev/null +++ b/test/libsolidity/syntaxTests/inheritance/duplicated_ancestor_constructor_call_V050.sol @@ -0,0 +1,7 @@ +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: Duplicated super constructor call. diff --git a/test/libsolidity/syntaxTests/inheritance/duplicated_super_constructor_call.sol b/test/libsolidity/syntaxTests/inheritance/duplicated_super_constructor_call.sol index 95df10402..876b07ea0 100644 --- a/test/libsolidity/syntaxTests/inheritance/duplicated_super_constructor_call.sol +++ b/test/libsolidity/syntaxTests/inheritance/duplicated_super_constructor_call.sol @@ -1,4 +1,4 @@ contract A { constructor(uint) public { } } contract B is A(2) { constructor() A(3) public { } } // ---- -// DeclarationError: Duplicated super constructor call. +// Warning: Duplicated super constructor calls are deprecated. diff --git a/test/libsolidity/syntaxTests/inheritance/duplicated_super_constructor_call_V050.sol b/test/libsolidity/syntaxTests/inheritance/duplicated_super_constructor_call_V050.sol new file mode 100644 index 000000000..31a363fd9 --- /dev/null +++ b/test/libsolidity/syntaxTests/inheritance/duplicated_super_constructor_call_V050.sol @@ -0,0 +1,6 @@ +pragma experimental "v0.5.0"; + +contract A { constructor(uint) public { } } +contract B is A(2) { constructor() A(3) public { } } +// ---- +// DeclarationError: Duplicated super constructor call. diff --git a/test/libsolidity/syntaxTests/inheritance/duplicated_super_constructor_call_empty.sol b/test/libsolidity/syntaxTests/inheritance/duplicated_super_constructor_call_empty.sol deleted file mode 100644 index f8024ad6e..000000000 --- a/test/libsolidity/syntaxTests/inheritance/duplicated_super_constructor_call_empty.sol +++ /dev/null @@ -1,4 +0,0 @@ -contract A { constructor() public { } } -contract B is A { constructor() A() public { } } -// ---- -// DeclarationError: Duplicated super constructor call. diff --git a/test/libsolidity/syntaxTests/inheritance/duplicated_super_constructor_call_multi.sol b/test/libsolidity/syntaxTests/inheritance/duplicated_super_constructor_call_multi.sol new file mode 100644 index 000000000..caed18eb7 --- /dev/null +++ b/test/libsolidity/syntaxTests/inheritance/duplicated_super_constructor_call_multi.sol @@ -0,0 +1,7 @@ +contract C { constructor(uint) public {} } +contract A is C(2) {} +contract B is C(2) {} +contract D is A, B { constructor() C(3) public {} } +// ---- +// Warning: Duplicated super constructor calls are deprecated. +// Warning: Duplicated super constructor calls are deprecated. From b918a105a40aa90fe9b89eecbcdfc7ac2937c141 Mon Sep 17 00:00:00 2001 From: Daniel Kirchner Date: Thu, 5 Apr 2018 16:25:20 +0200 Subject: [PATCH 122/197] Move constructor argument override check to TypeChecker and reuse annotations in ContractCompiler. --- libsolidity/analysis/StaticAnalyzer.cpp | 42 ----------- libsolidity/analysis/StaticAnalyzer.h | 1 - libsolidity/analysis/TypeChecker.cpp | 75 +++++++++++++------ libsolidity/analysis/TypeChecker.h | 7 +- libsolidity/ast/ASTAnnotations.h | 3 + libsolidity/codegen/ContractCompiler.cpp | 39 +++------- libsolidity/codegen/ContractCompiler.h | 2 +- .../base_arguments_multiple_inheritance.sol | 2 +- .../ancestor.sol} | 2 +- .../ancestor_V050.sol} | 2 +- .../base.sol} | 2 +- .../base_V050.sol} | 2 +- .../base_multi.sol} | 4 +- .../base_multi_no_constructor.sol | 6 ++ ...se_multi_no_constructor_modifier_style.sol | 6 ++ 15 files changed, 95 insertions(+), 100 deletions(-) rename test/libsolidity/syntaxTests/inheritance/{duplicated_ancestor_constructor_call.sol => duplicated_constructor_call/ancestor.sol} (70%) rename test/libsolidity/syntaxTests/inheritance/{duplicated_ancestor_constructor_call_V050.sol => duplicated_constructor_call/ancestor_V050.sol} (75%) rename test/libsolidity/syntaxTests/inheritance/{duplicated_super_constructor_call.sol => duplicated_constructor_call/base.sol} (62%) rename test/libsolidity/syntaxTests/inheritance/{duplicated_super_constructor_call_V050.sol => duplicated_constructor_call/base_V050.sol} (69%) rename test/libsolidity/syntaxTests/inheritance/{duplicated_super_constructor_call_multi.sol => duplicated_constructor_call/base_multi.sol} (53%) create mode 100644 test/libsolidity/syntaxTests/inheritance/duplicated_constructor_call/base_multi_no_constructor.sol create mode 100644 test/libsolidity/syntaxTests/inheritance/duplicated_constructor_call/base_multi_no_constructor_modifier_style.sol diff --git a/libsolidity/analysis/StaticAnalyzer.cpp b/libsolidity/analysis/StaticAnalyzer.cpp index 700c8a716..33b0e2965 100644 --- a/libsolidity/analysis/StaticAnalyzer.cpp +++ b/libsolidity/analysis/StaticAnalyzer.cpp @@ -90,48 +90,6 @@ void StaticAnalyzer::endVisit(FunctionDefinition const&) m_localVarUseCount.clear(); } -bool StaticAnalyzer::visit(ModifierInvocation const& _modifier) -{ - if (!m_constructor) - return true; - - bool const v050 = m_currentContract->sourceUnit().annotation().experimentalFeatures.count(ExperimentalFeature::V050); - - if (auto contract = dynamic_cast(_modifier.name()->annotation().referencedDeclaration)) - for (auto const& base: m_currentContract->annotation().linearizedBaseContracts) - for (auto const& specifier: base->baseContracts()) - { - Declaration const* parent = specifier->name().annotation().referencedDeclaration; - if (contract == parent && !specifier->arguments().empty()) - { - if (v050) - { - SecondarySourceLocation ssl; - ssl.append("Second constructor call is here:", specifier->location()); - - m_errorReporter.declarationError( - _modifier.location(), - ssl, - "Duplicated super constructor call." - ); - } - else - { - SecondarySourceLocation ssl; - ssl.append("Overridden constructor call is here:", specifier->location()); - - m_errorReporter.warning( - _modifier.location(), - "Duplicated super constructor calls are deprecated.", - ssl - ); - } - } - } - - return true; -} - bool StaticAnalyzer::visit(Identifier const& _identifier) { if (m_currentFunction) diff --git a/libsolidity/analysis/StaticAnalyzer.h b/libsolidity/analysis/StaticAnalyzer.h index e68325bc4..0a806bbdb 100644 --- a/libsolidity/analysis/StaticAnalyzer.h +++ b/libsolidity/analysis/StaticAnalyzer.h @@ -57,7 +57,6 @@ private: virtual bool visit(FunctionDefinition const& _function) override; virtual void endVisit(FunctionDefinition const& _function) override; - virtual bool visit(ModifierInvocation const& _modifier) override; virtual bool visit(ExpressionStatement const& _statement) override; virtual bool visit(VariableDeclaration const& _variable) override; diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index a252742d9..c6868a0a7 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -101,7 +101,7 @@ bool TypeChecker::visit(ContractDefinition const& _contract) checkContractDuplicateEvents(_contract); checkContractIllegalOverrides(_contract); checkContractAbstractFunctions(_contract); - checkContractAbstractConstructors(_contract); + checkContractBaseConstructorArguments(_contract); FunctionDefinition const* function = _contract.constructor(); if (function) @@ -291,42 +291,75 @@ void TypeChecker::checkContractAbstractFunctions(ContractDefinition const& _cont } } -void TypeChecker::checkContractAbstractConstructors(ContractDefinition const& _contract) +void TypeChecker::checkContractBaseConstructorArguments(ContractDefinition const& _contract) { - set argumentsNeeded; - // check that we get arguments for all base constructors that need it. - // If not mark the contract as abstract (not fully implemented) - vector const& bases = _contract.annotation().linearizedBaseContracts; - for (ContractDefinition const* contract: bases) - if (FunctionDefinition const* constructor = contract->constructor()) - if (contract != &_contract && !constructor->parameters().empty()) - argumentsNeeded.insert(contract); + // Determine the arguments that are used for the base constructors. for (ContractDefinition const* contract: bases) { if (FunctionDefinition const* constructor = contract->constructor()) for (auto const& modifier: constructor->modifiers()) { - auto baseContract = dynamic_cast( - &dereference(*modifier->name()) - ); - if (baseContract) - argumentsNeeded.erase(baseContract); + auto baseContract = dynamic_cast(&dereference(*modifier->name())); + if (baseContract && baseContract->constructor() && !modifier->arguments().empty()) + annotateBaseConstructorArguments(_contract, baseContract->constructor(), modifier.get()); } - for (ASTPointer const& base: contract->baseContracts()) { auto baseContract = dynamic_cast(&dereference(base->name())); solAssert(baseContract, ""); - if (base->arguments() && !base->arguments()->empty()) - argumentsNeeded.erase(baseContract); + + if (baseContract->constructor() && base->arguments() && !base->arguments()->empty()) + annotateBaseConstructorArguments(_contract, baseContract->constructor(), base.get()); } } - if (!argumentsNeeded.empty()) - for (ContractDefinition const* contract: argumentsNeeded) - _contract.annotation().unimplementedFunctions.push_back(contract->constructor()); + + // check that we get arguments for all base constructors that need it. + // If not mark the contract as abstract (not fully implemented) + for (ContractDefinition const* contract: bases) + if (FunctionDefinition const* constructor = contract->constructor()) + if (contract != &_contract && !constructor->parameters().empty()) + if (!_contract.annotation().baseConstructorArguments.count(constructor)) + _contract.annotation().unimplementedFunctions.push_back(constructor); +} + +void TypeChecker::annotateBaseConstructorArguments( + ContractDefinition const& _currentContract, + FunctionDefinition const* _baseConstructor, + ASTNode const* _argumentNode +) +{ + bool const v050 = _currentContract.sourceUnit().annotation().experimentalFeatures.count(ExperimentalFeature::V050); + + solAssert(_baseConstructor, ""); + solAssert(_argumentNode, ""); + + auto insertionResult = _currentContract.annotation().baseConstructorArguments.insert( + std::make_pair(_baseConstructor, _argumentNode) + ); + if (!insertionResult.second) + { + ASTNode const* previousNode = insertionResult.first->second; + + SecondarySourceLocation ssl; + ssl.append("Second constructor call is here:", _argumentNode->location()); + + if (v050) + m_errorReporter.declarationError( + previousNode->location(), + ssl, + "Base constructor arguments given twice." + ); + else + m_errorReporter.warning( + previousNode->location(), + "Base constructor arguments given twice.", + ssl + ); + } + } void TypeChecker::checkContractIllegalOverrides(ContractDefinition const& _contract) diff --git a/libsolidity/analysis/TypeChecker.h b/libsolidity/analysis/TypeChecker.h index 2ba312327..2245abd61 100644 --- a/libsolidity/analysis/TypeChecker.h +++ b/libsolidity/analysis/TypeChecker.h @@ -73,7 +73,12 @@ private: void checkFunctionOverride(FunctionDefinition const& function, FunctionDefinition const& super); void overrideError(FunctionDefinition const& function, FunctionDefinition const& super, std::string message); void checkContractAbstractFunctions(ContractDefinition const& _contract); - void checkContractAbstractConstructors(ContractDefinition const& _contract); + void checkContractBaseConstructorArguments(ContractDefinition const& _contract); + void annotateBaseConstructorArguments( + ContractDefinition const& _currentContract, + FunctionDefinition const* _baseConstructor, + ASTNode const* _argumentNode + ); /// Checks that different functions with external visibility end up having different /// external argument types (i.e. different signature). void checkContractExternalTypeClashes(ContractDefinition const& _contract); diff --git a/libsolidity/ast/ASTAnnotations.h b/libsolidity/ast/ASTAnnotations.h index 3d4236cce..5cbe42bde 100644 --- a/libsolidity/ast/ASTAnnotations.h +++ b/libsolidity/ast/ASTAnnotations.h @@ -90,6 +90,9 @@ struct ContractDefinitionAnnotation: TypeDeclarationAnnotation, DocumentedAnnota /// List of contracts this contract creates, i.e. which need to be compiled first. /// Also includes all contracts from @a linearizedBaseContracts. std::set contractDependencies; + /// Mapping containing the nodes that define the arguments for base constructors. + /// These can either be inheritance specifiers or modifier invocations. + std::map baseConstructorArguments; }; struct FunctionDefinitionAnnotation: ASTAnnotation, DocumentedAnnotation diff --git a/libsolidity/codegen/ContractCompiler.cpp b/libsolidity/codegen/ContractCompiler.cpp index d3a7e4ead..3ca0b69d6 100644 --- a/libsolidity/codegen/ContractCompiler.cpp +++ b/libsolidity/codegen/ContractCompiler.cpp @@ -135,34 +135,13 @@ void ContractCompiler::appendInitAndConstructorCode(ContractDefinition const& _c { solAssert(!_contract.isLibrary(), "Tried to initialize library."); CompilerContext::LocationSetter locationSetter(m_context, _contract); - // Determine the arguments that are used for the base constructors. - std::vector const& bases = _contract.annotation().linearizedBaseContracts; - for (ContractDefinition const* contract: bases) - { - if (FunctionDefinition const* constructor = contract->constructor()) - for (auto const& modifier: constructor->modifiers()) - { - auto baseContract = dynamic_cast( - modifier->name()->annotation().referencedDeclaration - ); - if (baseContract && !modifier->arguments().empty()) - if (m_baseArguments.count(baseContract->constructor()) == 0) - m_baseArguments[baseContract->constructor()] = &modifier->arguments(); - } - for (ASTPointer const& base: contract->baseContracts()) - { - ContractDefinition const* baseContract = dynamic_cast( - base->name().annotation().referencedDeclaration - ); - solAssert(baseContract, ""); + m_baseArguments = &_contract.annotation().baseConstructorArguments; - if (!m_baseArguments.count(baseContract->constructor()) && base->arguments() && !base->arguments()->empty()) - m_baseArguments[baseContract->constructor()] = base->arguments(); - } - } // Initialization of state variables in base-to-derived order. - for (ContractDefinition const* contract: boost::adaptors::reverse(bases)) + for (ContractDefinition const* contract: boost::adaptors::reverse( + _contract.annotation().linearizedBaseContracts + )) initializeStateVariables(*contract); if (FunctionDefinition const* constructor = _contract.constructor()) @@ -236,8 +215,14 @@ void ContractCompiler::appendBaseConstructor(FunctionDefinition const& _construc FunctionType constructorType(_constructor); if (!constructorType.parameterTypes().empty()) { - solAssert(m_baseArguments.count(&_constructor), ""); - std::vector> const* arguments = m_baseArguments[&_constructor]; + solAssert(m_baseArguments, ""); + solAssert(m_baseArguments->count(&_constructor), ""); + std::vector> const* arguments = nullptr; + ASTNode const* baseArgumentNode = m_baseArguments->at(&_constructor); + if (auto inheritanceSpecifier = dynamic_cast(baseArgumentNode)) + arguments = inheritanceSpecifier->arguments(); + else if (auto modifierInvocation = dynamic_cast(baseArgumentNode)) + arguments = &modifierInvocation->arguments(); solAssert(arguments, ""); solAssert(arguments->size() == constructorType.parameterTypes().size(), ""); for (unsigned i = 0; i < arguments->size(); ++i) diff --git a/libsolidity/codegen/ContractCompiler.h b/libsolidity/codegen/ContractCompiler.h index e04a56fb1..02a3452fd 100644 --- a/libsolidity/codegen/ContractCompiler.h +++ b/libsolidity/codegen/ContractCompiler.h @@ -135,7 +135,7 @@ private: 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> const*> m_baseArguments; + std::map const* m_baseArguments; }; } diff --git a/test/libsolidity/syntaxTests/inheritance/base_arguments_multiple_inheritance.sol b/test/libsolidity/syntaxTests/inheritance/base_arguments_multiple_inheritance.sol index f63d0f027..5483d5d7d 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: Duplicated super constructor calls are deprecated. +// Warning: Base constructor arguments given twice. diff --git a/test/libsolidity/syntaxTests/inheritance/duplicated_ancestor_constructor_call.sol b/test/libsolidity/syntaxTests/inheritance/duplicated_constructor_call/ancestor.sol similarity index 70% rename from test/libsolidity/syntaxTests/inheritance/duplicated_ancestor_constructor_call.sol rename to test/libsolidity/syntaxTests/inheritance/duplicated_constructor_call/ancestor.sol index 97f3f8ff2..8b1af2456 100644 --- a/test/libsolidity/syntaxTests/inheritance/duplicated_ancestor_constructor_call.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: Duplicated super constructor calls are deprecated. +// Warning: Base constructor arguments given twice. diff --git a/test/libsolidity/syntaxTests/inheritance/duplicated_ancestor_constructor_call_V050.sol b/test/libsolidity/syntaxTests/inheritance/duplicated_constructor_call/ancestor_V050.sol similarity index 75% rename from test/libsolidity/syntaxTests/inheritance/duplicated_ancestor_constructor_call_V050.sol rename to test/libsolidity/syntaxTests/inheritance/duplicated_constructor_call/ancestor_V050.sol index 933c9087a..6616c9a9f 100644 --- a/test/libsolidity/syntaxTests/inheritance/duplicated_ancestor_constructor_call_V050.sol +++ b/test/libsolidity/syntaxTests/inheritance/duplicated_constructor_call/ancestor_V050.sol @@ -4,4 +4,4 @@ contract A { constructor(uint) public { } } contract B is A(2) { constructor() public { } } contract C is B { constructor() A(3) public { } } // ---- -// DeclarationError: Duplicated super constructor call. +// DeclarationError: Base constructor arguments given twice. diff --git a/test/libsolidity/syntaxTests/inheritance/duplicated_super_constructor_call.sol b/test/libsolidity/syntaxTests/inheritance/duplicated_constructor_call/base.sol similarity index 62% rename from test/libsolidity/syntaxTests/inheritance/duplicated_super_constructor_call.sol rename to test/libsolidity/syntaxTests/inheritance/duplicated_constructor_call/base.sol index 876b07ea0..1fb504fe1 100644 --- a/test/libsolidity/syntaxTests/inheritance/duplicated_super_constructor_call.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: Duplicated super constructor calls are deprecated. +// Warning: Base constructor arguments given twice. diff --git a/test/libsolidity/syntaxTests/inheritance/duplicated_super_constructor_call_V050.sol b/test/libsolidity/syntaxTests/inheritance/duplicated_constructor_call/base_V050.sol similarity index 69% rename from test/libsolidity/syntaxTests/inheritance/duplicated_super_constructor_call_V050.sol rename to test/libsolidity/syntaxTests/inheritance/duplicated_constructor_call/base_V050.sol index 31a363fd9..96eb1bb12 100644 --- a/test/libsolidity/syntaxTests/inheritance/duplicated_super_constructor_call_V050.sol +++ b/test/libsolidity/syntaxTests/inheritance/duplicated_constructor_call/base_V050.sol @@ -3,4 +3,4 @@ pragma experimental "v0.5.0"; contract A { constructor(uint) public { } } contract B is A(2) { constructor() A(3) public { } } // ---- -// DeclarationError: Duplicated super constructor call. +// DeclarationError: Base constructor arguments given twice. diff --git a/test/libsolidity/syntaxTests/inheritance/duplicated_super_constructor_call_multi.sol b/test/libsolidity/syntaxTests/inheritance/duplicated_constructor_call/base_multi.sol similarity index 53% rename from test/libsolidity/syntaxTests/inheritance/duplicated_super_constructor_call_multi.sol rename to test/libsolidity/syntaxTests/inheritance/duplicated_constructor_call/base_multi.sol index caed18eb7..db9ffc85e 100644 --- a/test/libsolidity/syntaxTests/inheritance/duplicated_super_constructor_call_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: Duplicated super constructor calls are deprecated. -// Warning: Duplicated super constructor calls are deprecated. +// Warning: Base constructor arguments given twice. +// Warning: 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 new file mode 100644 index 000000000..fe280ad59 --- /dev/null +++ b/test/libsolidity/syntaxTests/inheritance/duplicated_constructor_call/base_multi_no_constructor.sol @@ -0,0 +1,6 @@ +contract C { constructor(uint) public {} } +contract A is C(2) {} +contract B is C(2) {} +contract D is A, B {} +// ---- +// Warning: 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 new file mode 100644 index 000000000..ea85aae77 --- /dev/null +++ b/test/libsolidity/syntaxTests/inheritance/duplicated_constructor_call/base_multi_no_constructor_modifier_style.sol @@ -0,0 +1,6 @@ +contract C { constructor(uint) public {} } +contract A is C { constructor() C(2) public {} } +contract B is C { constructor() C(2) public {} } +contract D is A, B { } +// ---- +// Warning: Base constructor arguments given twice. From 549ba801fb2a175cfb24e3c83b63030f47984e95 Mon Sep 17 00:00:00 2001 From: Daniel Kirchner Date: Mon, 9 Apr 2018 16:01:29 +0200 Subject: [PATCH 123/197] Use the most derived contract as main location in case of diamond inheritance. --- libsolidity/analysis/TypeChecker.cpp | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index c6868a0a7..1d8fd82de 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -343,18 +343,33 @@ void TypeChecker::annotateBaseConstructorArguments( { ASTNode const* previousNode = insertionResult.first->second; + SourceLocation const* mainLocation = nullptr; SecondarySourceLocation ssl; - ssl.append("Second constructor call is here:", _argumentNode->location()); + + if ( + _currentContract.location().contains(previousNode->location()) || + _currentContract.location().contains(_argumentNode->location()) + ) + { + mainLocation = &previousNode->location(); + ssl.append("Second constructor call is here:", _argumentNode->location()); + } + else + { + mainLocation = &_currentContract.location(); + ssl.append("First constructor call is here: ", _argumentNode->location()); + ssl.append("Second constructor call is here: ", previousNode->location()); + } if (v050) m_errorReporter.declarationError( - previousNode->location(), + *mainLocation, ssl, "Base constructor arguments given twice." ); else m_errorReporter.warning( - previousNode->location(), + *mainLocation, "Base constructor arguments given twice.", ssl ); From 089c295641bb640608b0dda5e62bf2ba574391c6 Mon Sep 17 00:00:00 2001 From: NetX Date: Thu, 5 Apr 2018 23:56:01 +0200 Subject: [PATCH 124/197] Update solidity-by-example.rst --- docs/solidity-by-example.rst | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/docs/solidity-by-example.rst b/docs/solidity-by-example.rst index 27fefd497..3636a3328 100644 --- a/docs/solidity-by-example.rst +++ b/docs/solidity-by-example.rst @@ -89,11 +89,10 @@ of votes. function giveRightToVote(address voter) public { // If the argument of `require` evaluates to `false`, // it terminates and reverts all changes to - // the state and to Ether balances. It is often - // a good idea to use this if functions are - // called incorrectly. But watch out, this - // will currently also consume all provided gas - // (this is planned to change in the future). + // the state and to Ether balances. + // This consumes all gas in old EVM versions, but not anymore. + // It is often a good idea to use this if functions are + // called incorrectly. require( (msg.sender == chairperson) && !voters[voter].voted && From cb352edd26f55adda63dde6cb66931089aff6656 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Wed, 10 May 2017 10:46:44 +0100 Subject: [PATCH 125/197] Add constant optimiser for SHR/SHL instructions --- Changelog.md | 1 + libevmasm/RuleList.h | 10 ++++++++++ 2 files changed, 11 insertions(+) diff --git a/Changelog.md b/Changelog.md index d1e199b7e..1ed2f1ddb 100644 --- a/Changelog.md +++ b/Changelog.md @@ -6,6 +6,7 @@ Features: * Commandline interface: Error when missing or inaccessible file detected. Suppress it with the ``--ignore-missing`` flag. * General: Support accessing dynamic return data in post-byzantium EVMs. * Interfaces: Allow overriding external functions in interfaces with public in an implementing contract. + * Optimizer: Optimize ``SHL`` and ``SHR`` only involving constants (Constantinople only). * Optimizer: Remove useless ``SWAP1`` instruction preceding a commutative instruction (such as ``ADD``, ``MUL``, etc). * Optimizer: Replace comparison operators (``LT``, ``GT``, etc) with opposites if preceded by ``SWAP1``, e.g. ``SWAP1 LT`` is replaced with ``GT``. * Optimizer: Optimize across ``mload`` if ``msize()`` is not used. diff --git a/libevmasm/RuleList.h b/libevmasm/RuleList.h index da522cec8..abcf170c6 100644 --- a/libevmasm/RuleList.h +++ b/libevmasm/RuleList.h @@ -89,6 +89,16 @@ std::vector> simplificationRuleList( u256 mask = (u256(1) << testBit) - 1; return u256(boost::multiprecision::bit_test(B.d(), testBit) ? B.d() | ~mask : B.d() & mask); }, false}, + {{Instruction::SHL, {A, B}}, [=]{ + if (A.d() > 255) + return u256(0); + return u256(bigint(B.d()) << unsigned(A.d())); + }, false}, + {{Instruction::SHR, {A, B}}, [=]{ + if (A.d() > 255) + return u256(0); + return B.d() >> unsigned(A.d()); + }, false}, // invariants involving known constants {{Instruction::ADD, {X, 0}}, [=]{ return X; }, false}, From aaa8edc36dbddf08cd2326d6e6ce30f9d537c354 Mon Sep 17 00:00:00 2001 From: hydai Date: Tue, 10 Apr 2018 13:41:35 +0800 Subject: [PATCH 126/197] Fixed typos --- libsolidity/ast/Types.h | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/libsolidity/ast/Types.h b/libsolidity/ast/Types.h index aa46520f6..05f506f1c 100644 --- a/libsolidity/ast/Types.h +++ b/libsolidity/ast/Types.h @@ -403,7 +403,7 @@ private: }; /** - * Integer and fixed point constants either literals or computed. + * Integer and fixed point constants either literals or computed. * Example expressions: 2, 3.14, 2+10.2, ~10. * There is one distinct type per value. */ @@ -415,7 +415,7 @@ public: /// @returns true if the literal is a valid integer. static std::tuple isValidLiteral(Literal const& _literal); - + explicit RationalNumberType(rational const& _value): m_value(_value) {} @@ -436,7 +436,7 @@ public: /// @returns the smallest integer type that can hold the value or an empty pointer if not possible. std::shared_ptr 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. /// If the integer part does not fit, returns an empty pointer. std::shared_ptr fixedPointType() const; @@ -778,7 +778,7 @@ public: virtual std::string canonicalName() const override; virtual std::string signatureInExternalFunction(bool _structsByName) const override; - /// @returns a function that peforms the type conversion between a list of struct members + /// @returns a function that performs the type conversion between a list of struct members /// and a memory struct of this type. FunctionTypePointer constructorType() const; @@ -1039,7 +1039,7 @@ public: return *m_declaration; } bool hasDeclaration() const { return !!m_declaration; } - /// @returns true if the the result of this function only depends on its arguments + /// @returns true if the result of this function only depends on its arguments /// and it does not modify the state. /// Currently, this will only return true for internal functions like keccak and ecrecover. bool isPure() const; @@ -1056,7 +1056,7 @@ public: bool bound() const { return m_bound; } /// @returns a copy of this type, where gas or value are set manually. This will never set one - /// of the parameters to fals. + /// of the parameters to false. TypePointer copyAndSetGasOrValue(bool _setGas, bool _setValue) const; /// @returns a copy of this function type where all return parameters of dynamic size are From 3eedbc6a9c60888dd967d6673a34511947da4aa1 Mon Sep 17 00:00:00 2001 From: Daniel Kirchner Date: Tue, 10 Apr 2018 11:22:26 +0200 Subject: [PATCH 127/197] Error when using no parentheses in modifier-style constructor calls. --- Changelog.md | 3 ++- libsolidity/analysis/TypeChecker.cpp | 27 ++++++++++++++++--- libsolidity/ast/AST.h | 11 +++++--- libsolidity/ast/ASTJsonConverter.cpp | 4 +-- libsolidity/ast/AST_accept.h | 6 +++-- libsolidity/codegen/ContractCompiler.cpp | 9 ++++--- libsolidity/parsing/Parser.cpp | 6 ++--- ...mpty_duplicated_super_constructor_call.sol | 3 +-- ...low_modifier_style_without_parentheses.sol | 4 +++ 9 files changed, 52 insertions(+), 21 deletions(-) create mode 100644 test/libsolidity/syntaxTests/inheritance/disallow_modifier_style_without_parentheses.sol diff --git a/Changelog.md b/Changelog.md index 3b8cba1d3..eb35652f5 100644 --- a/Changelog.md +++ b/Changelog.md @@ -12,7 +12,8 @@ Features: * Static Analyzer: Error on duplicated super constructor calls as experimental 0.5.0 feature. * Syntax Checker: Issue warning for empty structs (or error as experimental 0.5.0 feature). * General: Introduce new constructor syntax using the ``constructor`` keyword as experimental 0.5.0 feature. - * Inheritance: Error when using empty parenthesis for base class constructors that require arguments as experimental 0.5.0 feature. + * 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. Bugfixes: * Code Generator: Allow ``block.blockhash`` without being called. diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index 1d8fd82de..abe57778a 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -293,6 +293,8 @@ void TypeChecker::checkContractAbstractFunctions(ContractDefinition const& _cont void TypeChecker::checkContractBaseConstructorArguments(ContractDefinition const& _contract) { + bool const v050 = _contract.sourceUnit().annotation().experimentalFeatures.count(ExperimentalFeature::V050); + vector const& bases = _contract.annotation().linearizedBaseContracts; // Determine the arguments that are used for the base constructors. @@ -302,8 +304,24 @@ void TypeChecker::checkContractBaseConstructorArguments(ContractDefinition const for (auto const& modifier: constructor->modifiers()) { auto baseContract = dynamic_cast(&dereference(*modifier->name())); - if (baseContract && baseContract->constructor() && !modifier->arguments().empty()) - annotateBaseConstructorArguments(_contract, baseContract->constructor(), modifier.get()); + if (modifier->arguments()) + { + 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." + ); + else + m_errorReporter.warning( + modifier->location(), + "Modifier-style base constructor call without arguments." + ); + } } for (ASTPointer const& base: contract->baseContracts()) @@ -804,7 +822,8 @@ void TypeChecker::visitManually( vector const& _bases ) { - std::vector> const& arguments = _modifier.arguments(); + std::vector> const& arguments = + _modifier.arguments() ? *_modifier.arguments() : std::vector>(); for (ASTPointer const& argument: arguments) argument->accept(*this); _modifier.name()->accept(*this); @@ -842,7 +861,7 @@ void TypeChecker::visitManually( ); return; } - for (size_t i = 0; i < _modifier.arguments().size(); ++i) + for (size_t i = 0; i < arguments.size(); ++i) if (!type(*arguments[i])->isImplicitlyConvertibleTo(*type(*(*parameters)[i]))) m_errorReporter.typeError( arguments[i]->location(), diff --git a/libsolidity/ast/AST.h b/libsolidity/ast/AST.h index bc85349b9..ae253f0c8 100644 --- a/libsolidity/ast/AST.h +++ b/libsolidity/ast/AST.h @@ -762,19 +762,22 @@ public: ModifierInvocation( SourceLocation const& _location, ASTPointer const& _name, - std::vector> _arguments + std::unique_ptr>> _arguments ): - ASTNode(_location), m_modifierName(_name), m_arguments(_arguments) {} + ASTNode(_location), m_modifierName(_name), m_arguments(std::move(_arguments)) {} virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTConstVisitor& _visitor) const override; ASTPointer const& name() const { return m_modifierName; } - std::vector> const& arguments() const { return m_arguments; } + // Returns nullptr if no argument list was given (``mod``). + // If an argument list is given (``mod(...)``), the arguments are returned + // as a vector of expressions. Note that this vector can be empty (``mod()``). + std::vector> const* arguments() const { return m_arguments.get(); } private: ASTPointer m_modifierName; - std::vector> m_arguments; + std::unique_ptr>> m_arguments; }; /** diff --git a/libsolidity/ast/ASTJsonConverter.cpp b/libsolidity/ast/ASTJsonConverter.cpp index 94932eca9..95ba30893 100644 --- a/libsolidity/ast/ASTJsonConverter.cpp +++ b/libsolidity/ast/ASTJsonConverter.cpp @@ -268,7 +268,7 @@ bool ASTJsonConverter::visit(InheritanceSpecifier const& _node) { setJsonNode(_node, "InheritanceSpecifier", { make_pair("baseName", toJson(_node.name())), - make_pair("arguments", _node.arguments() ? toJson(*_node.arguments()) : Json::Value(Json::arrayValue)) + make_pair("arguments", _node.arguments() ? toJson(*_node.arguments()) : Json::nullValue) }); return false; } @@ -378,7 +378,7 @@ bool ASTJsonConverter::visit(ModifierInvocation const& _node) { setJsonNode(_node, "ModifierInvocation", { make_pair("modifierName", toJson(*_node.name())), - make_pair("arguments", toJson(_node.arguments())) + make_pair("arguments", _node.arguments() ? toJson(*_node.arguments()) : Json::nullValue) }); return false; } diff --git a/libsolidity/ast/AST_accept.h b/libsolidity/ast/AST_accept.h index dac414fc5..aeff6e4a9 100644 --- a/libsolidity/ast/AST_accept.h +++ b/libsolidity/ast/AST_accept.h @@ -264,7 +264,8 @@ void ModifierInvocation::accept(ASTVisitor& _visitor) if (_visitor.visit(*this)) { m_modifierName->accept(_visitor); - listAccept(m_arguments, _visitor); + if (m_arguments) + listAccept(*m_arguments, _visitor); } _visitor.endVisit(*this); } @@ -274,7 +275,8 @@ void ModifierInvocation::accept(ASTConstVisitor& _visitor) const if (_visitor.visit(*this)) { m_modifierName->accept(_visitor); - listAccept(m_arguments, _visitor); + if (m_arguments) + listAccept(*m_arguments, _visitor); } _visitor.endVisit(*this); } diff --git a/libsolidity/codegen/ContractCompiler.cpp b/libsolidity/codegen/ContractCompiler.cpp index 3ca0b69d6..5cb371035 100644 --- a/libsolidity/codegen/ContractCompiler.cpp +++ b/libsolidity/codegen/ContractCompiler.cpp @@ -222,7 +222,7 @@ void ContractCompiler::appendBaseConstructor(FunctionDefinition const& _construc if (auto inheritanceSpecifier = dynamic_cast(baseArgumentNode)) arguments = inheritanceSpecifier->arguments(); else if (auto modifierInvocation = dynamic_cast(baseArgumentNode)) - arguments = &modifierInvocation->arguments(); + arguments = modifierInvocation->arguments(); solAssert(arguments, ""); solAssert(arguments->size() == constructorType.parameterTypes().size(), ""); for (unsigned i = 0; i < arguments->size(); ++i) @@ -897,13 +897,16 @@ void ContractCompiler::appendModifierOrFunctionCode() ); ModifierDefinition const& modifier = m_context.resolveVirtualFunctionModifier(nonVirtualModifier); CompilerContext::LocationSetter locationSetter(m_context, modifier); - solAssert(modifier.parameters().size() == modifierInvocation->arguments().size(), ""); + std::vector> const& modifierArguments = + modifierInvocation->arguments() ? *modifierInvocation->arguments() : std::vector>(); + + solAssert(modifier.parameters().size() == modifierArguments.size(), ""); for (unsigned i = 0; i < modifier.parameters().size(); ++i) { m_context.addVariable(*modifier.parameters()[i]); addedVariables.push_back(modifier.parameters()[i].get()); compileExpression( - *modifierInvocation->arguments()[i], + *modifierArguments[i], modifier.parameters()[i]->annotation().type ); } diff --git a/libsolidity/parsing/Parser.cpp b/libsolidity/parsing/Parser.cpp index 9a7731d84..18ef740a3 100644 --- a/libsolidity/parsing/Parser.cpp +++ b/libsolidity/parsing/Parser.cpp @@ -701,17 +701,17 @@ ASTPointer Parser::parseModifierInvocation() RecursionGuard recursionGuard(*this); ASTNodeFactory nodeFactory(*this); ASTPointer name(parseIdentifier()); - vector> arguments; + unique_ptr>> arguments; if (m_scanner->currentToken() == Token::LParen) { m_scanner->next(); - arguments = parseFunctionCallListArguments(); + arguments.reset(new vector>(parseFunctionCallListArguments())); nodeFactory.markEndPosition(); expectToken(Token::RParen); } else nodeFactory.setEndPositionFromNode(name); - return nodeFactory.createNode(name, arguments); + return nodeFactory.createNode(name, move(arguments)); } ASTPointer Parser::parseIdentifier() diff --git a/test/libsolidity/syntaxTests/inheritance/allow_empty_duplicated_super_constructor_call.sol b/test/libsolidity/syntaxTests/inheritance/allow_empty_duplicated_super_constructor_call.sol index 1f580b1d4..ce9d5f5fc 100644 --- a/test/libsolidity/syntaxTests/inheritance/allow_empty_duplicated_super_constructor_call.sol +++ b/test/libsolidity/syntaxTests/inheritance/allow_empty_duplicated_super_constructor_call.sol @@ -1,3 +1,2 @@ contract A { constructor() public { } } -contract B1 is A { constructor() A() public { } } -contract B2 is A { constructor() A public { } } +contract B is A { constructor() A() public { } } diff --git a/test/libsolidity/syntaxTests/inheritance/disallow_modifier_style_without_parentheses.sol b/test/libsolidity/syntaxTests/inheritance/disallow_modifier_style_without_parentheses.sol new file mode 100644 index 000000000..0c159a228 --- /dev/null +++ b/test/libsolidity/syntaxTests/inheritance/disallow_modifier_style_without_parentheses.sol @@ -0,0 +1,4 @@ +contract A { constructor() public { } } +contract B is A { constructor() A public { } } +// ---- +// Warning: Modifier-style base constructor call without arguments. From f03695731b9b36cd4014620b7b63556a69b4e952 Mon Sep 17 00:00:00 2001 From: Daniel Kirchner Date: Fri, 6 Apr 2018 18:37:01 +0200 Subject: [PATCH 128/197] Add source locations to syntax test expectations. --- Changelog.md | 1 + test/libsolidity/FormattedScope.h | 2 + test/libsolidity/SyntaxTest.cpp | 88 ++++++++++++++++--- test/libsolidity/SyntaxTest.h | 7 +- .../constants/cyclic_dependency_1.sol | 2 +- .../constants/cyclic_dependency_2.sol | 6 +- .../constants/cyclic_dependency_3.sol | 8 +- .../double_stateVariable_declaration.sol | 2 +- .../double_variable_declaration.sol | 2 +- .../double_variable_declaration_050.sol | 6 +- test/libsolidity/syntaxTests/empty_struct.sol | 2 +- .../syntaxTests/empty_struct_050.sol | 2 +- .../base_arguments_empty_parentheses.sol | 2 +- .../base_arguments_empty_parentheses_V050.sol | 2 +- .../base_arguments_multiple_inheritance.sol | 2 +- ...low_modifier_style_without_parentheses.sol | 2 +- .../duplicated_constructor_call/ancestor.sol | 2 +- .../ancestor_V050.sol | 2 +- .../duplicated_constructor_call/base.sol | 2 +- .../duplicated_constructor_call/base_V050.sol | 2 +- .../base_multi.sol | 4 +- .../base_multi_no_constructor.sol | 2 +- ...se_multi_no_constructor_modifier_style.sol | 2 +- .../inheritance/too_few_base_arguments.sol | 4 +- .../missing_variable_name_in_declaration.sol | 2 +- .../scoping/double_function_declaration.sol | 2 +- ...le_variable_declaration_disjoint_scope.sol | 2 +- ...ariable_declaration_disjoint_scope_050.sol | 4 +- ..._declaration_disjoint_scope_activation.sol | 2 +- ...laration_disjoint_scope_activation_050.sol | 4 +- .../syntaxTests/scoping/name_shadowing.sol | 3 +- .../syntaxTests/scoping/scoping.sol | 2 +- .../scoping/scoping_activation.sol | 2 +- .../syntaxTests/scoping/scoping_for3.sol | 2 +- .../scoping/scoping_for_decl_in_body.sol | 2 +- .../scoping/scoping_self_use_050.sol | 2 +- test/libsolidity/syntaxTests/smoke_test.sol | 2 +- ...nspecified_encoding_internal_functions.sol | 8 +- ...ith_unspecified_encoding_special_types.sol | 10 +-- ...ypes_with_unspecified_encoding_structs.sol | 6 +- .../types_with_unspecified_encoding_types.sol | 12 +-- .../recursion/multi_struct_composition.sol | 2 +- .../structs/recursion/parallel_structs.sol | 2 +- .../recursion/return_recursive_structs.sol | 2 +- .../recursion/return_recursive_structs2.sol | 2 +- .../recursion/return_recursive_structs3.sol | 2 +- .../struct_definition_directly_recursive.sol | 2 +- ...struct_definition_indirectly_recursive.sol | 2 +- .../visibility/interface/function_default.sol | 4 +- .../interface/function_default050.sol | 4 +- .../interface/function_internal.sol | 2 +- .../visibility/interface/function_private.sol | 2 +- .../visibility/interface/function_public.sol | 2 +- .../interface/function_public050.sol | 2 +- test/tools/isoltest.cpp | 71 +++++++++++---- 55 files changed, 216 insertions(+), 108 deletions(-) diff --git a/Changelog.md b/Changelog.md index eb35652f5..9a910a1ac 100644 --- a/Changelog.md +++ b/Changelog.md @@ -11,6 +11,7 @@ Features: * Optimizer: Optimize across ``mload`` if ``msize()`` is not used. * Static Analyzer: Error on duplicated super constructor calls as experimental 0.5.0 feature. * Syntax Checker: Issue warning for empty structs (or error as experimental 0.5.0 feature). + * Syntax Tests: Add source locations to syntax test expectations. * General: Introduce new constructor syntax using the ``constructor`` keyword as experimental 0.5.0 feature. * 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. diff --git a/test/libsolidity/FormattedScope.h b/test/libsolidity/FormattedScope.h index 78560848e..923404f0c 100644 --- a/test/libsolidity/FormattedScope.h +++ b/test/libsolidity/FormattedScope.h @@ -38,6 +38,8 @@ static constexpr char const* GREEN = "\033[1;32m"; static constexpr char const* YELLOW = "\033[1;33m"; static constexpr char const* CYAN = "\033[1;36m"; static constexpr char const* BOLD = "\033[1m"; +static constexpr char const* RED_BACKGROUND = "\033[48;5;160m"; +static constexpr char const* ORANGE_BACKGROUND = "\033[48;5;166m"; static constexpr char const* INVERSE = "\033[7m"; } diff --git a/test/libsolidity/SyntaxTest.cpp b/test/libsolidity/SyntaxTest.cpp index 329543bfb..1c2355d5f 100644 --- a/test/libsolidity/SyntaxTest.cpp +++ b/test/libsolidity/SyntaxTest.cpp @@ -34,17 +34,38 @@ namespace fs = boost::filesystem; using namespace boost::unit_test; template -void skipWhitespace(IteratorType& it, IteratorType end) +void skipWhitespace(IteratorType& _it, IteratorType _end) { - while (it != end && isspace(*it)) - ++it; + while (_it != _end && isspace(*_it)) + ++_it; } template -void skipSlashes(IteratorType& it, IteratorType end) +void skipSlashes(IteratorType& _it, IteratorType _end) { - while (it != end && *it == '/') - ++it; + 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) +{ + if (_it == _end || !isdigit(*_it)) + throw runtime_error("Invalid test expectation. Source location expected."); + int result = 0; + while (_it != _end && isdigit(*_it)) + { + result *= 10; + result += *_it - '0'; + ++_it; + } + return result; } SyntaxTest::SyntaxTest(string const& _filename) @@ -60,22 +81,39 @@ SyntaxTest::SyntaxTest(string const& _filename) bool SyntaxTest::run(ostream& _stream, string const& _linePrefix, bool const _formatted) { + string const versionPragma = "pragma solidity >=0.0;\n"; m_compiler.reset(); - m_compiler.addSource("", "pragma solidity >=0.0;\n" + m_source); + m_compiler.addSource("", versionPragma + m_source); m_compiler.setEVMVersion(dev::test::Options::get().evmVersion()); if (m_compiler.parse()) m_compiler.analyze(); for (auto const& currentError: filterErrors(m_compiler.errors(), true)) - m_errorList.emplace_back(SyntaxTestError{currentError->typeName(), errorMessage(*currentError)}); + { + int locationStart = -1, locationEnd = -1; + if (auto location = boost::get_error_info(*currentError)) + { + // ignore the version pragma inserted by the testing tool when calculating locations. + if (location->start >= static_cast(versionPragma.size())) + locationStart = location->start - versionPragma.size(); + if (location->end >= static_cast(versionPragma.size())) + locationEnd = location->end - versionPragma.size(); + } + m_errorList.emplace_back(SyntaxTestError{ + currentError->typeName(), + errorMessage(*currentError), + locationStart, + locationEnd + }); + } if (m_expectations != m_errorList) { string nextIndentLevel = _linePrefix + " "; FormattedScope(_stream, _formatted, {BOLD, CYAN}) << _linePrefix << "Expected result:" << endl; printErrorList(_stream, m_expectations, nextIndentLevel, _formatted); - FormattedScope(_stream, _formatted, {BOLD, CYAN}) << _linePrefix << "Obtained result:\n"; + FormattedScope(_stream, _formatted, {BOLD, CYAN}) << _linePrefix << "Obtained result:" << endl; printErrorList(_stream, m_errorList, nextIndentLevel, _formatted); return false; } @@ -99,6 +137,16 @@ void SyntaxTest::printErrorList( _stream << _linePrefix; _stream << error.type << ": "; } + if (error.locationStart >= 0 || error.locationEnd >= 0) + { + _stream << "("; + if (error.locationStart >= 0) + _stream << error.locationStart; + _stream << "-"; + if (error.locationEnd >= 0) + _stream << error.locationEnd; + _stream << "): "; + } _stream << error.message << endl; } } @@ -147,8 +195,28 @@ vector SyntaxTest::parseExpectations(istream& _stream) skipWhitespace(it, line.end()); + int locationStart = -1; + int locationEnd = -1; + + if (it != line.end() && *it == '(') + { + ++it; + locationStart = parseUnsignedInteger(it, line.end()); + expect(it, line.end(), '-'); + locationEnd = parseUnsignedInteger(it, line.end()); + expect(it, line.end(), ')'); + expect(it, line.end(), ':'); + } + + skipWhitespace(it, line.end()); + string errorMessage(it, line.end()); - expectations.emplace_back(SyntaxTestError{move(errorType), move(errorMessage)}); + expectations.emplace_back(SyntaxTestError{ + move(errorType), + move(errorMessage), + locationStart, + locationEnd + }); } return expectations; } diff --git a/test/libsolidity/SyntaxTest.h b/test/libsolidity/SyntaxTest.h index dddd86ef4..6159e7894 100644 --- a/test/libsolidity/SyntaxTest.h +++ b/test/libsolidity/SyntaxTest.h @@ -40,9 +40,14 @@ struct SyntaxTestError { std::string type; std::string message; + int locationStart; + int locationEnd; bool operator==(SyntaxTestError const& _rhs) const { - return type == _rhs.type && message == _rhs.message; + return type == _rhs.type && + message == _rhs.message && + locationStart == _rhs.locationStart && + locationEnd == _rhs.locationEnd; } }; diff --git a/test/libsolidity/syntaxTests/constants/cyclic_dependency_1.sol b/test/libsolidity/syntaxTests/constants/cyclic_dependency_1.sol index 2b6aa0883..cb553fbe4 100644 --- a/test/libsolidity/syntaxTests/constants/cyclic_dependency_1.sol +++ b/test/libsolidity/syntaxTests/constants/cyclic_dependency_1.sol @@ -2,4 +2,4 @@ contract C { uint constant a = a; } // ---- -// TypeError: The value of the constant a has a cyclic dependency via a. +// TypeError: (17-36): The value of the constant a has a cyclic dependency via a. diff --git a/test/libsolidity/syntaxTests/constants/cyclic_dependency_2.sol b/test/libsolidity/syntaxTests/constants/cyclic_dependency_2.sol index 461979f85..00f9bb0f1 100644 --- a/test/libsolidity/syntaxTests/constants/cyclic_dependency_2.sol +++ b/test/libsolidity/syntaxTests/constants/cyclic_dependency_2.sol @@ -5,6 +5,6 @@ contract C { uint constant d = 2 + a; } // ---- -// TypeError: The value of the constant a has a cyclic dependency via c. -// TypeError: The value of the constant c has a cyclic dependency via d. -// TypeError: The value of the constant d has a cyclic dependency via a. +// 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. diff --git a/test/libsolidity/syntaxTests/constants/cyclic_dependency_3.sol b/test/libsolidity/syntaxTests/constants/cyclic_dependency_3.sol index f63be05e3..969ed50d3 100644 --- a/test/libsolidity/syntaxTests/constants/cyclic_dependency_3.sol +++ b/test/libsolidity/syntaxTests/constants/cyclic_dependency_3.sol @@ -5,7 +5,7 @@ contract C { uint constant c = b; } // ---- -// TypeError: The value of the constant x has a cyclic dependency via a. -// TypeError: The value of the constant a has a cyclic dependency via b. -// TypeError: The value of the constant b has a cyclic dependency via c. -// TypeError: The value of the constant c has a cyclic dependency via b. +// TypeError: (17-36): The value of the constant x has a cyclic dependency via a. +// TypeError: (42-65): The value of the constant a has a cyclic dependency via b. +// TypeError: (71-90): The value of the constant b has a cyclic dependency via c. +// TypeError: (96-115): The value of the constant c has a cyclic dependency via b. diff --git a/test/libsolidity/syntaxTests/double_stateVariable_declaration.sol b/test/libsolidity/syntaxTests/double_stateVariable_declaration.sol index c5507b649..fda4a17ab 100644 --- a/test/libsolidity/syntaxTests/double_stateVariable_declaration.sol +++ b/test/libsolidity/syntaxTests/double_stateVariable_declaration.sol @@ -3,4 +3,4 @@ contract test { uint128 variable; } // ---- -// DeclarationError: Identifier already declared. +// DeclarationError: (36-52): Identifier already declared. diff --git a/test/libsolidity/syntaxTests/double_variable_declaration.sol b/test/libsolidity/syntaxTests/double_variable_declaration.sol index 3349cfece..9ab87959c 100644 --- a/test/libsolidity/syntaxTests/double_variable_declaration.sol +++ b/test/libsolidity/syntaxTests/double_variable_declaration.sol @@ -5,4 +5,4 @@ contract test { } } // ---- -// DeclarationError: Identifier already declared. +// DeclarationError: (71-80): Identifier already declared. diff --git a/test/libsolidity/syntaxTests/double_variable_declaration_050.sol b/test/libsolidity/syntaxTests/double_variable_declaration_050.sol index 9c2d40d52..2f47e6dc9 100644 --- a/test/libsolidity/syntaxTests/double_variable_declaration_050.sol +++ b/test/libsolidity/syntaxTests/double_variable_declaration_050.sol @@ -6,6 +6,6 @@ contract test { } } // ---- -// Warning: This declaration shadows an existing declaration. -// Warning: Unused local variable. -// Warning: Unused local variable. +// 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/empty_struct.sol b/test/libsolidity/syntaxTests/empty_struct.sol index dcced618a..12655309f 100644 --- a/test/libsolidity/syntaxTests/empty_struct.sol +++ b/test/libsolidity/syntaxTests/empty_struct.sol @@ -2,4 +2,4 @@ contract test { struct A {} } // ---- -// Warning: Defining empty structs is deprecated. +// Warning: (17-28): Defining empty structs is deprecated. diff --git a/test/libsolidity/syntaxTests/empty_struct_050.sol b/test/libsolidity/syntaxTests/empty_struct_050.sol index dbec93c49..886f1f838 100644 --- a/test/libsolidity/syntaxTests/empty_struct_050.sol +++ b/test/libsolidity/syntaxTests/empty_struct_050.sol @@ -3,4 +3,4 @@ contract test { struct A {} } // ---- -// SyntaxError: Defining empty structs is disallowed. +// SyntaxError: (47-58): Defining empty structs is disallowed. diff --git a/test/libsolidity/syntaxTests/inheritance/base_arguments_empty_parentheses.sol b/test/libsolidity/syntaxTests/inheritance/base_arguments_empty_parentheses.sol index b3fbd04ac..0b18b995b 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: Wrong argument count for constructor call: 0 arguments given but expected 1. +// Warning: (101-107): Wrong argument count for constructor call: 0 arguments given but expected 1. diff --git a/test/libsolidity/syntaxTests/inheritance/base_arguments_empty_parentheses_V050.sol b/test/libsolidity/syntaxTests/inheritance/base_arguments_empty_parentheses_V050.sol index b37286347..db04ab8c9 100644 --- a/test/libsolidity/syntaxTests/inheritance/base_arguments_empty_parentheses_V050.sol +++ b/test/libsolidity/syntaxTests/inheritance/base_arguments_empty_parentheses_V050.sol @@ -6,4 +6,4 @@ contract Base { contract Derived is Base(2) { } contract Derived2 is Base(), Derived() { } // ---- -// TypeError: Wrong argument count for constructor call: 0 arguments given but expected 1. +// 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 5483d5d7d..015b33e59 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: Base constructor arguments given twice. +// Warning: (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 0c159a228..6cf68d2aa 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: Modifier-style base constructor call without arguments. +// Warning: (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 8b1af2456..24cff54de 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: Base constructor arguments given twice. +// Warning: (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 index 6616c9a9f..8d5df5bf5 100644 --- a/test/libsolidity/syntaxTests/inheritance/duplicated_constructor_call/ancestor_V050.sol +++ b/test/libsolidity/syntaxTests/inheritance/duplicated_constructor_call/ancestor_V050.sol @@ -4,4 +4,4 @@ contract A { constructor(uint) public { } } contract B is A(2) { constructor() public { } } contract C is B { constructor() A(3) public { } } // ---- -// DeclarationError: Base constructor arguments given twice. +// 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 1fb504fe1..9ceaea5ea 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: Base constructor arguments given twice. +// Warning: (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 index 96eb1bb12..f9325f99d 100644 --- a/test/libsolidity/syntaxTests/inheritance/duplicated_constructor_call/base_V050.sol +++ b/test/libsolidity/syntaxTests/inheritance/duplicated_constructor_call/base_V050.sol @@ -3,4 +3,4 @@ pragma experimental "v0.5.0"; contract A { constructor(uint) public { } } contract B is A(2) { constructor() A(3) public { } } // ---- -// DeclarationError: Base constructor arguments given twice. +// 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 db9ffc85e..e5c2aa365 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: Base constructor arguments given twice. -// Warning: Base constructor arguments given twice. +// Warning: (122-126): Base constructor arguments given twice. +// Warning: (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 fe280ad59..1abf29926 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: Base constructor arguments given twice. +// Warning: (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 ea85aae77..e15242db0 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: Base constructor arguments given twice. +// Warning: (141-163): Base constructor arguments given twice. diff --git a/test/libsolidity/syntaxTests/inheritance/too_few_base_arguments.sol b/test/libsolidity/syntaxTests/inheritance/too_few_base_arguments.sol index 45a0770f3..c55c41f2e 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: Wrong argument count for constructor call: 1 arguments given but expected 2. -// TypeError: Wrong argument count for modifier invocation: 1 arguments given but expected 2. +// TypeError: (74-81): Wrong argument count for constructor call: 1 arguments given but expected 2. +// TypeError: (130-137): Wrong argument count for modifier invocation: 1 arguments given but expected 2. diff --git a/test/libsolidity/syntaxTests/parsing/missing_variable_name_in_declaration.sol b/test/libsolidity/syntaxTests/parsing/missing_variable_name_in_declaration.sol index c03fd97d1..fd3067e30 100644 --- a/test/libsolidity/syntaxTests/parsing/missing_variable_name_in_declaration.sol +++ b/test/libsolidity/syntaxTests/parsing/missing_variable_name_in_declaration.sol @@ -2,4 +2,4 @@ contract test { uint256 ; } // ---- -// ParserError: Expected identifier, got 'Semicolon' +// ParserError: (28-28): Expected identifier, got 'Semicolon' diff --git a/test/libsolidity/syntaxTests/scoping/double_function_declaration.sol b/test/libsolidity/syntaxTests/scoping/double_function_declaration.sol index 2841fd382..dfd67aaaf 100644 --- a/test/libsolidity/syntaxTests/scoping/double_function_declaration.sol +++ b/test/libsolidity/syntaxTests/scoping/double_function_declaration.sol @@ -3,4 +3,4 @@ contract test { function fun() public { } } // ---- -// DeclarationError: Function with same name and arguments defined twice. +// DeclarationError: (20-45): Function with same name and arguments defined twice. 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 ea61d0f37..d90ec2d77 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,4 @@ contract test { } } // ---- -// DeclarationError: Identifier already declared. +// DeclarationError: (77-83): Identifier already declared. 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 index 221959635..06bfe7be2 100644 --- a/test/libsolidity/syntaxTests/scoping/double_variable_declaration_disjoint_scope_050.sol +++ b/test/libsolidity/syntaxTests/scoping/double_variable_declaration_disjoint_scope_050.sol @@ -6,5 +6,5 @@ contract test { } } // ---- -// Warning: Unused local variable. -// Warning: Unused local variable. +// 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 6af89c93d..1a5ff2f9c 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,4 @@ contract test { } } // ---- -// DeclarationError: Identifier already declared. +// DeclarationError: (75-81): Identifier already declared. 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 index 73cddfed5..20ea03498 100644 --- 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 @@ -6,5 +6,5 @@ contract test { } } // ---- -// Warning: Unused local variable. -// Warning: Unused local variable. +// Warning: (87-93): Unused local variable. +// Warning: (105-111): Unused local variable. diff --git a/test/libsolidity/syntaxTests/scoping/name_shadowing.sol b/test/libsolidity/syntaxTests/scoping/name_shadowing.sol index d16877f95..67ada4a43 100644 --- a/test/libsolidity/syntaxTests/scoping/name_shadowing.sol +++ b/test/libsolidity/syntaxTests/scoping/name_shadowing.sol @@ -3,5 +3,4 @@ contract test { function f() pure public { uint32 variable; variable = 2; } } // ---- -// Warning: This declaration shadows an existing declaration. - +// Warning: (69-84): This declaration shadows an existing declaration. diff --git a/test/libsolidity/syntaxTests/scoping/scoping.sol b/test/libsolidity/syntaxTests/scoping/scoping.sol index f47a3e99e..34b055d98 100644 --- a/test/libsolidity/syntaxTests/scoping/scoping.sol +++ b/test/libsolidity/syntaxTests/scoping/scoping.sol @@ -8,4 +8,4 @@ contract test { } } // ---- -// DeclarationError: Undeclared identifier. +// DeclarationError: (123-124): Undeclared identifier. diff --git a/test/libsolidity/syntaxTests/scoping/scoping_activation.sol b/test/libsolidity/syntaxTests/scoping/scoping_activation.sol index 0ed74a00d..7334bc49c 100644 --- a/test/libsolidity/syntaxTests/scoping/scoping_activation.sol +++ b/test/libsolidity/syntaxTests/scoping/scoping_activation.sol @@ -6,4 +6,4 @@ contract test { } } // ---- -// DeclarationError: Undeclared identifier. Did you mean "x"? +// DeclarationError: (85-86): Undeclared identifier. Did you mean "x"? diff --git a/test/libsolidity/syntaxTests/scoping/scoping_for3.sol b/test/libsolidity/syntaxTests/scoping/scoping_for3.sol index 9bc7d5695..1814cb475 100644 --- a/test/libsolidity/syntaxTests/scoping/scoping_for3.sol +++ b/test/libsolidity/syntaxTests/scoping/scoping_for3.sol @@ -8,4 +8,4 @@ contract test { } } // ---- -// DeclarationError: Undeclared identifier. +// DeclarationError: (154-155): 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 07503983e..3e80b3853 100644 --- a/test/libsolidity/syntaxTests/scoping/scoping_for_decl_in_body.sol +++ b/test/libsolidity/syntaxTests/scoping/scoping_for_decl_in_body.sol @@ -7,4 +7,4 @@ contract test { } } // ---- -// DeclarationError: Undeclared identifier. +// DeclarationError: (93-94): Undeclared identifier. diff --git a/test/libsolidity/syntaxTests/scoping/scoping_self_use_050.sol b/test/libsolidity/syntaxTests/scoping/scoping_self_use_050.sol index e942020e8..ab3dcefb9 100644 --- a/test/libsolidity/syntaxTests/scoping/scoping_self_use_050.sol +++ b/test/libsolidity/syntaxTests/scoping/scoping_self_use_050.sol @@ -5,4 +5,4 @@ contract test { } } // ---- -// DeclarationError: Undeclared identifier. Did you mean "a"? +// DeclarationError: (94-95): Undeclared identifier. Did you mean "a"? diff --git a/test/libsolidity/syntaxTests/smoke_test.sol b/test/libsolidity/syntaxTests/smoke_test.sol index 2d48098a1..6abaff183 100644 --- a/test/libsolidity/syntaxTests/smoke_test.sol +++ b/test/libsolidity/syntaxTests/smoke_test.sol @@ -3,4 +3,4 @@ contract test { function fun(uint256 arg1) public { uint256 y; y = arg1; } } // ---- -// Warning: Function state mutability can be restricted to pure +// Warning: (42-100): Function state mutability can be restricted to pure 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 9f57c3a4a..c98d7a577 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 @@ -5,7 +5,7 @@ contract C { } } // ---- -// TypeError: This type cannot be encoded. -// TypeError: This type cannot be encoded. -// TypeError: This type cannot be encoded. -// TypeError: This type cannot be encoded. +// TypeError: (74-83): This type cannot be encoded. +// TypeError: (85-86): This type cannot be encoded. +// TypeError: (88-98): This type cannot be encoded. +// TypeError: (100-115): 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 a7d13215b..f1b5606ea 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 @@ -6,8 +6,8 @@ contract C { } } // ---- -// TypeError: This type cannot be encoded. -// TypeError: This type cannot be encoded. -// TypeError: This type cannot be encoded. -// TypeError: This type cannot be encoded. -// TypeError: This type cannot be encoded. +// 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. 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 378155e98..cc354819f 100644 --- a/test/libsolidity/syntaxTests/specialFunctions/types_with_unspecified_encoding_structs.sol +++ b/test/libsolidity/syntaxTests/specialFunctions/types_with_unspecified_encoding_structs.sol @@ -9,6 +9,6 @@ contract C { } } // ---- -// Warning: Defining empty structs is deprecated. -// TypeError: This type cannot be encoded. -// TypeError: This type cannot be encoded. +// Warning: (51-63): Defining empty structs is deprecated. +// TypeError: (131-132): This type cannot be encoded. +// TypeError: (134-135): 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 6e073fd88..d10c17187 100644 --- a/test/libsolidity/syntaxTests/specialFunctions/types_with_unspecified_encoding_types.sol +++ b/test/libsolidity/syntaxTests/specialFunctions/types_with_unspecified_encoding_types.sol @@ -9,9 +9,9 @@ contract C { } } // ---- -// Warning: Defining empty structs is deprecated. -// TypeError: This type cannot be encoded. -// TypeError: This type cannot be encoded. -// TypeError: This type cannot be encoded. -// TypeError: This type cannot be encoded. -// TypeError: This type cannot be encoded. +// 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. diff --git a/test/libsolidity/syntaxTests/structs/recursion/multi_struct_composition.sol b/test/libsolidity/syntaxTests/structs/recursion/multi_struct_composition.sol index 9a1c22f18..895bb6c58 100644 --- a/test/libsolidity/syntaxTests/structs/recursion/multi_struct_composition.sol +++ b/test/libsolidity/syntaxTests/structs/recursion/multi_struct_composition.sol @@ -12,4 +12,4 @@ contract C { function f(T) public pure { } } // ---- -// Warning: Experimental features are turned on. Do not use experimental features on live deployments. +// 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 d4ad088dc..96362ef0c 100644 --- a/test/libsolidity/syntaxTests/structs/recursion/parallel_structs.sol +++ b/test/libsolidity/syntaxTests/structs/recursion/parallel_structs.sol @@ -12,4 +12,4 @@ contract TestContract function addTestStruct(TestStruct) public pure {} } // ---- -// Warning: Experimental features are turned on. Do not use experimental features on live deployments. +// 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 c02a8aa48..4966a731e 100644 --- a/test/libsolidity/syntaxTests/structs/recursion/return_recursive_structs.sol +++ b/test/libsolidity/syntaxTests/structs/recursion/return_recursive_structs.sol @@ -4,4 +4,4 @@ contract C { } } // ---- -// TypeError: Internal or recursive type is not allowed for public or external functions. +// TypeError: (91-92): 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 e9488cf49..681139245 100644 --- a/test/libsolidity/syntaxTests/structs/recursion/return_recursive_structs2.sol +++ b/test/libsolidity/syntaxTests/structs/recursion/return_recursive_structs2.sol @@ -4,4 +4,4 @@ contract C { } } // ---- -// TypeError: Internal or recursive type is not allowed for public or external functions. +// TypeError: (94-95): 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 6728baec6..47690d9bc 100644 --- a/test/libsolidity/syntaxTests/structs/recursion/return_recursive_structs3.sol +++ b/test/libsolidity/syntaxTests/structs/recursion/return_recursive_structs3.sol @@ -5,4 +5,4 @@ contract C { } } // ---- -// TypeError: Internal or recursive type is not allowed for public or external functions. +// TypeError: (119-122): Internal or recursive type is not allowed for public or external functions. diff --git a/test/libsolidity/syntaxTests/structs/recursion/struct_definition_directly_recursive.sol b/test/libsolidity/syntaxTests/structs/recursion/struct_definition_directly_recursive.sol index cac2e23fd..bcffe3838 100644 --- a/test/libsolidity/syntaxTests/structs/recursion/struct_definition_directly_recursive.sol +++ b/test/libsolidity/syntaxTests/structs/recursion/struct_definition_directly_recursive.sol @@ -5,4 +5,4 @@ contract Test { } } // ---- -// TypeError: Recursive struct definition. +// TypeError: (20-93): Recursive struct definition. diff --git a/test/libsolidity/syntaxTests/structs/recursion/struct_definition_indirectly_recursive.sol b/test/libsolidity/syntaxTests/structs/recursion/struct_definition_indirectly_recursive.sol index 11fc63078..64dab8d00 100644 --- a/test/libsolidity/syntaxTests/structs/recursion/struct_definition_indirectly_recursive.sol +++ b/test/libsolidity/syntaxTests/structs/recursion/struct_definition_indirectly_recursive.sol @@ -9,4 +9,4 @@ contract Test { } } // ---- -// TypeError: Recursive struct definition. +// TypeError: (20-118): Recursive struct definition. diff --git a/test/libsolidity/syntaxTests/visibility/interface/function_default.sol b/test/libsolidity/syntaxTests/visibility/interface/function_default.sol index 7b9044dd2..72ce3b40c 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: Functions in interfaces should be declared external. -// Warning: No visibility specified. Defaulting to "public". In interfaces it defaults to external. +// 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. diff --git a/test/libsolidity/syntaxTests/visibility/interface/function_default050.sol b/test/libsolidity/syntaxTests/visibility/interface/function_default050.sol index 127d4e92a..513df26b3 100644 --- a/test/libsolidity/syntaxTests/visibility/interface/function_default050.sol +++ b/test/libsolidity/syntaxTests/visibility/interface/function_default050.sol @@ -3,5 +3,5 @@ interface I { function f(); } // ---- -// SyntaxError: No visibility specified. -// TypeError: Functions in interfaces must be declared external. +// 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_internal.sol b/test/libsolidity/syntaxTests/visibility/interface/function_internal.sol index a1cf52463..ac62e69b2 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: Functions in interfaces cannot be internal or private. +// TypeError: (15-37): Functions in interfaces cannot be internal or private. diff --git a/test/libsolidity/syntaxTests/visibility/interface/function_private.sol b/test/libsolidity/syntaxTests/visibility/interface/function_private.sol index 887ebd4bf..881e647e1 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: Functions in interfaces cannot be internal or private. +// TypeError: (15-36): Functions in interfaces cannot be internal or private. diff --git a/test/libsolidity/syntaxTests/visibility/interface/function_public.sol b/test/libsolidity/syntaxTests/visibility/interface/function_public.sol index 146d4f5b5..891d9fdf3 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: Functions in interfaces should be declared external. \ No newline at end of file +// Warning: (15-35): Functions in interfaces should be declared external. diff --git a/test/libsolidity/syntaxTests/visibility/interface/function_public050.sol b/test/libsolidity/syntaxTests/visibility/interface/function_public050.sol index f957f0b4c..e0c040956 100644 --- a/test/libsolidity/syntaxTests/visibility/interface/function_public050.sol +++ b/test/libsolidity/syntaxTests/visibility/interface/function_public050.sol @@ -3,4 +3,4 @@ interface I { function f() public; } // ---- -// TypeError: Functions in interfaces must be declared external. \ No newline at end of file +// TypeError: (45-65): Functions in interfaces must be declared external. diff --git a/test/tools/isoltest.cpp b/test/tools/isoltest.cpp index 07df8f60a..7a147bd02 100644 --- a/test/tools/isoltest.cpp +++ b/test/tools/isoltest.cpp @@ -55,7 +55,6 @@ public: { Success, Failure, - InputOutputError, Exception }; @@ -90,11 +89,53 @@ string SyntaxTestTool::editor; void SyntaxTestTool::printContract() const { - stringstream stream(m_test->source()); - string line; - while (getline(stream, line)) - cout << " " << line << endl; - cout << endl; + if (m_formatted) + { + string const& source = m_test->source(); + if (source.empty()) + return; + + std::vector sourceFormatting(source.length(), formatting::RESET); + for (auto const& error: m_test->errorList()) + if (error.locationStart >= 0 && error.locationEnd >= 0) + { + assert(static_cast(error.locationStart) < source.length()); + assert(static_cast(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() @@ -107,15 +148,6 @@ SyntaxTestTool::Result SyntaxTestTool::process() try { m_test = unique_ptr(new SyntaxTest(m_path.string())); - } - catch (std::exception const& _e) - { - FormattedScope(cout, m_formatted, {BOLD, RED}) << "cannot read test: " << _e.what() << endl; - return Result::InputOutputError; - } - - try - { success = m_test->run(outputMessages, " ", m_formatted); } catch(CompilerError const& _e) @@ -142,6 +174,11 @@ SyntaxTestTool::Result SyntaxTestTool::process() "UnimplementedFeatureError: " << SyntaxTest::errorMessage(_e) << endl; return Result::Exception; } + catch (std::exception const& _e) + { + FormattedScope(cout, m_formatted, {BOLD, RED}) << "Exception: " << _e.what() << endl; + return Result::Exception; + } catch(...) { FormattedScope(cout, m_formatted, {BOLD, RED}) << @@ -262,10 +299,6 @@ SyntaxTestStats SyntaxTestTool::processPath( paths.pop(); ++successCount; break; - default: - // non-recoverable error; continue with next test case - paths.pop(); - break; } } } From 5f76f47f2edabe7be335575d80722a94a847587a Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Mon, 9 Apr 2018 15:23:18 +0100 Subject: [PATCH 129/197] Add end to end tests for SHL/SHR/SAR instructions (constantinople only) --- test/libsolidity/SolidityEndToEndTest.cpp | 129 ++++++++++++++++++++++ 1 file changed, 129 insertions(+) diff --git a/test/libsolidity/SolidityEndToEndTest.cpp b/test/libsolidity/SolidityEndToEndTest.cpp index beeae7860..0a43069a1 100644 --- a/test/libsolidity/SolidityEndToEndTest.cpp +++ b/test/libsolidity/SolidityEndToEndTest.cpp @@ -11141,6 +11141,135 @@ BOOST_AUTO_TEST_CASE(swap_peephole_optimisation) 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) { + assembly { + a + b + shl + =: c + } + } + function shr(uint a, uint b) returns (uint c) { + assembly { + a + b + shr + =: c + } + } + function sar(uint a, uint b) returns (uint c) { + assembly { + a + b + sar + =: c + } + } + } + )"; + compileAndRun(sourceCode); + BOOST_CHECK(callContractFunction("shl(uint256,uint256)", u256(1), u256(2)) == encodeArgs(u256(4))); + BOOST_CHECK(callContractFunction("shl(uint256,uint256)", u256("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"), u256(1)) == encodeArgs(u256("0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe"))); + BOOST_CHECK(callContractFunction("shl(uint256,uint256)", u256("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"), u256(256)) == encodeArgs(u256(0))); + BOOST_CHECK(callContractFunction("shr(uint256,uint256)", u256(3), u256(1)) == encodeArgs(u256(1))); + BOOST_CHECK(callContractFunction("shr(uint256,uint256)", u256("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"), u256(1)) == encodeArgs(u256("0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"))); + BOOST_CHECK(callContractFunction("shr(uint256,uint256)", u256("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"), u256(255)) == encodeArgs(u256(1))); + BOOST_CHECK(callContractFunction("shr(uint256,uint256)", u256("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"), u256(256)) == encodeArgs(u256(0))); + BOOST_CHECK(callContractFunction("sar(uint256,uint256)", u256(3), u256(1)) == encodeArgs(u256(1))); + BOOST_CHECK(callContractFunction("sar(uint256,uint256)", u256("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"), u256(1)) == encodeArgs(u256("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"))); + BOOST_CHECK(callContractFunction("sar(uint256,uint256)", u256("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"), u256(255)) == encodeArgs(u256("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"))); + BOOST_CHECK(callContractFunction("sar(uint256,uint256)", u256("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"), u256(256)) == encodeArgs(u256("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"))); +} + +BOOST_AUTO_TEST_CASE(bitwise_shifting_constants_constantinople) +{ + if (!dev::test::Options::get().evmVersion().hasBitwiseShifting()) + return; + char const* sourceCode = R"( + contract C { + function shl_1() returns (bool) { + uint c; + assembly { + 1 + 2 + shl + =: c + } + assert(c == 4); + return true; + } + function shl_2() returns (bool) { + uint c; + assembly { + 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff + 1 + shl + =: c + } + assert(c == 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe); + return true; + } + function shl_3() returns (bool) { + uint c; + assembly { + 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff + 256 + shl + =: c + } + assert(c == 0); + return true; + } + function shr_1() returns (bool) { + uint c; + assembly { + 3 + 1 + shr + =: c + } + assert(c == 1); + return true; + } + function shr_2() returns (bool) { + uint c; + assembly { + 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff + 1 + shr + =: c + } + assert(c == 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff); + return true; + } + function shr_3() returns (bool) { + uint c; + assembly { + 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff + 256 + shr + =: c + } + assert(c == 0); + return true; + } + } + )"; + compileAndRun(sourceCode); + BOOST_CHECK(callContractFunction("shl_1()") == encodeArgs(u256(1))); + BOOST_CHECK(callContractFunction("shl_2()") == encodeArgs(u256(1))); + BOOST_CHECK(callContractFunction("shl_3()") == encodeArgs(u256(1))); + BOOST_CHECK(callContractFunction("shr_1()") == encodeArgs(u256(1))); + BOOST_CHECK(callContractFunction("shr_2()") == encodeArgs(u256(1))); + BOOST_CHECK(callContractFunction("shr_3()") == encodeArgs(u256(1))); +} + BOOST_AUTO_TEST_SUITE_END() } From 44500341748070bd1ad97046b915bf2c76b1eb9b Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Fri, 6 Apr 2018 12:20:01 +0200 Subject: [PATCH 130/197] Run tests in constantinople mode too --- scripts/tests.sh | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/scripts/tests.sh b/scripts/tests.sh index 2e40f8cb8..542c932a3 100755 --- a/scripts/tests.sh +++ b/scripts/tests.sh @@ -99,11 +99,18 @@ then progress="" fi +EVM_VERSIONS="homestead byzantium" + +if [ "$CIRCLECI" ] || [ -z "$CI" ] +then +EVM_VERSIONS+=" constantinople" +fi + # And then run the Solidity unit-tests in the matrix combination of optimizer / no optimizer # and homestead / byzantium VM, # pointing to that IPC endpoint. for optimize in "" "--optimize" do - for vm in homestead byzantium + for vm in $EVM_VERSIONS do echo "--> Running tests using "$optimize" --evm-version "$vm"..." log="" From 2e73ef5ac5db406e86e3a90c89d3be4b0d666073 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Fri, 6 Apr 2018 14:48:17 +0200 Subject: [PATCH 131/197] Ignore shift warnings on constantinople tests --- test/libsolidity/InlineAssembly.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/libsolidity/InlineAssembly.cpp b/test/libsolidity/InlineAssembly.cpp index 34ca33e3c..0ced17924 100644 --- a/test/libsolidity/InlineAssembly.cpp +++ b/test/libsolidity/InlineAssembly.cpp @@ -783,6 +783,8 @@ BOOST_AUTO_TEST_CASE(shift) BOOST_AUTO_TEST_CASE(shift_constantinople_warning) { + if (dev::test::Options::get().evmVersion().hasBitwiseShifting()) + return; CHECK_PARSE_WARNING("{ pop(shl(10, 32)) }", Warning, "The \"shl\" instruction is only available for Constantinople-compatible VMs."); CHECK_PARSE_WARNING("{ pop(shr(10, 32)) }", Warning, "The \"shr\" instruction is only available for Constantinople-compatible VMs."); CHECK_PARSE_WARNING("{ pop(sar(10, 32)) }", Warning, "The \"sar\" instruction is only available for Constantinople-compatible VMs."); From 3b7b962b663b493ad876a084fea72a3b747ce9e8 Mon Sep 17 00:00:00 2001 From: Jason Cobb Date: Tue, 10 Apr 2018 17:35:15 -0400 Subject: [PATCH 132/197] Fix bug in typechecking when comparing rational literals --- Changelog.md | 1 + libsolidity/ast/Types.cpp | 6 +++--- test/libsolidity/syntaxTests/literal_comparisons.sol | 7 +++++++ 3 files changed, 11 insertions(+), 3 deletions(-) create mode 100644 test/libsolidity/syntaxTests/literal_comparisons.sol diff --git a/Changelog.md b/Changelog.md index 9a910a1ac..8c9ba460c 100644 --- a/Changelog.md +++ b/Changelog.md @@ -27,6 +27,7 @@ Bugfixes: * DocString Parser: Fix error message for empty descriptions. * Standard JSON: Support ``constantinople`` as ``evmVersion`` properly. * Type Checker: Fix detection of recursive structs. + * Type Checker: Fix asymmetry bug when comparing with literal numbers. * Type System: Improve error message when attempting to shift by a fractional amount. * Type System: Make external library functions accessible. * Type System: Prevent encoding of weird types. diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp index de359ec6f..21353080b 100644 --- a/libsolidity/ast/Types.cpp +++ b/libsolidity/ast/Types.cpp @@ -839,10 +839,10 @@ TypePointer RationalNumberType::binaryOperatorResult(Token::Value _operator, Typ { if (_other->category() == Category::Integer || _other->category() == Category::FixedPoint) { - auto mobile = mobileType(); - if (!mobile) + auto commonType = Type::commonType(shared_from_this(), _other); + if (!commonType) return TypePointer(); - return mobile->binaryOperatorResult(_operator, _other); + return commonType->binaryOperatorResult(_operator, _other); } else if (_other->category() != category()) return TypePointer(); diff --git a/test/libsolidity/syntaxTests/literal_comparisons.sol b/test/libsolidity/syntaxTests/literal_comparisons.sol new file mode 100644 index 000000000..dd2afcaa1 --- /dev/null +++ b/test/libsolidity/syntaxTests/literal_comparisons.sol @@ -0,0 +1,7 @@ +contract test { + function f(int8 x) public pure { + if (x == 1) {} + if (1 == x) {} + } +} +// ---- From 05781c955b0654fca82a4016e5b2f11b60ccf8f6 Mon Sep 17 00:00:00 2001 From: chriseth Date: Wed, 11 Apr 2018 16:20:37 +0200 Subject: [PATCH 133/197] Fix line numbers for errors. --- .../more_than_256_declarationerrors.sol | 512 +++++++++--------- .../more_than_256_syntaxerrors.sol | 512 +++++++++--------- 2 files changed, 512 insertions(+), 512 deletions(-) diff --git a/test/libsolidity/syntaxTests/more_than_256_declarationerrors.sol b/test/libsolidity/syntaxTests/more_than_256_declarationerrors.sol index 363059775..2d75f29bc 100644 --- a/test/libsolidity/syntaxTests/more_than_256_declarationerrors.sol +++ b/test/libsolidity/syntaxTests/more_than_256_declarationerrors.sol @@ -265,260 +265,260 @@ contract C { } } // ---- -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. -// DeclarationError: Undeclared identifier. +// 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. // 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 66e185fae..2c9b8a426 100644 --- a/test/libsolidity/syntaxTests/more_than_256_syntaxerrors.sol +++ b/test/libsolidity/syntaxTests/more_than_256_syntaxerrors.sol @@ -265,260 +265,260 @@ contract C { } } // ---- -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. -// SyntaxError: "continue" has to be in a "for" or "while" loop. +// 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. // Warning: There are more than 256 errors. Aborting. From fdcbf1337a018087fe82a2fbef938d0acd6769f9 Mon Sep 17 00:00:00 2001 From: Daniel Kirchner Date: Wed, 11 Apr 2018 18:01:01 +0200 Subject: [PATCH 134/197] Syntax Tests: extract array_length_* tests. --- .../SolidityNameAndTypeResolution.cpp | 165 ------------------ ..._cannot_be_constant_function_parameter.sol | 7 + .../can_be_constant_in_function.sol | 8 + .../arrayLength/can_be_constant_in_struct.sol | 7 + .../arrayLength/can_be_recursive_constant.sol | 6 + .../arrayLength/cannot_be_function.sol | 6 + .../arrayLength/cannot_be_function_call.sol | 7 + .../arrayLength/complex_cyclic_constant.sol | 10 ++ .../const_cannot_be_fractional.sol | 6 + .../syntaxTests/arrayLength/constant_var.sol | 5 + .../arrayLength/cyclic_constant.sol | 8 + .../arrayLength/invalid_expression_1.sol | 5 + .../arrayLength/invalid_expression_2.sol | 5 + .../arrayLength/invalid_expression_3.sol | 5 + .../arrayLength/invalid_expression_4.sol | 5 + .../arrayLength/invalid_expression_5.sol | 5 + .../arrayLength/non_integer_constant_var.sol | 6 + .../not_convertible_to_integer.sol | 5 + .../arrayLength/pure_functions.sol | 6 + .../syntaxTests/arrayLength/too_large.sol | 5 + 20 files changed, 117 insertions(+), 165 deletions(-) create mode 100644 test/libsolidity/syntaxTests/arrayLength/array_length_cannot_be_constant_function_parameter.sol create mode 100644 test/libsolidity/syntaxTests/arrayLength/can_be_constant_in_function.sol create mode 100644 test/libsolidity/syntaxTests/arrayLength/can_be_constant_in_struct.sol create mode 100644 test/libsolidity/syntaxTests/arrayLength/can_be_recursive_constant.sol create mode 100644 test/libsolidity/syntaxTests/arrayLength/cannot_be_function.sol create mode 100644 test/libsolidity/syntaxTests/arrayLength/cannot_be_function_call.sol create mode 100644 test/libsolidity/syntaxTests/arrayLength/complex_cyclic_constant.sol create mode 100644 test/libsolidity/syntaxTests/arrayLength/const_cannot_be_fractional.sol create mode 100644 test/libsolidity/syntaxTests/arrayLength/constant_var.sol create mode 100644 test/libsolidity/syntaxTests/arrayLength/cyclic_constant.sol create mode 100644 test/libsolidity/syntaxTests/arrayLength/invalid_expression_1.sol create mode 100644 test/libsolidity/syntaxTests/arrayLength/invalid_expression_2.sol create mode 100644 test/libsolidity/syntaxTests/arrayLength/invalid_expression_3.sol create mode 100644 test/libsolidity/syntaxTests/arrayLength/invalid_expression_4.sol create mode 100644 test/libsolidity/syntaxTests/arrayLength/invalid_expression_5.sol create mode 100644 test/libsolidity/syntaxTests/arrayLength/non_integer_constant_var.sol create mode 100644 test/libsolidity/syntaxTests/arrayLength/not_convertible_to_integer.sol create mode 100644 test/libsolidity/syntaxTests/arrayLength/pure_functions.sol create mode 100644 test/libsolidity/syntaxTests/arrayLength/too_large.sol diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index fcee0df3a..293f5f44e 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -7720,171 +7720,6 @@ BOOST_AUTO_TEST_CASE(address_overload_resolution) CHECK_SUCCESS(text); } -BOOST_AUTO_TEST_CASE(array_length_too_large) -{ - char const* text = R"( - contract C { - uint[8**90] ids; - } - )"; - CHECK_ERROR(text, TypeError, "Invalid array length, expected integer literal or constant expression."); -} - -BOOST_AUTO_TEST_CASE(array_length_not_convertible_to_integer) -{ - char const* text = R"( - contract C { - uint[true] ids; - } - )"; - CHECK_ERROR(text, TypeError, "Invalid array length, expected integer literal or constant expression."); -} - -BOOST_AUTO_TEST_CASE(array_length_constant_var) -{ - char const* text = R"( - contract C { - uint constant LEN = 10; - uint[LEN] ids; - } - )"; - CHECK_SUCCESS(text); -} - -BOOST_AUTO_TEST_CASE(array_length_non_integer_constant_var) -{ - char const* text = R"( - contract C { - bool constant LEN = true; - uint[LEN] ids; - } - )"; - CHECK_ERROR(text, TypeError, "Invalid array length, expected integer literal or constant expression."); -} - -BOOST_AUTO_TEST_CASE(array_length_cannot_be_function) -{ - char const* text = R"( - contract C { - function f() {} - uint[f] ids; - } - )"; - CHECK_ERROR(text, TypeError, "Invalid array length, expected integer literal or constant expression."); -} - -BOOST_AUTO_TEST_CASE(array_length_can_be_recursive_constant) -{ - char const* text = R"( - contract C { - uint constant L = 5; - uint constant LEN = L + 4 * L; - uint[LEN] ids; - } - )"; - CHECK_SUCCESS(text); -} - -BOOST_AUTO_TEST_CASE(array_length_cannot_be_function_call) -{ - char const* text = R"( - contract C { - function f(uint x) {} - uint constant LEN = f(); - uint[LEN] ids; - } - )"; - CHECK_ERROR(text, TypeError, "Invalid array length, expected integer literal or constant expression."); -} - -BOOST_AUTO_TEST_CASE(array_length_const_cannot_be_fractional) -{ - char const* text = R"( - contract C { - fixed constant L = 10.5; - uint[L] ids; - } - )"; - CHECK_ERROR(text, TypeError, "Array with fractional length specified"); -} - -BOOST_AUTO_TEST_CASE(array_length_can_be_constant_in_struct) -{ - char const* text = R"( - contract C { - uint constant LEN = 10; - struct Test { - uint[LEN] ids; - } - } - )"; - CHECK_SUCCESS(text); -} - -BOOST_AUTO_TEST_CASE(array_length_can_be_constant_in_function) -{ - char const* text = R"( - contract C { - uint constant LEN = 10; - function f() { - uint[LEN] a; - } - } - )"; - CHECK_SUCCESS(text); -} - -BOOST_AUTO_TEST_CASE(array_length_cannot_be_constant_function_parameter) -{ - char const* text = R"( - contract C { - function f(uint constant LEN) { - uint[LEN] a; - } - } - )"; - CHECK_ERROR(text, TypeError, "Invalid array length, expected integer literal or constant expression."); -} - -BOOST_AUTO_TEST_CASE(array_length_with_cyclic_constant) -{ - char const* text = R"( - contract C { - uint constant LEN = LEN; - function f() { - uint[LEN] a; - } - } - )"; - CHECK_ERROR(text, TypeError, "Cyclic constant definition (or maximum recursion depth exhausted)."); -} - -BOOST_AUTO_TEST_CASE(array_length_with_complex_cyclic_constant) -{ - char const* text = R"( - contract C { - uint constant L2 = LEN - 10; - uint constant L1 = L2 / 10; - uint constant LEN = 10 + L1 * 5; - function f() { - uint[LEN] a; - } - } - )"; - CHECK_ERROR(text, TypeError, "Cyclic constant definition (or maximum recursion depth exhausted)."); -} - -BOOST_AUTO_TEST_CASE(array_length_with_pure_functions) -{ - char const* text = R"( - contract C { - uint constant LEN = keccak256(ripemd160(33)); - uint[LEN] ids; - } - )"; - CHECK_ERROR(text, TypeError, "Invalid array length, expected integer literal or constant expression."); -} - BOOST_AUTO_TEST_CASE(array_length_invalid_expression) { char const* text = R"( 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 new file mode 100644 index 000000000..11d40f26d --- /dev/null +++ b/test/libsolidity/syntaxTests/arrayLength/array_length_cannot_be_constant_function_parameter.sol @@ -0,0 +1,7 @@ +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/can_be_constant_in_function.sol b/test/libsolidity/syntaxTests/arrayLength/can_be_constant_in_function.sol new file mode 100644 index 000000000..92536dd50 --- /dev/null +++ b/test/libsolidity/syntaxTests/arrayLength/can_be_constant_in_function.sol @@ -0,0 +1,8 @@ +contract C { + uint constant LEN = 10; + function f() public pure { + uint[LEN] memory a; + a; + } +} +// ---- diff --git a/test/libsolidity/syntaxTests/arrayLength/can_be_constant_in_struct.sol b/test/libsolidity/syntaxTests/arrayLength/can_be_constant_in_struct.sol new file mode 100644 index 000000000..89e174f2d --- /dev/null +++ b/test/libsolidity/syntaxTests/arrayLength/can_be_constant_in_struct.sol @@ -0,0 +1,7 @@ +contract C { + uint constant LEN = 10; + struct Test { + uint[LEN] ids; + } +} +// ---- diff --git a/test/libsolidity/syntaxTests/arrayLength/can_be_recursive_constant.sol b/test/libsolidity/syntaxTests/arrayLength/can_be_recursive_constant.sol new file mode 100644 index 000000000..6810a9d6a --- /dev/null +++ b/test/libsolidity/syntaxTests/arrayLength/can_be_recursive_constant.sol @@ -0,0 +1,6 @@ +contract C { + uint constant L = 5; + uint constant LEN = L + 4 * L; + uint[LEN] ids; +} +// ---- diff --git a/test/libsolidity/syntaxTests/arrayLength/cannot_be_function.sol b/test/libsolidity/syntaxTests/arrayLength/cannot_be_function.sol new file mode 100644 index 000000000..ac3abc4c4 --- /dev/null +++ b/test/libsolidity/syntaxTests/arrayLength/cannot_be_function.sol @@ -0,0 +1,6 @@ +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 new file mode 100644 index 000000000..a68639556 --- /dev/null +++ b/test/libsolidity/syntaxTests/arrayLength/cannot_be_function_call.sol @@ -0,0 +1,7 @@ +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 new file mode 100644 index 000000000..254f9f020 --- /dev/null +++ b/test/libsolidity/syntaxTests/arrayLength/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() { + 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/arrayLength/const_cannot_be_fractional.sol new file mode 100644 index 000000000..397bbbcd8 --- /dev/null +++ b/test/libsolidity/syntaxTests/arrayLength/const_cannot_be_fractional.sol @@ -0,0 +1,6 @@ +contract C { + fixed constant L = 10.5; + uint[L] ids; +} +// ---- +// TypeError: (51-52): Array with fractional length specified. diff --git a/test/libsolidity/syntaxTests/arrayLength/constant_var.sol b/test/libsolidity/syntaxTests/arrayLength/constant_var.sol new file mode 100644 index 000000000..41750250e --- /dev/null +++ b/test/libsolidity/syntaxTests/arrayLength/constant_var.sol @@ -0,0 +1,5 @@ +contract C { + uint constant LEN = 10; + uint[LEN] ids; +} +// ---- \ No newline at end of file diff --git a/test/libsolidity/syntaxTests/arrayLength/cyclic_constant.sol b/test/libsolidity/syntaxTests/arrayLength/cyclic_constant.sol new file mode 100644 index 000000000..91ba9045d --- /dev/null +++ b/test/libsolidity/syntaxTests/arrayLength/cyclic_constant.sol @@ -0,0 +1,8 @@ +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/invalid_expression_1.sol b/test/libsolidity/syntaxTests/arrayLength/invalid_expression_1.sol new file mode 100644 index 000000000..c92861eb4 --- /dev/null +++ b/test/libsolidity/syntaxTests/arrayLength/invalid_expression_1.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/arrayLength/invalid_expression_2.sol b/test/libsolidity/syntaxTests/arrayLength/invalid_expression_2.sol new file mode 100644 index 000000000..92e3c3cfb --- /dev/null +++ b/test/libsolidity/syntaxTests/arrayLength/invalid_expression_2.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/arrayLength/invalid_expression_3.sol b/test/libsolidity/syntaxTests/arrayLength/invalid_expression_3.sol new file mode 100644 index 000000000..26add45ca --- /dev/null +++ b/test/libsolidity/syntaxTests/arrayLength/invalid_expression_3.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/arrayLength/invalid_expression_4.sol b/test/libsolidity/syntaxTests/arrayLength/invalid_expression_4.sol new file mode 100644 index 000000000..a0d58f4a9 --- /dev/null +++ b/test/libsolidity/syntaxTests/arrayLength/invalid_expression_4.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/arrayLength/invalid_expression_5.sol b/test/libsolidity/syntaxTests/arrayLength/invalid_expression_5.sol new file mode 100644 index 000000000..38a80867d --- /dev/null +++ b/test/libsolidity/syntaxTests/arrayLength/invalid_expression_5.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/arrayLength/non_integer_constant_var.sol b/test/libsolidity/syntaxTests/arrayLength/non_integer_constant_var.sol new file mode 100644 index 000000000..7a853a345 --- /dev/null +++ b/test/libsolidity/syntaxTests/arrayLength/non_integer_constant_var.sol @@ -0,0 +1,6 @@ +contract C { + bool constant LEN = true; + uint[LEN] ids; +} +// ---- +// TypeError: (52-55): Invalid array length, expected integer literal or constant expression. diff --git a/test/libsolidity/syntaxTests/arrayLength/not_convertible_to_integer.sol b/test/libsolidity/syntaxTests/arrayLength/not_convertible_to_integer.sol new file mode 100644 index 000000000..b44ccfe93 --- /dev/null +++ b/test/libsolidity/syntaxTests/arrayLength/not_convertible_to_integer.sol @@ -0,0 +1,5 @@ +contract C { + uint[true] ids; +} +// ---- +// TypeError: (22-26): Invalid array length, expected integer literal or constant expression. diff --git a/test/libsolidity/syntaxTests/arrayLength/pure_functions.sol b/test/libsolidity/syntaxTests/arrayLength/pure_functions.sol new file mode 100644 index 000000000..b620db763 --- /dev/null +++ b/test/libsolidity/syntaxTests/arrayLength/pure_functions.sol @@ -0,0 +1,6 @@ +contract C { + uint constant LEN = keccak256(ripemd160(33)); + uint[LEN] ids; +} +// ---- +// TypeError: (72-75): Invalid array length, expected integer literal or constant expression. diff --git a/test/libsolidity/syntaxTests/arrayLength/too_large.sol b/test/libsolidity/syntaxTests/arrayLength/too_large.sol new file mode 100644 index 000000000..c90a7494e --- /dev/null +++ b/test/libsolidity/syntaxTests/arrayLength/too_large.sol @@ -0,0 +1,5 @@ +contract C { + uint[8**90] ids; +} +// ---- +// TypeError: (22-27): Invalid array length, expected integer literal or constant expression. From cb548f6f530cf52d8490fb194623888126bd85d4 Mon Sep 17 00:00:00 2001 From: Daniel Kirchner Date: Wed, 11 Apr 2018 17:27:06 +0200 Subject: [PATCH 135/197] Fix ConstantEvaluator to correctly handle single element tuples. --- Changelog.md | 1 + libsolidity/analysis/ConstantEvaluator.cpp | 6 +++++ libsolidity/analysis/ConstantEvaluator.h | 1 + .../syntaxTests/arrayLength/inline_array.sol | 5 ++++ .../syntaxTests/arrayLength/parentheses.sol | 25 +++++++++++++++++++ .../syntaxTests/arrayLength/tuples.sol | 5 ++++ 6 files changed, 43 insertions(+) create mode 100644 test/libsolidity/syntaxTests/arrayLength/inline_array.sol create mode 100644 test/libsolidity/syntaxTests/arrayLength/parentheses.sol create mode 100644 test/libsolidity/syntaxTests/arrayLength/tuples.sol diff --git a/Changelog.md b/Changelog.md index 6df11f275..1612fe3bd 100644 --- a/Changelog.md +++ b/Changelog.md @@ -4,6 +4,7 @@ Features: * Code Generator: Initialize arrays without using ``msize()``. * Code Generator: More specialized and thus optimized implementation for ``x.push(...)`` * Commandline interface: Error when missing or inaccessible file detected. Suppress it with the ``--ignore-missing`` flag. + * Constant Evaluator: Fix evaluation of single element tuples. * General: Limit the number of errors output in a single run to 256. * General: Support accessing dynamic return data in post-byzantium EVMs. * Interfaces: Allow overriding external functions in interfaces with public in an implementing contract. diff --git a/libsolidity/analysis/ConstantEvaluator.cpp b/libsolidity/analysis/ConstantEvaluator.cpp index 83f37f474..8659bbfdc 100644 --- a/libsolidity/analysis/ConstantEvaluator.cpp +++ b/libsolidity/analysis/ConstantEvaluator.cpp @@ -87,6 +87,12 @@ void ConstantEvaluator::endVisit(Identifier const& _identifier) setType(_identifier, type(*value)); } +void ConstantEvaluator::endVisit(TupleExpression const& _tuple) +{ + if (!_tuple.isInlineArray() && _tuple.components().size() == 1) + setType(_tuple, type(*_tuple.components().front())); +} + void ConstantEvaluator::setType(ASTNode const& _node, TypePointer const& _type) { if (_type && _type->category() == Type::Category::RationalNumber) diff --git a/libsolidity/analysis/ConstantEvaluator.h b/libsolidity/analysis/ConstantEvaluator.h index 77a357b65..ac3a24a1f 100644 --- a/libsolidity/analysis/ConstantEvaluator.h +++ b/libsolidity/analysis/ConstantEvaluator.h @@ -56,6 +56,7 @@ private: virtual void endVisit(UnaryOperation const& _operation); virtual void endVisit(Literal const& _literal); virtual void endVisit(Identifier const& _identifier); + virtual void endVisit(TupleExpression const& _tuple); void setType(ASTNode const& _node, TypePointer const& _type); TypePointer type(ASTNode const& _node); diff --git a/test/libsolidity/syntaxTests/arrayLength/inline_array.sol b/test/libsolidity/syntaxTests/arrayLength/inline_array.sol new file mode 100644 index 000000000..a30745d32 --- /dev/null +++ b/test/libsolidity/syntaxTests/arrayLength/inline_array.sol @@ -0,0 +1,5 @@ +contract C { + uint[[2]] a15; +} +// ---- +// TypeError: (22-25): Invalid array length, expected integer literal or constant expression. diff --git a/test/libsolidity/syntaxTests/arrayLength/parentheses.sol b/test/libsolidity/syntaxTests/arrayLength/parentheses.sol new file mode 100644 index 000000000..40f55ad63 --- /dev/null +++ b/test/libsolidity/syntaxTests/arrayLength/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[((((2) + 1)) + (((1))))%1] a15; +} diff --git a/test/libsolidity/syntaxTests/arrayLength/tuples.sol b/test/libsolidity/syntaxTests/arrayLength/tuples.sol new file mode 100644 index 000000000..bc10b3b5d --- /dev/null +++ b/test/libsolidity/syntaxTests/arrayLength/tuples.sol @@ -0,0 +1,5 @@ +contract C { + uint[(1,2)] a15; +} +// ---- +// TypeError: (22-27): Invalid array length, expected integer literal or constant expression. From c15cb6cc7ac68e539dd3969e614be52e9a943ec7 Mon Sep 17 00:00:00 2001 From: Daniel Kirchner Date: Thu, 5 Apr 2018 14:25:14 +0200 Subject: [PATCH 136/197] Prevent information about file existence outside the allowed paths to leak by mimicing boost::filesystem::weakly_canonical. --- Changelog.md | 1 + libdevcore/CommonIO.cpp | 20 ++++++++++++++++++++ libdevcore/CommonIO.h | 5 +++++ solc/CommandLineInterface.cpp | 14 +++++++------- 4 files changed, 33 insertions(+), 7 deletions(-) diff --git a/Changelog.md b/Changelog.md index d6860bdf9..c2ef7210a 100644 --- a/Changelog.md +++ b/Changelog.md @@ -20,6 +20,7 @@ Bugfixes: * Code Generator: Bugfix in modifier lookup in libraries. * Code Generator: Implement packed encoding of external function types. * Code Generator: Treat empty base constructor argument list as not provided. + * Commandline interface: Fix error messages for imported files that do not exist. * Commandline interface: Support ``--evm-version constantinople`` properly. * DocString Parser: Fix error message for empty descriptions. * Standard JSON: Support ``constantinople`` as ``evmVersion`` properly. diff --git a/libdevcore/CommonIO.cpp b/libdevcore/CommonIO.cpp index 6526baf97..0063a8d46 100644 --- a/libdevcore/CommonIO.cpp +++ b/libdevcore/CommonIO.cpp @@ -167,3 +167,23 @@ int dev::readStandardInputChar() DisableConsoleBuffering disableConsoleBuffering; return cin.get(); } + +boost::filesystem::path dev::weaklyCanonicalFilesystemPath(boost::filesystem::path const &_path) +{ + if (boost::filesystem::exists(_path)) + return boost::filesystem::canonical(_path); + else + { + boost::filesystem::path head(_path); + boost::filesystem::path tail; + for (auto it = --_path.end(); !head.empty(); --it) + { + if (boost::filesystem::exists(head)) + break; + tail = (*it) / tail; + head.remove_filename(); + } + head = boost::filesystem::canonical(head); + return head / tail; + } +} diff --git a/libdevcore/CommonIO.h b/libdevcore/CommonIO.h index 3ecdb4c38..9ba68e745 100644 --- a/libdevcore/CommonIO.h +++ b/libdevcore/CommonIO.h @@ -25,6 +25,7 @@ #include #include +#include #include "Common.h" namespace dev @@ -57,4 +58,8 @@ std::string toString(_T const& _t) return o.str(); } +/// Partial implementation of boost::filesystem::weakly_canonical (available in boost>=1.60). +/// 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); + } diff --git a/solc/CommandLineInterface.cpp b/solc/CommandLineInterface.cpp index 93203de60..4da394b2f 100644 --- a/solc/CommandLineInterface.cpp +++ b/solc/CommandLineInterface.cpp @@ -700,13 +700,7 @@ bool CommandLineInterface::processInput() try { auto path = boost::filesystem::path(_path); - if (!boost::filesystem::exists(path)) - return ReadCallback::Result{false, "File not found."}; - - auto canonicalPath = boost::filesystem::canonical(path); - if (!boost::filesystem::is_regular_file(canonicalPath)) - return ReadCallback::Result{false, "Not a valid file."}; - + auto canonicalPath = weaklyCanonicalFilesystemPath(path); bool isAllowed = false; for (auto const& allowedDir: m_allowedDirectories) { @@ -723,6 +717,12 @@ bool CommandLineInterface::processInput() if (!isAllowed) return ReadCallback::Result{false, "File outside of allowed directories."}; + if (!boost::filesystem::exists(canonicalPath)) + return ReadCallback::Result{false, "File not found."}; + + if (!boost::filesystem::is_regular_file(canonicalPath)) + return ReadCallback::Result{false, "Not a valid file."}; + auto contents = dev::readFileAsString(canonicalPath.string()); m_sourceCodes[path.string()] = contents; return ReadCallback::Result{true, contents}; From 928ce088456987431143262dcde2d02553b68192 Mon Sep 17 00:00:00 2001 From: chriseth Date: Wed, 11 Apr 2018 18:30:20 +0200 Subject: [PATCH 137/197] Correctly ignore costs of fallback for other functions. --- Changelog.md | 1 + libsolidity/interface/GasEstimator.cpp | 9 ++++++++- test/libsolidity/GasMeter.cpp | 13 +++++++++++++ 3 files changed, 22 insertions(+), 1 deletion(-) diff --git a/Changelog.md b/Changelog.md index 6df11f275..c6de5872e 100644 --- a/Changelog.md +++ b/Changelog.md @@ -27,6 +27,7 @@ Bugfixes: * Code Generator: Treat empty base constructor argument list as not provided. * Commandline interface: Support ``--evm-version constantinople`` properly. * DocString Parser: Fix error message for empty descriptions. + * Gas Estimator: Correctly ignore costs of fallback function for other functions. * Standard JSON: Support ``constantinople`` as ``evmVersion`` properly. * Type Checker: Fix detection of recursive structs. * Type Checker: Fix asymmetry bug when comparing with literal numbers. diff --git a/libsolidity/interface/GasEstimator.cpp b/libsolidity/interface/GasEstimator.cpp index 2139395f2..a496cc21e 100644 --- a/libsolidity/interface/GasEstimator.cpp +++ b/libsolidity/interface/GasEstimator.cpp @@ -136,12 +136,19 @@ GasEstimator::GasConsumption GasEstimator::functionalEstimation( ExpressionClasses& classes = state->expressionClasses(); using Id = ExpressionClasses::Id; using Ids = vector; + // div(calldataload(0), 1 << 224) equals to hashValue Id hashValue = classes.find(u256(FixedHash<4>::Arith(FixedHash<4>(dev::keccak256(_signature))))); Id calldata = classes.find(Instruction::CALLDATALOAD, Ids{classes.find(u256(0))}); classes.forceEqual(hashValue, Instruction::DIV, Ids{ calldata, - classes.find(u256(1) << (8 * 28)) + classes.find(u256(1) << 224) }); + // lt(calldatasize(), 4) equals to 0 (ignore the shortcut for fallback functions) + classes.forceEqual( + classes.find(u256(0)), + Instruction::LT, + Ids{classes.find(Instruction::CALLDATASIZE), classes.find(u256(4))} + ); } PathGasMeter meter(_items, m_evmVersion); diff --git a/test/libsolidity/GasMeter.cpp b/test/libsolidity/GasMeter.cpp index fd2017f92..0d66456ce 100644 --- a/test/libsolidity/GasMeter.cpp +++ b/test/libsolidity/GasMeter.cpp @@ -294,6 +294,19 @@ BOOST_AUTO_TEST_CASE(extcodesize_gas) testRunTimeGas("f()", vector{encodeArgs()}); } +BOOST_AUTO_TEST_CASE(regular_functions_exclude_fallback) +{ + // A bug in the estimator caused the costs for a specific function + // to always include the costs for the fallback. + char const* sourceCode = R"( + contract A { + uint public x; + function() { x = 2; } + } + )"; + testCreationTimeGas(sourceCode); + testRunTimeGas("x()", vector{encodeArgs()}); +} BOOST_AUTO_TEST_SUITE_END() } From 576964bd016e265db9720946d626b8af9b4e4b14 Mon Sep 17 00:00:00 2001 From: Li Xuanji Date: Wed, 11 Apr 2018 19:20:39 +0800 Subject: [PATCH 138/197] Mark --formal as deprecated in CLI options --- solc/CommandLineInterface.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/solc/CommandLineInterface.cpp b/solc/CommandLineInterface.cpp index 8f81e7994..fc03d497b 100644 --- a/solc/CommandLineInterface.cpp +++ b/solc/CommandLineInterface.cpp @@ -636,7 +636,7 @@ Allowed options)", (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."); + (g_argFormal.c_str(), "Translated source suitable for formal analysis. (Deprecated)"); desc.add(outputComponents); po::options_description allOptions = desc; From daa69df447e167fe75d57a5bbbabee4d637218a4 Mon Sep 17 00:00:00 2001 From: Daniel Kirchner Date: Mon, 9 Apr 2018 15:22:45 +0200 Subject: [PATCH 139/197] Error on invalid arithmetic with constant expressions. --- Changelog.md | 1 + libsolidity/analysis/StaticAnalyzer.cpp | 42 +++++++++++++++++++ libsolidity/analysis/StaticAnalyzer.h | 2 + libsolidity/ast/Types.h | 3 ++ test/libsolidity/SolidityEndToEndTest.cpp | 12 +++--- .../SolidityNameAndTypeResolution.cpp | 14 ------- .../constants/addmod_mulmod_rational.sol | 7 ++++ .../syntaxTests/constants/addmod_zero.sol | 11 +++++ .../constants/division_by_zero.sol | 9 ++++ .../constants/mod_div_rational.sol | 6 +++ .../syntaxTests/constants/mod_zero.sol | 9 ++++ .../syntaxTests/constants/mulmod_zero.sol | 11 +++++ .../syntaxTests/signed_rational_modulus.sol | 8 ++++ 13 files changed, 115 insertions(+), 20 deletions(-) create mode 100644 test/libsolidity/syntaxTests/constants/addmod_mulmod_rational.sol create mode 100644 test/libsolidity/syntaxTests/constants/addmod_zero.sol create mode 100644 test/libsolidity/syntaxTests/constants/division_by_zero.sol create mode 100644 test/libsolidity/syntaxTests/constants/mod_div_rational.sol create mode 100644 test/libsolidity/syntaxTests/constants/mod_zero.sol create mode 100644 test/libsolidity/syntaxTests/constants/mulmod_zero.sol create mode 100644 test/libsolidity/syntaxTests/signed_rational_modulus.sol diff --git a/Changelog.md b/Changelog.md index 1612fe3bd..58ca27051 100644 --- a/Changelog.md +++ b/Changelog.md @@ -35,6 +35,7 @@ Bugfixes: * Type System: Make external library functions accessible. * Type System: Prevent encoding of weird types. * Static Analyzer: Fix non-deterministic order of unused variable warnings. + * Static Analyzer: Invalid arithmetic with constant expressions causes errors. ### 0.4.21 (2018-03-07) diff --git a/libsolidity/analysis/StaticAnalyzer.cpp b/libsolidity/analysis/StaticAnalyzer.cpp index 33b0e2965..51aa0b286 100644 --- a/libsolidity/analysis/StaticAnalyzer.cpp +++ b/libsolidity/analysis/StaticAnalyzer.cpp @@ -21,6 +21,7 @@ */ #include +#include #include #include #include @@ -231,6 +232,47 @@ bool StaticAnalyzer::visit(InlineAssembly const& _inlineAssembly) return true; } +bool StaticAnalyzer::visit(BinaryOperation const& _operation) +{ + if ( + _operation.rightExpression().annotation().isPure && + (_operation.getOperator() == Token::Div || _operation.getOperator() == Token::Mod) + ) + if (auto rhs = dynamic_pointer_cast( + ConstantEvaluator(m_errorReporter).evaluate(_operation.rightExpression()) + )) + if (rhs->isZero()) + m_errorReporter.typeError( + _operation.location(), + (_operation.getOperator() == Token::Div) ? "Division by zero." : "Modulo zero." + ); + + return true; +} + +bool StaticAnalyzer::visit(FunctionCall const& _functionCall) +{ + if (_functionCall.annotation().kind == FunctionCallKind::FunctionCall) + { + auto functionType = dynamic_pointer_cast(_functionCall.expression().annotation().type); + solAssert(functionType, ""); + if (functionType->kind() == FunctionType::Kind::AddMod || functionType->kind() == FunctionType::Kind::MulMod) + { + solAssert(_functionCall.arguments().size() == 3, ""); + if (_functionCall.arguments()[2]->annotation().isPure) + if (auto lastArg = dynamic_pointer_cast( + ConstantEvaluator(m_errorReporter).evaluate(*(_functionCall.arguments())[2]) + )) + if (lastArg->isZero()) + m_errorReporter.typeError( + _functionCall.location(), + "Arithmetic modulo zero." + ); + } + } + return true; +} + bigint StaticAnalyzer::structureSizeEstimate(Type const& _type, set& _structsSeen) { switch (_type.category()) diff --git a/libsolidity/analysis/StaticAnalyzer.h b/libsolidity/analysis/StaticAnalyzer.h index 0a806bbdb..2a62e3911 100644 --- a/libsolidity/analysis/StaticAnalyzer.h +++ b/libsolidity/analysis/StaticAnalyzer.h @@ -64,6 +64,8 @@ private: virtual bool visit(Return const& _return) override; virtual bool visit(MemberAccess const& _memberAccess) override; virtual bool visit(InlineAssembly const& _inlineAssembly) override; + virtual bool visit(BinaryOperation const& _operation) override; + virtual bool visit(FunctionCall const& _functionCall) override; /// @returns the size of this type in storage, including all sub-types. static bigint structureSizeEstimate(Type const& _type, std::set& _structsSeen); diff --git a/libsolidity/ast/Types.h b/libsolidity/ast/Types.h index 05f506f1c..ecfc23330 100644 --- a/libsolidity/ast/Types.h +++ b/libsolidity/ast/Types.h @@ -446,6 +446,9 @@ public: /// @returns true if the value is negative. bool isNegative() const { return m_value < 0; } + /// @returns true if the value is zero. + bool isZero() const { return m_value == 0; } + private: rational m_value; diff --git a/test/libsolidity/SolidityEndToEndTest.cpp b/test/libsolidity/SolidityEndToEndTest.cpp index 39f4b03e9..f7f1062d1 100644 --- a/test/libsolidity/SolidityEndToEndTest.cpp +++ b/test/libsolidity/SolidityEndToEndTest.cpp @@ -7847,12 +7847,12 @@ BOOST_AUTO_TEST_CASE(addmod_mulmod_zero) { char const* sourceCode = R"( contract C { - function f() pure returns (uint) { - addmod(1, 2, 0); + function f(uint d) pure returns (uint) { + addmod(1, 2, d); return 2; } - function g() pure returns (uint) { - mulmod(1, 2, 0); + function g(uint d) pure returns (uint) { + mulmod(1, 2, d); return 2; } function h() pure returns (uint) { @@ -7865,8 +7865,8 @@ BOOST_AUTO_TEST_CASE(addmod_mulmod_zero) } )"; compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("f()"), encodeArgs()); - ABI_CHECK(callContractFunction("g()"), encodeArgs()); + ABI_CHECK(callContractFunction("f(uint)", 0), encodeArgs()); + ABI_CHECK(callContractFunction("g(uint)", 0), encodeArgs()); ABI_CHECK(callContractFunction("h()"), encodeArgs(2)); } diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index 293f5f44e..9847ecaee 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -4920,20 +4920,6 @@ BOOST_AUTO_TEST_CASE(integer_and_fixed_interaction) CHECK_SUCCESS(text); } -BOOST_AUTO_TEST_CASE(signed_rational_modulus) -{ - char const* text = R"( - contract test { - function f() public { - fixed a = 0.42578125 % -0.4271087646484375; - fixed b = .5 % a; - fixed c = a % b; - } - } - )"; - CHECK_SUCCESS(text); -} - BOOST_AUTO_TEST_CASE(one_divided_by_three_integer_conversion) { char const* text = R"( diff --git a/test/libsolidity/syntaxTests/constants/addmod_mulmod_rational.sol b/test/libsolidity/syntaxTests/constants/addmod_mulmod_rational.sol new file mode 100644 index 000000000..267127356 --- /dev/null +++ b/test/libsolidity/syntaxTests/constants/addmod_mulmod_rational.sol @@ -0,0 +1,7 @@ +contract C { + uint constant a = addmod(3, 4, 0.1); + uint constant b = mulmod(3, 4, 0.1); +} +// ---- +// TypeError: (48-51): Invalid type for argument in function call. Invalid implicit conversion from rational_const 1 / 10 to uint256 requested. +// TypeError: (89-92): Invalid type for argument in function call. Invalid implicit conversion from rational_const 1 / 10 to uint256 requested. diff --git a/test/libsolidity/syntaxTests/constants/addmod_zero.sol b/test/libsolidity/syntaxTests/constants/addmod_zero.sol new file mode 100644 index 000000000..18f7d64ad --- /dev/null +++ b/test/libsolidity/syntaxTests/constants/addmod_zero.sol @@ -0,0 +1,11 @@ +contract c { + uint constant a1 = 0; + uint constant a2 = 1; + uint constant b1 = addmod(3, 4, 0); + uint constant b2 = addmod(3, 4, a1); + uint constant b3 = addmod(3, 4, a2 - 1); +} +// ---- +// TypeError: (88-103): Arithmetic modulo zero. +// TypeError: (128-144): Arithmetic modulo zero. +// TypeError: (169-189): Arithmetic modulo zero. diff --git a/test/libsolidity/syntaxTests/constants/division_by_zero.sol b/test/libsolidity/syntaxTests/constants/division_by_zero.sol new file mode 100644 index 000000000..bf6000ec1 --- /dev/null +++ b/test/libsolidity/syntaxTests/constants/division_by_zero.sol @@ -0,0 +1,9 @@ +contract c { + uint constant a1 = 0; + uint constant a2 = 1; + uint constant b1 = 7 / a1; + uint constant b2 = 7 / (a2 - 1); +} +// ---- +// TypeError: (88-94): Division by zero. +// TypeError: (119-131): Division by zero. diff --git a/test/libsolidity/syntaxTests/constants/mod_div_rational.sol b/test/libsolidity/syntaxTests/constants/mod_div_rational.sol new file mode 100644 index 000000000..f8b6ce313 --- /dev/null +++ b/test/libsolidity/syntaxTests/constants/mod_div_rational.sol @@ -0,0 +1,6 @@ +contract C { + fixed a1 = 0.1 % -0.4271087646484375; + fixed a2 = 0.1 % 0.4271087646484375; + fixed a3 = 0 / 0.123; + fixed a4 = 0 / -0.123; +} diff --git a/test/libsolidity/syntaxTests/constants/mod_zero.sol b/test/libsolidity/syntaxTests/constants/mod_zero.sol new file mode 100644 index 000000000..f5e4a23ac --- /dev/null +++ b/test/libsolidity/syntaxTests/constants/mod_zero.sol @@ -0,0 +1,9 @@ +contract c { + uint constant a1 = 0; + uint constant a2 = 1; + uint constant b1 = 3 % a1; + uint constant b2 = 3 % (a2 - 1); +} +// ---- +// TypeError: (88-94): Modulo zero. +// TypeError: (119-131): Modulo zero. diff --git a/test/libsolidity/syntaxTests/constants/mulmod_zero.sol b/test/libsolidity/syntaxTests/constants/mulmod_zero.sol new file mode 100644 index 000000000..856d01eb9 --- /dev/null +++ b/test/libsolidity/syntaxTests/constants/mulmod_zero.sol @@ -0,0 +1,11 @@ +contract c { + uint constant a1 = 0; + uint constant a2 = 1; + uint constant b1 = mulmod(3, 4, 0); + uint constant b2 = mulmod(3, 4, a1); + uint constant b3 = mulmod(3, 4, a2 - 1); +} +// ---- +// TypeError: (88-103): Arithmetic modulo zero. +// TypeError: (128-144): Arithmetic modulo zero. +// TypeError: (169-189): Arithmetic modulo zero. diff --git a/test/libsolidity/syntaxTests/signed_rational_modulus.sol b/test/libsolidity/syntaxTests/signed_rational_modulus.sol new file mode 100644 index 000000000..b37d33d0c --- /dev/null +++ b/test/libsolidity/syntaxTests/signed_rational_modulus.sol @@ -0,0 +1,8 @@ +contract test { + function f() public pure { + fixed a = 0.42578125 % -0.4271087646484375; + fixed b = .5 % a; + fixed c = a % b; + a; b; c; + } +} From bd27ce0e25d298e075de6c77318e7a8df2e27f6d Mon Sep 17 00:00:00 2001 From: chriseth Date: Fri, 16 Mar 2018 11:20:06 +0100 Subject: [PATCH 140/197] Extract function type tests. --- .../SolidityNameAndTypeResolution.cpp | 313 ------------------ ...all_value_on_non_payable_function_type.sol | 6 + .../call_value_on_payable_function_type.sol | 6 + .../delete_external_function_type_invalid.sol | 5 + .../functionTypes/delete_function_type.sol | 14 + .../delete_function_type_invalid.sol | 5 + ...on_to_function_type_calldata_parameter.sol | 7 + ...ernal_function_type_returning_internal.sol | 3 + ...external_function_type_taking_internal.sol | 3 + .../external_function_type_to_address.sol | 5 + .../external_function_type_to_uint.sol | 5 + .../functionTypes/function_type.sol | 5 + .../functionTypes/function_type_arrays.sol | 11 + .../functionTypes/function_type_parameter.sol | 5 + .../functionTypes/function_type_returned.sol | 5 + ...nternal_function_as_external_parameter.sol | 4 + ...external_parameter_in_library_external.sol | 4 + ...external_parameter_in_library_internal.sol | 4 + ...function_returned_from_public_function.sol | 4 + .../internal_function_type_to_address.sol | 5 + .../payable_internal_function_type.sol | 3 + ...le_internal_function_type_is_not_fatal.sol | 7 + .../functionTypes/private_function_type.sol | 5 + .../functionTypes/public_function_type.sol | 5 + ...rn_function_type_parameters_with_names.sol | 3 + ...tion_type_return_parameters_with_names.sol | 3 + 26 files changed, 132 insertions(+), 313 deletions(-) create mode 100644 test/libsolidity/syntaxTests/functionTypes/call_value_on_non_payable_function_type.sol create mode 100644 test/libsolidity/syntaxTests/functionTypes/call_value_on_payable_function_type.sol create mode 100644 test/libsolidity/syntaxTests/functionTypes/delete_external_function_type_invalid.sol create mode 100644 test/libsolidity/syntaxTests/functionTypes/delete_function_type.sol create mode 100644 test/libsolidity/syntaxTests/functionTypes/delete_function_type_invalid.sol create mode 100644 test/libsolidity/syntaxTests/functionTypes/external_function_to_function_type_calldata_parameter.sol create mode 100644 test/libsolidity/syntaxTests/functionTypes/external_function_type_returning_internal.sol create mode 100644 test/libsolidity/syntaxTests/functionTypes/external_function_type_taking_internal.sol create mode 100644 test/libsolidity/syntaxTests/functionTypes/external_function_type_to_address.sol create mode 100644 test/libsolidity/syntaxTests/functionTypes/external_function_type_to_uint.sol create mode 100644 test/libsolidity/syntaxTests/functionTypes/function_type.sol create mode 100644 test/libsolidity/syntaxTests/functionTypes/function_type_arrays.sol create mode 100644 test/libsolidity/syntaxTests/functionTypes/function_type_parameter.sol create mode 100644 test/libsolidity/syntaxTests/functionTypes/function_type_returned.sol create mode 100644 test/libsolidity/syntaxTests/functionTypes/internal_function_as_external_parameter.sol create mode 100644 test/libsolidity/syntaxTests/functionTypes/internal_function_as_external_parameter_in_library_external.sol create mode 100644 test/libsolidity/syntaxTests/functionTypes/internal_function_as_external_parameter_in_library_internal.sol create mode 100644 test/libsolidity/syntaxTests/functionTypes/internal_function_returned_from_public_function.sol create mode 100644 test/libsolidity/syntaxTests/functionTypes/internal_function_type_to_address.sol create mode 100644 test/libsolidity/syntaxTests/functionTypes/payable_internal_function_type.sol create mode 100644 test/libsolidity/syntaxTests/functionTypes/payable_internal_function_type_is_not_fatal.sol create mode 100644 test/libsolidity/syntaxTests/functionTypes/private_function_type.sol create mode 100644 test/libsolidity/syntaxTests/functionTypes/public_function_type.sol create mode 100644 test/libsolidity/syntaxTests/functionTypes/warn_function_type_parameters_with_names.sol create mode 100644 test/libsolidity/syntaxTests/functionTypes/warn_function_type_return_parameters_with_names.sol diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index 293f5f44e..e5e5e246d 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -5264,319 +5264,6 @@ BOOST_AUTO_TEST_CASE(using_directive_for_missing_selftype) CHECK_ERROR(text, TypeError, "Member \"b\" not found or not visible after argument-dependent lookup in bytes memory"); } -BOOST_AUTO_TEST_CASE(function_type) -{ - char const* text = R"( - contract C { - function f() public { - function(uint) returns (uint) x; - } - } - )"; - CHECK_SUCCESS(text); -} - -BOOST_AUTO_TEST_CASE(function_type_parameter) -{ - char const* text = R"( - contract C { - function f(function(uint) external returns (uint) g) public returns (function(uint) external returns (uint)) { - return g; - } - } - )"; - CHECK_SUCCESS(text); -} - -BOOST_AUTO_TEST_CASE(function_type_returned) -{ - char const* text = R"( - contract C { - function f() public returns (function(uint) external returns (uint) g) { - return g; - } - } - )"; - CHECK_SUCCESS(text); -} - -BOOST_AUTO_TEST_CASE(private_function_type) -{ - char const* text = R"( - contract C { - function f() public { - function(uint) private returns (uint) x; - } - } - )"; - CHECK_ERROR(text, TypeError, "Invalid visibility, can only be \"external\" or \"internal\"."); -} - -BOOST_AUTO_TEST_CASE(public_function_type) -{ - char const* text = R"( - contract C { - function f() public { - function(uint) public returns (uint) x; - } - } - )"; - CHECK_ERROR(text, TypeError, "Invalid visibility, can only be \"external\" or \"internal\"."); -} - -BOOST_AUTO_TEST_CASE(payable_internal_function_type) -{ - char const* text = R"( - contract C { - function (uint) internal payable returns (uint) x; - } - )"; - CHECK_ERROR(text, TypeError, "Only external function types can be payable."); -} - -BOOST_AUTO_TEST_CASE(payable_internal_function_type_is_not_fatal) -{ - char const* text = R"( - contract C { - function (uint) internal payable returns (uint) x; - - function g() { - x = g; - } - } - )"; - CHECK_ERROR_ALLOW_MULTI(text, TypeError, (std::vector{"Only external function types can be payable."})); -} - -BOOST_AUTO_TEST_CASE(call_value_on_non_payable_function_type) -{ - char const* text = R"( - contract C { - function (uint) external returns (uint) x; - function f() public { - x.value(2)(); - } - } - )"; - CHECK_ERROR(text, TypeError, "Member \"value\" not found or not visible after argument-dependent lookup in function (uint256) external returns (uint256) - did you forget the \"payable\" modifier?"); -} - -BOOST_AUTO_TEST_CASE(external_function_type_returning_internal) -{ - char const* text = R"( - contract C { - function() external returns (function () internal) x; - } - )"; - CHECK_ERROR(text, TypeError, "Internal type cannot be used for external function type."); -} - -BOOST_AUTO_TEST_CASE(external_function_type_taking_internal) -{ - char const* text = R"( - contract C { - function(function () internal) external x; - } - )"; - CHECK_ERROR(text, TypeError, "Internal type cannot be used for external function type."); -} - -BOOST_AUTO_TEST_CASE(call_value_on_payable_function_type) -{ - char const* text = R"( - contract C { - function (uint) external payable returns (uint) x; - function f() public { - x.value(2)(1); - } - } - )"; - CHECK_SUCCESS(text); -} - -BOOST_AUTO_TEST_CASE(internal_function_as_external_parameter) -{ - // It should not be possible to give internal functions - // as parameters to external functions. - char const* text = R"( - contract C { - function f(function(uint) internal returns (uint) x) public { - } - } - )"; - CHECK_ERROR(text, TypeError, "Internal or recursive type is not allowed for public or external functions."); -} - -BOOST_AUTO_TEST_CASE(internal_function_returned_from_public_function) -{ - // It should not be possible to return internal functions from external functions. - char const* text = R"( - contract C { - function f() public returns (function(uint) internal returns (uint) x) { - } - } - )"; - CHECK_ERROR(text, TypeError, "Internal or recursive type is not allowed for public or external functions."); -} - -BOOST_AUTO_TEST_CASE(internal_function_as_external_parameter_in_library_internal) -{ - char const* text = R"( - library L { - function f(function(uint) internal returns (uint) x) internal { - } - } - )"; - CHECK_SUCCESS(text); -} - -BOOST_AUTO_TEST_CASE(internal_function_as_external_parameter_in_library_external) -{ - char const* text = R"( - library L { - function f(function(uint) internal returns (uint) x) public { - } - } - )"; - CHECK_ERROR(text, TypeError, "Internal or recursive type is not allowed for public or external functions."); -} - -BOOST_AUTO_TEST_CASE(function_type_arrays) -{ - char const* text = R"( - contract C { - function(uint) external returns (uint)[] public x; - function(uint) internal returns (uint)[10] y; - function f() public { - function(uint) returns (uint)[10] memory a; - function(uint) returns (uint)[10] storage b = y; - function(uint) external returns (uint)[] memory c; - c = new function(uint) external returns (uint)[](200); - a; b; - } - } - )"; - CHECK_SUCCESS(text); -} - -BOOST_AUTO_TEST_CASE(delete_function_type) -{ - char const* text = R"( - contract C { - function(uint) external returns (uint) x; - function(uint) internal returns (uint) y; - function f() public { - delete x; - var a = y; - delete a; - delete y; - var c = f; - delete c; - function(uint) internal returns (uint) g; - delete g; - } - } - )"; - CHECK_SUCCESS(text); -} - -BOOST_AUTO_TEST_CASE(delete_function_type_invalid) -{ - char const* text = R"( - contract C { - function f() public { - delete f; - } - } - )"; - CHECK_ERROR(text, TypeError, "Expression has to be an lvalue."); -} - -BOOST_AUTO_TEST_CASE(delete_external_function_type_invalid) -{ - char const* text = R"( - contract C { - function f() public { - delete this.f; - } - } - )"; - CHECK_ERROR(text, TypeError, "Expression has to be an lvalue."); -} - -BOOST_AUTO_TEST_CASE(external_function_to_function_type_calldata_parameter) -{ - // This is a test that checks that the type of the `bytes` parameter is - // correctly changed from its own type `bytes calldata` to `bytes memory` - // when converting to a function type. - char const* text = R"( - contract C { - function f(function(bytes memory) external g) public { } - function callback(bytes) external {} - function g() public { - f(this.callback); - } - } - )"; - CHECK_SUCCESS(text); -} - -BOOST_AUTO_TEST_CASE(external_function_type_to_address) -{ - char const* text = R"( - contract C { - function f() public returns (address) { - return address(this.f); - } - } - )"; - CHECK_SUCCESS(text); -} - -BOOST_AUTO_TEST_CASE(internal_function_type_to_address) -{ - char const* text = R"( - contract C { - function f() public returns (address) { - return address(f); - } - } - )"; - CHECK_ERROR(text, TypeError, "Explicit type conversion not allowed"); -} - -BOOST_AUTO_TEST_CASE(external_function_type_to_uint) -{ - char const* text = R"( - contract C { - function f() public returns (uint) { - return uint(this.f); - } - } - )"; - CHECK_ERROR(text, TypeError, "Explicit type conversion not allowed"); -} - -BOOST_AUTO_TEST_CASE(warn_function_type_parameters_with_names) -{ - char const* text = R"( - contract C { - function(uint a) f; - } - )"; - CHECK_WARNING(text, "Naming function type parameters is deprecated."); -} - -BOOST_AUTO_TEST_CASE(warn_function_type_return_parameters_with_names) -{ - char const* text = R"( - contract C { - function(uint) returns (bool ret) f; - } - )"; - CHECK_WARNING(text, "Naming function type return parameters is deprecated."); -} - BOOST_AUTO_TEST_CASE(shift_constant_left_negative_rvalue) { char const* text = R"( diff --git a/test/libsolidity/syntaxTests/functionTypes/call_value_on_non_payable_function_type.sol b/test/libsolidity/syntaxTests/functionTypes/call_value_on_non_payable_function_type.sol new file mode 100644 index 000000000..dac7908fe --- /dev/null +++ b/test/libsolidity/syntaxTests/functionTypes/call_value_on_non_payable_function_type.sol @@ -0,0 +1,6 @@ +contract C { + function (uint) external returns (uint) x; + function f() public { + x.value(2)(); + } +} diff --git a/test/libsolidity/syntaxTests/functionTypes/call_value_on_payable_function_type.sol b/test/libsolidity/syntaxTests/functionTypes/call_value_on_payable_function_type.sol new file mode 100644 index 000000000..ca2a01964 --- /dev/null +++ b/test/libsolidity/syntaxTests/functionTypes/call_value_on_payable_function_type.sol @@ -0,0 +1,6 @@ +contract C { + function (uint) external payable returns (uint) x; + function f() public { + x.value(2)(1); + } +} diff --git a/test/libsolidity/syntaxTests/functionTypes/delete_external_function_type_invalid.sol b/test/libsolidity/syntaxTests/functionTypes/delete_external_function_type_invalid.sol new file mode 100644 index 000000000..e3bf2dee9 --- /dev/null +++ b/test/libsolidity/syntaxTests/functionTypes/delete_external_function_type_invalid.sol @@ -0,0 +1,5 @@ +contract C { + function f() public { + delete this.f; + } +} diff --git a/test/libsolidity/syntaxTests/functionTypes/delete_function_type.sol b/test/libsolidity/syntaxTests/functionTypes/delete_function_type.sol new file mode 100644 index 000000000..40c0c86f8 --- /dev/null +++ b/test/libsolidity/syntaxTests/functionTypes/delete_function_type.sol @@ -0,0 +1,14 @@ +contract C { + function(uint) external returns (uint) x; + function(uint) internal returns (uint) y; + function f() public { + delete x; + var a = y; + delete a; + delete y; + var c = f; + delete c; + function(uint) internal returns (uint) g; + delete g; + } +} diff --git a/test/libsolidity/syntaxTests/functionTypes/delete_function_type_invalid.sol b/test/libsolidity/syntaxTests/functionTypes/delete_function_type_invalid.sol new file mode 100644 index 000000000..c2c77be0e --- /dev/null +++ b/test/libsolidity/syntaxTests/functionTypes/delete_function_type_invalid.sol @@ -0,0 +1,5 @@ +contract C { + function f() public { + delete f; + } +} 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 new file mode 100644 index 000000000..cbf90e403 --- /dev/null +++ b/test/libsolidity/syntaxTests/functionTypes/external_function_to_function_type_calldata_parameter.sol @@ -0,0 +1,7 @@ +contract C { + function f(function(bytes memory) external g) public { } + function callback(bytes) external {} + function g() public { + f(this.callback); + } +} diff --git a/test/libsolidity/syntaxTests/functionTypes/external_function_type_returning_internal.sol b/test/libsolidity/syntaxTests/functionTypes/external_function_type_returning_internal.sol new file mode 100644 index 000000000..c4f56b41d --- /dev/null +++ b/test/libsolidity/syntaxTests/functionTypes/external_function_type_returning_internal.sol @@ -0,0 +1,3 @@ +contract C { + function() external returns (function () internal) x; +} diff --git a/test/libsolidity/syntaxTests/functionTypes/external_function_type_taking_internal.sol b/test/libsolidity/syntaxTests/functionTypes/external_function_type_taking_internal.sol new file mode 100644 index 000000000..c3ba80a3b --- /dev/null +++ b/test/libsolidity/syntaxTests/functionTypes/external_function_type_taking_internal.sol @@ -0,0 +1,3 @@ +contract C { + function(function () internal) external x; +} diff --git a/test/libsolidity/syntaxTests/functionTypes/external_function_type_to_address.sol b/test/libsolidity/syntaxTests/functionTypes/external_function_type_to_address.sol new file mode 100644 index 000000000..5f4d40650 --- /dev/null +++ b/test/libsolidity/syntaxTests/functionTypes/external_function_type_to_address.sol @@ -0,0 +1,5 @@ +contract C { + function f() public returns (address) { + return address(this.f); + } +} diff --git a/test/libsolidity/syntaxTests/functionTypes/external_function_type_to_uint.sol b/test/libsolidity/syntaxTests/functionTypes/external_function_type_to_uint.sol new file mode 100644 index 000000000..e47abefbf --- /dev/null +++ b/test/libsolidity/syntaxTests/functionTypes/external_function_type_to_uint.sol @@ -0,0 +1,5 @@ +contract C { + function f() public returns (uint) { + return uint(this.f); + } +} diff --git a/test/libsolidity/syntaxTests/functionTypes/function_type.sol b/test/libsolidity/syntaxTests/functionTypes/function_type.sol new file mode 100644 index 000000000..a879e351c --- /dev/null +++ b/test/libsolidity/syntaxTests/functionTypes/function_type.sol @@ -0,0 +1,5 @@ +contract C { + function f() public { + function(uint) returns (uint) x; + } +} diff --git a/test/libsolidity/syntaxTests/functionTypes/function_type_arrays.sol b/test/libsolidity/syntaxTests/functionTypes/function_type_arrays.sol new file mode 100644 index 000000000..c402dbff2 --- /dev/null +++ b/test/libsolidity/syntaxTests/functionTypes/function_type_arrays.sol @@ -0,0 +1,11 @@ +contract C { + function(uint) external returns (uint)[] public x; + function(uint) internal returns (uint)[10] y; + function f() public { + function(uint) returns (uint)[10] memory a; + function(uint) returns (uint)[10] storage b = y; + function(uint) external returns (uint)[] memory c; + c = new function(uint) external returns (uint)[](200); + a; b; + } +} diff --git a/test/libsolidity/syntaxTests/functionTypes/function_type_parameter.sol b/test/libsolidity/syntaxTests/functionTypes/function_type_parameter.sol new file mode 100644 index 000000000..c787e6f07 --- /dev/null +++ b/test/libsolidity/syntaxTests/functionTypes/function_type_parameter.sol @@ -0,0 +1,5 @@ +contract C { + function f(function(uint) external returns (uint) g) public returns (function(uint) external returns (uint)) { + return g; + } +} diff --git a/test/libsolidity/syntaxTests/functionTypes/function_type_returned.sol b/test/libsolidity/syntaxTests/functionTypes/function_type_returned.sol new file mode 100644 index 000000000..0590fad4d --- /dev/null +++ b/test/libsolidity/syntaxTests/functionTypes/function_type_returned.sol @@ -0,0 +1,5 @@ +contract C { + function f() public returns (function(uint) external returns (uint) g) { + return g; + } +} diff --git a/test/libsolidity/syntaxTests/functionTypes/internal_function_as_external_parameter.sol b/test/libsolidity/syntaxTests/functionTypes/internal_function_as_external_parameter.sol new file mode 100644 index 000000000..3103001d2 --- /dev/null +++ b/test/libsolidity/syntaxTests/functionTypes/internal_function_as_external_parameter.sol @@ -0,0 +1,4 @@ +contract C { + function f(function(uint) internal returns (uint) x) public { + } +} diff --git a/test/libsolidity/syntaxTests/functionTypes/internal_function_as_external_parameter_in_library_external.sol b/test/libsolidity/syntaxTests/functionTypes/internal_function_as_external_parameter_in_library_external.sol new file mode 100644 index 000000000..7856d62d2 --- /dev/null +++ b/test/libsolidity/syntaxTests/functionTypes/internal_function_as_external_parameter_in_library_external.sol @@ -0,0 +1,4 @@ +library L { + function f(function(uint) internal returns (uint) x) public { + } +} diff --git a/test/libsolidity/syntaxTests/functionTypes/internal_function_as_external_parameter_in_library_internal.sol b/test/libsolidity/syntaxTests/functionTypes/internal_function_as_external_parameter_in_library_internal.sol new file mode 100644 index 000000000..13d6d03d8 --- /dev/null +++ b/test/libsolidity/syntaxTests/functionTypes/internal_function_as_external_parameter_in_library_internal.sol @@ -0,0 +1,4 @@ +library L { + function f(function(uint) internal returns (uint) x) internal { + } +} diff --git a/test/libsolidity/syntaxTests/functionTypes/internal_function_returned_from_public_function.sol b/test/libsolidity/syntaxTests/functionTypes/internal_function_returned_from_public_function.sol new file mode 100644 index 000000000..e836a6cc6 --- /dev/null +++ b/test/libsolidity/syntaxTests/functionTypes/internal_function_returned_from_public_function.sol @@ -0,0 +1,4 @@ +contract C { + function f() public returns (function(uint) internal returns (uint) x) { + } +} diff --git a/test/libsolidity/syntaxTests/functionTypes/internal_function_type_to_address.sol b/test/libsolidity/syntaxTests/functionTypes/internal_function_type_to_address.sol new file mode 100644 index 000000000..4a3f02c1f --- /dev/null +++ b/test/libsolidity/syntaxTests/functionTypes/internal_function_type_to_address.sol @@ -0,0 +1,5 @@ +contract C { + function f() public returns (address) { + return address(f); + } +} diff --git a/test/libsolidity/syntaxTests/functionTypes/payable_internal_function_type.sol b/test/libsolidity/syntaxTests/functionTypes/payable_internal_function_type.sol new file mode 100644 index 000000000..69efaad4b --- /dev/null +++ b/test/libsolidity/syntaxTests/functionTypes/payable_internal_function_type.sol @@ -0,0 +1,3 @@ +contract C { + function (uint) internal payable returns (uint) x; +} diff --git a/test/libsolidity/syntaxTests/functionTypes/payable_internal_function_type_is_not_fatal.sol b/test/libsolidity/syntaxTests/functionTypes/payable_internal_function_type_is_not_fatal.sol new file mode 100644 index 000000000..fb84f9ca4 --- /dev/null +++ b/test/libsolidity/syntaxTests/functionTypes/payable_internal_function_type_is_not_fatal.sol @@ -0,0 +1,7 @@ +contract C { + function (uint) internal payable returns (uint) x; + + function g() { + x = g; + } +} diff --git a/test/libsolidity/syntaxTests/functionTypes/private_function_type.sol b/test/libsolidity/syntaxTests/functionTypes/private_function_type.sol new file mode 100644 index 000000000..d15430d23 --- /dev/null +++ b/test/libsolidity/syntaxTests/functionTypes/private_function_type.sol @@ -0,0 +1,5 @@ +contract C { + function f() public { + function(uint) private returns (uint) x; + } +} diff --git a/test/libsolidity/syntaxTests/functionTypes/public_function_type.sol b/test/libsolidity/syntaxTests/functionTypes/public_function_type.sol new file mode 100644 index 000000000..ecc2493ad --- /dev/null +++ b/test/libsolidity/syntaxTests/functionTypes/public_function_type.sol @@ -0,0 +1,5 @@ +contract C { + function f() public { + function(uint) public returns (uint) x; + } +} diff --git a/test/libsolidity/syntaxTests/functionTypes/warn_function_type_parameters_with_names.sol b/test/libsolidity/syntaxTests/functionTypes/warn_function_type_parameters_with_names.sol new file mode 100644 index 000000000..44417f256 --- /dev/null +++ b/test/libsolidity/syntaxTests/functionTypes/warn_function_type_parameters_with_names.sol @@ -0,0 +1,3 @@ +contract C { + function(uint a) f; +} 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 new file mode 100644 index 000000000..f8c21fa80 --- /dev/null +++ b/test/libsolidity/syntaxTests/functionTypes/warn_function_type_return_parameters_with_names.sol @@ -0,0 +1,3 @@ +contract C { + function(uint) returns (bool ret) f; +} From 08e807aea01a443419dc82e159776899d96441ec Mon Sep 17 00:00:00 2001 From: chriseth Date: Fri, 16 Mar 2018 11:33:05 +0100 Subject: [PATCH 141/197] Add expectations. --- .../call_value_on_non_payable_function_type.sol | 2 ++ .../delete_external_function_type_invalid.sol | 2 ++ .../syntaxTests/functionTypes/delete_function_type.sol | 3 +++ .../functionTypes/delete_function_type_invalid.sol | 2 ++ ...rnal_function_to_function_type_calldata_parameter.sol | 9 ++++++--- .../external_function_type_returning_internal.sol | 2 ++ .../external_function_type_taking_internal.sol | 2 ++ .../functionTypes/external_function_type_to_address.sol | 2 +- .../functionTypes/external_function_type_to_uint.sol | 2 ++ .../syntaxTests/functionTypes/function_type.sol | 3 ++- .../syntaxTests/functionTypes/function_type_arrays.sol | 2 +- .../functionTypes/function_type_parameter.sol | 2 ++ .../syntaxTests/functionTypes/function_type_returned.sol | 2 +- .../internal_function_as_external_parameter.sol | 4 ++++ ...unction_as_external_parameter_in_library_external.sol | 2 ++ ...unction_as_external_parameter_in_library_internal.sol | 2 +- .../internal_function_returned_from_public_function.sol | 3 +++ .../functionTypes/internal_function_type_to_address.sol | 2 ++ .../functionTypes/payable_internal_function_type.sol | 2 ++ .../payable_internal_function_type_is_not_fatal.sol | 4 +++- .../syntaxTests/functionTypes/private_function_type.sol | 2 ++ .../syntaxTests/functionTypes/public_function_type.sol | 2 ++ .../warn_function_type_parameters_with_names.sol | 2 ++ .../warn_function_type_return_parameters_with_names.sol | 2 ++ 24 files changed, 53 insertions(+), 9 deletions(-) diff --git a/test/libsolidity/syntaxTests/functionTypes/call_value_on_non_payable_function_type.sol b/test/libsolidity/syntaxTests/functionTypes/call_value_on_non_payable_function_type.sol index dac7908fe..87c3b05b1 100644 --- a/test/libsolidity/syntaxTests/functionTypes/call_value_on_non_payable_function_type.sol +++ b/test/libsolidity/syntaxTests/functionTypes/call_value_on_non_payable_function_type.sol @@ -4,3 +4,5 @@ contract C { x.value(2)(); } } +// ---- +// TypeError: (94-101): Member "value" not found or not visible after argument-dependent lookup in function (uint256) external returns (uint256) - did you forget the "payable" modifier? diff --git a/test/libsolidity/syntaxTests/functionTypes/delete_external_function_type_invalid.sol b/test/libsolidity/syntaxTests/functionTypes/delete_external_function_type_invalid.sol index e3bf2dee9..2711dae82 100644 --- a/test/libsolidity/syntaxTests/functionTypes/delete_external_function_type_invalid.sol +++ b/test/libsolidity/syntaxTests/functionTypes/delete_external_function_type_invalid.sol @@ -3,3 +3,5 @@ contract C { delete this.f; } } +// ---- +// TypeError: (54-60): Expression has to be an lvalue. diff --git a/test/libsolidity/syntaxTests/functionTypes/delete_function_type.sol b/test/libsolidity/syntaxTests/functionTypes/delete_function_type.sol index 40c0c86f8..a6fe6c225 100644 --- a/test/libsolidity/syntaxTests/functionTypes/delete_function_type.sol +++ b/test/libsolidity/syntaxTests/functionTypes/delete_function_type.sol @@ -12,3 +12,6 @@ contract C { 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/delete_function_type_invalid.sol b/test/libsolidity/syntaxTests/functionTypes/delete_function_type_invalid.sol index c2c77be0e..60da19e40 100644 --- a/test/libsolidity/syntaxTests/functionTypes/delete_function_type_invalid.sol +++ b/test/libsolidity/syntaxTests/functionTypes/delete_function_type_invalid.sol @@ -3,3 +3,5 @@ contract C { delete f; } } +// ---- +// TypeError: (54-55): Expression has to be an lvalue. 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 cbf90e403..eb4f0693e 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 @@ -1,7 +1,10 @@ +// This is a test that checks that the type of the `bytes` parameter is +// correctly changed from its own type `bytes calldata` to `bytes memory` +// when converting to a function type. contract C { - function f(function(bytes memory) external g) public { } - function callback(bytes) external {} - function g() public { + function f(function(bytes memory) pure external /*g*/) pure public { } + function callback(bytes) pure external {} + function g() view public { f(this.callback); } } diff --git a/test/libsolidity/syntaxTests/functionTypes/external_function_type_returning_internal.sol b/test/libsolidity/syntaxTests/functionTypes/external_function_type_returning_internal.sol index c4f56b41d..8b14d3dc5 100644 --- a/test/libsolidity/syntaxTests/functionTypes/external_function_type_returning_internal.sol +++ b/test/libsolidity/syntaxTests/functionTypes/external_function_type_returning_internal.sol @@ -1,3 +1,5 @@ contract C { function() external returns (function () internal) x; } +// ---- +// TypeError: (46-67): Internal type cannot be used for external function type. diff --git a/test/libsolidity/syntaxTests/functionTypes/external_function_type_taking_internal.sol b/test/libsolidity/syntaxTests/functionTypes/external_function_type_taking_internal.sol index c3ba80a3b..3e264c8cc 100644 --- a/test/libsolidity/syntaxTests/functionTypes/external_function_type_taking_internal.sol +++ b/test/libsolidity/syntaxTests/functionTypes/external_function_type_taking_internal.sol @@ -1,3 +1,5 @@ contract C { function(function () internal) external x; } +// ---- +// TypeError: (26-47): Internal type cannot be used for external function type. diff --git a/test/libsolidity/syntaxTests/functionTypes/external_function_type_to_address.sol b/test/libsolidity/syntaxTests/functionTypes/external_function_type_to_address.sol index 5f4d40650..b86425dbc 100644 --- a/test/libsolidity/syntaxTests/functionTypes/external_function_type_to_address.sol +++ b/test/libsolidity/syntaxTests/functionTypes/external_function_type_to_address.sol @@ -1,5 +1,5 @@ contract C { - function f() public returns (address) { + function f() public view returns (address) { return address(this.f); } } diff --git a/test/libsolidity/syntaxTests/functionTypes/external_function_type_to_uint.sol b/test/libsolidity/syntaxTests/functionTypes/external_function_type_to_uint.sol index e47abefbf..f42872238 100644 --- a/test/libsolidity/syntaxTests/functionTypes/external_function_type_to_uint.sol +++ b/test/libsolidity/syntaxTests/functionTypes/external_function_type_to_uint.sol @@ -3,3 +3,5 @@ contract C { return uint(this.f); } } +// ---- +// TypeError: (69-81): Explicit type conversion not allowed from "function () external returns (uint256)" to "uint256". diff --git a/test/libsolidity/syntaxTests/functionTypes/function_type.sol b/test/libsolidity/syntaxTests/functionTypes/function_type.sol index a879e351c..23d501367 100644 --- a/test/libsolidity/syntaxTests/functionTypes/function_type.sol +++ b/test/libsolidity/syntaxTests/functionTypes/function_type.sol @@ -1,5 +1,6 @@ contract C { - function f() public { + function f() pure public { function(uint) returns (uint) x; + x; } } diff --git a/test/libsolidity/syntaxTests/functionTypes/function_type_arrays.sol b/test/libsolidity/syntaxTests/functionTypes/function_type_arrays.sol index c402dbff2..ec23d637a 100644 --- a/test/libsolidity/syntaxTests/functionTypes/function_type_arrays.sol +++ b/test/libsolidity/syntaxTests/functionTypes/function_type_arrays.sol @@ -1,7 +1,7 @@ contract C { function(uint) external returns (uint)[] public x; function(uint) internal returns (uint)[10] y; - function f() public { + function f() view public { function(uint) returns (uint)[10] memory a; function(uint) returns (uint)[10] storage b = y; function(uint) external returns (uint)[] memory c; diff --git a/test/libsolidity/syntaxTests/functionTypes/function_type_parameter.sol b/test/libsolidity/syntaxTests/functionTypes/function_type_parameter.sol index c787e6f07..da66ec8a2 100644 --- a/test/libsolidity/syntaxTests/functionTypes/function_type_parameter.sol +++ b/test/libsolidity/syntaxTests/functionTypes/function_type_parameter.sol @@ -1,5 +1,7 @@ contract C { + uint x; function f(function(uint) external returns (uint) g) public returns (function(uint) external returns (uint)) { + x = 2; return g; } } diff --git a/test/libsolidity/syntaxTests/functionTypes/function_type_returned.sol b/test/libsolidity/syntaxTests/functionTypes/function_type_returned.sol index 0590fad4d..9cd313c8f 100644 --- a/test/libsolidity/syntaxTests/functionTypes/function_type_returned.sol +++ b/test/libsolidity/syntaxTests/functionTypes/function_type_returned.sol @@ -1,5 +1,5 @@ contract C { - function f() public returns (function(uint) external returns (uint) g) { + function f() public pure returns (function(uint) pure external returns (uint) g) { return g; } } diff --git a/test/libsolidity/syntaxTests/functionTypes/internal_function_as_external_parameter.sol b/test/libsolidity/syntaxTests/functionTypes/internal_function_as_external_parameter.sol index 3103001d2..fa92d5597 100644 --- a/test/libsolidity/syntaxTests/functionTypes/internal_function_as_external_parameter.sol +++ b/test/libsolidity/syntaxTests/functionTypes/internal_function_as_external_parameter.sol @@ -1,4 +1,8 @@ +// It should not be possible to give internal functions +// as parameters to external functions. contract C { function f(function(uint) internal returns (uint) x) public { } } +// ---- +// TypeError: (124-164): Internal or recursive type is not allowed for public or external functions. diff --git a/test/libsolidity/syntaxTests/functionTypes/internal_function_as_external_parameter_in_library_external.sol b/test/libsolidity/syntaxTests/functionTypes/internal_function_as_external_parameter_in_library_external.sol index 7856d62d2..b37fb285c 100644 --- a/test/libsolidity/syntaxTests/functionTypes/internal_function_as_external_parameter_in_library_external.sol +++ b/test/libsolidity/syntaxTests/functionTypes/internal_function_as_external_parameter_in_library_external.sol @@ -2,3 +2,5 @@ library L { function f(function(uint) internal returns (uint) x) public { } } +// ---- +// TypeError: (27-67): Internal or recursive type is not allowed for public or external functions. diff --git a/test/libsolidity/syntaxTests/functionTypes/internal_function_as_external_parameter_in_library_internal.sol b/test/libsolidity/syntaxTests/functionTypes/internal_function_as_external_parameter_in_library_internal.sol index 13d6d03d8..7ffa447ea 100644 --- a/test/libsolidity/syntaxTests/functionTypes/internal_function_as_external_parameter_in_library_internal.sol +++ b/test/libsolidity/syntaxTests/functionTypes/internal_function_as_external_parameter_in_library_internal.sol @@ -1,4 +1,4 @@ library L { - function f(function(uint) internal returns (uint) x) internal { + function f(function(uint) internal returns (uint) /*x*/) pure internal { } } diff --git a/test/libsolidity/syntaxTests/functionTypes/internal_function_returned_from_public_function.sol b/test/libsolidity/syntaxTests/functionTypes/internal_function_returned_from_public_function.sol index e836a6cc6..41fcd0a44 100644 --- a/test/libsolidity/syntaxTests/functionTypes/internal_function_returned_from_public_function.sol +++ b/test/libsolidity/syntaxTests/functionTypes/internal_function_returned_from_public_function.sol @@ -1,4 +1,7 @@ +// It should not be possible to return internal functions from external functions. contract C { function f() public returns (function(uint) internal returns (uint) x) { } } +// ---- +// TypeError: (129-169): Internal or recursive type is not allowed for public or external functions. diff --git a/test/libsolidity/syntaxTests/functionTypes/internal_function_type_to_address.sol b/test/libsolidity/syntaxTests/functionTypes/internal_function_type_to_address.sol index 4a3f02c1f..b75a0d436 100644 --- a/test/libsolidity/syntaxTests/functionTypes/internal_function_type_to_address.sol +++ b/test/libsolidity/syntaxTests/functionTypes/internal_function_type_to_address.sol @@ -3,3 +3,5 @@ contract C { return address(f); } } +// ---- +// TypeError: (72-82): Explicit type conversion not allowed from "function () returns (address)" to "address". diff --git a/test/libsolidity/syntaxTests/functionTypes/payable_internal_function_type.sol b/test/libsolidity/syntaxTests/functionTypes/payable_internal_function_type.sol index 69efaad4b..a7cb9d92a 100644 --- a/test/libsolidity/syntaxTests/functionTypes/payable_internal_function_type.sol +++ b/test/libsolidity/syntaxTests/functionTypes/payable_internal_function_type.sol @@ -1,3 +1,5 @@ contract C { function (uint) internal payable returns (uint) x; } +// ---- +// TypeError: (17-66): Only external function types can be payable. diff --git a/test/libsolidity/syntaxTests/functionTypes/payable_internal_function_type_is_not_fatal.sol b/test/libsolidity/syntaxTests/functionTypes/payable_internal_function_type_is_not_fatal.sol index fb84f9ca4..5c6dc0561 100644 --- a/test/libsolidity/syntaxTests/functionTypes/payable_internal_function_type_is_not_fatal.sol +++ b/test/libsolidity/syntaxTests/functionTypes/payable_internal_function_type_is_not_fatal.sol @@ -1,7 +1,9 @@ contract C { function (uint) internal payable returns (uint) x; - function g() { + function g() public { x = g; } } +// ---- +// TypeError: (17-66): Only external function types can be payable. diff --git a/test/libsolidity/syntaxTests/functionTypes/private_function_type.sol b/test/libsolidity/syntaxTests/functionTypes/private_function_type.sol index d15430d23..9d4f0a097 100644 --- a/test/libsolidity/syntaxTests/functionTypes/private_function_type.sol +++ b/test/libsolidity/syntaxTests/functionTypes/private_function_type.sol @@ -3,3 +3,5 @@ contract C { function(uint) private returns (uint) x; } } +// ---- +// TypeError: (47-86): Invalid visibility, can only be "external" or "internal". diff --git a/test/libsolidity/syntaxTests/functionTypes/public_function_type.sol b/test/libsolidity/syntaxTests/functionTypes/public_function_type.sol index ecc2493ad..756766d3b 100644 --- a/test/libsolidity/syntaxTests/functionTypes/public_function_type.sol +++ b/test/libsolidity/syntaxTests/functionTypes/public_function_type.sol @@ -3,3 +3,5 @@ contract C { function(uint) public returns (uint) x; } } +// ---- +// TypeError: (47-85): Invalid visibility, can only be "external" or "internal". diff --git a/test/libsolidity/syntaxTests/functionTypes/warn_function_type_parameters_with_names.sol b/test/libsolidity/syntaxTests/functionTypes/warn_function_type_parameters_with_names.sol index 44417f256..072c7eb71 100644 --- a/test/libsolidity/syntaxTests/functionTypes/warn_function_type_parameters_with_names.sol +++ b/test/libsolidity/syntaxTests/functionTypes/warn_function_type_parameters_with_names.sol @@ -1,3 +1,5 @@ contract C { function(uint a) f; } +// ---- +// Warning: (26-32): Naming function type parameters is deprecated. 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 index f8c21fa80..67a74e546 100644 --- 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 @@ -1,3 +1,5 @@ contract C { function(uint) returns (bool ret) f; } +// ---- +// Warning: (41-49): Naming function type return parameters is deprecated. From 6d289783b41109b445fec924354580a79b65a94a Mon Sep 17 00:00:00 2001 From: chriseth Date: Fri, 16 Mar 2018 11:02:35 +0100 Subject: [PATCH 142/197] Fix state variable parsing. --- libsolidity/parsing/Parser.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/libsolidity/parsing/Parser.cpp b/libsolidity/parsing/Parser.cpp index 18ef740a3..bdb46ad63 100644 --- a/libsolidity/parsing/Parser.cpp +++ b/libsolidity/parsing/Parser.cpp @@ -380,6 +380,14 @@ Parser::FunctionHeaderParserResult Parser::parseFunctionHeader( { if (result.visibility != Declaration::Visibility::Default) { + // There is the special case of a public state variable of function type. + // Detect this and return early. + if ( + (result.visibility == Declaration::Visibility::External || result.visibility == Declaration::Visibility::Internal) && + result.modifiers.empty() && + result.name->empty() + ) + break; parserError(string( "Visibility already specified as \"" + Declaration::visibilityToString(result.visibility) + From 42b90ad4c3fc33c2cb1cafb7370b8d10c5fbc957 Mon Sep 17 00:00:00 2001 From: chriseth Date: Fri, 16 Mar 2018 11:45:18 +0100 Subject: [PATCH 143/197] New tests for function state variables. --- ...external_function_type_public_variable.sol | 11 +++++++++ ...function_type_internal_public_variable.sol | 5 ++++ ...nction_type_variable_external_internal.sol | 6 +++++ ...nction_types_internal_visibility_error.sol | 7 ++++++ .../function_types_variable_visibility.sol | 9 ++++++++ .../valid_function_type_variables.sol | 23 +++++++++++++++++++ 6 files changed, 61 insertions(+) create mode 100644 test/libsolidity/syntaxTests/functionTypes/external_function_type_public_variable.sol create mode 100644 test/libsolidity/syntaxTests/functionTypes/function_type_internal_public_variable.sol create mode 100644 test/libsolidity/syntaxTests/functionTypes/function_type_variable_external_internal.sol create mode 100644 test/libsolidity/syntaxTests/functionTypes/function_types_internal_visibility_error.sol create mode 100644 test/libsolidity/syntaxTests/functionTypes/function_types_variable_visibility.sol create mode 100644 test/libsolidity/syntaxTests/functionTypes/valid_function_type_variables.sol diff --git a/test/libsolidity/syntaxTests/functionTypes/external_function_type_public_variable.sol b/test/libsolidity/syntaxTests/functionTypes/external_function_type_public_variable.sol new file mode 100644 index 000000000..0a6d1e534 --- /dev/null +++ b/test/libsolidity/syntaxTests/functionTypes/external_function_type_public_variable.sol @@ -0,0 +1,11 @@ +contract C { + function (uint) external public x; + + function g(uint) public { + x = this.g; + } + function f() public view returns (function(uint) external) { + return this.x(); + } +} +// ---- diff --git a/test/libsolidity/syntaxTests/functionTypes/function_type_internal_public_variable.sol b/test/libsolidity/syntaxTests/functionTypes/function_type_internal_public_variable.sol new file mode 100644 index 000000000..4eb53227b --- /dev/null +++ b/test/libsolidity/syntaxTests/functionTypes/function_type_internal_public_variable.sol @@ -0,0 +1,5 @@ +contract C { + function(bytes memory) internal public a; +} +// ---- +// TypeError: (17-57): Internal or recursive type is not allowed for public state variables. diff --git a/test/libsolidity/syntaxTests/functionTypes/function_type_variable_external_internal.sol b/test/libsolidity/syntaxTests/functionTypes/function_type_variable_external_internal.sol new file mode 100644 index 000000000..b42f69087 --- /dev/null +++ b/test/libsolidity/syntaxTests/functionTypes/function_type_variable_external_internal.sol @@ -0,0 +1,6 @@ +contract test { + function fa(bytes memory) { } + function(bytes memory) external internal a = fa; +} +// ---- +// TypeError: (99-101): Type function (bytes memory) is not implicitly convertible to expected type function (bytes memory) external. diff --git a/test/libsolidity/syntaxTests/functionTypes/function_types_internal_visibility_error.sol b/test/libsolidity/syntaxTests/functionTypes/function_types_internal_visibility_error.sol new file mode 100644 index 000000000..36206d634 --- /dev/null +++ b/test/libsolidity/syntaxTests/functionTypes/function_types_internal_visibility_error.sol @@ -0,0 +1,7 @@ +contract C { + // This is an error, you should explicitly use + // `external public` to fix it - `internal public` does not exist. + function(bytes memory) public a; +} +// ---- +// TypeError: (139-170): Invalid visibility, can only be "external" or "internal". diff --git a/test/libsolidity/syntaxTests/functionTypes/function_types_variable_visibility.sol b/test/libsolidity/syntaxTests/functionTypes/function_types_variable_visibility.sol new file mode 100644 index 000000000..91c2420a8 --- /dev/null +++ b/test/libsolidity/syntaxTests/functionTypes/function_types_variable_visibility.sol @@ -0,0 +1,9 @@ +contract C { + function(bytes memory) a1; + function(bytes memory) internal b1; + function(bytes memory) internal internal b2; + function(bytes memory) external c1; + function(bytes memory) external internal c2; + function(bytes memory) external public c3; +} +// ---- diff --git a/test/libsolidity/syntaxTests/functionTypes/valid_function_type_variables.sol b/test/libsolidity/syntaxTests/functionTypes/valid_function_type_variables.sol new file mode 100644 index 000000000..10c6767c2 --- /dev/null +++ b/test/libsolidity/syntaxTests/functionTypes/valid_function_type_variables.sol @@ -0,0 +1,23 @@ +contract test { + function fa(uint) {} + function fb(uint) internal {} + function fc(uint) internal {} + function fd(uint) external {} + function fe(uint) external {} + function ff(uint) internal {} + function fg(uint) internal pure {} + function fh(uint) pure internal {} + + function(uint) a = fa; + function(uint) internal b = fb; // (explicit internal applies to the function type) + 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; +} +// ---- +// 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. From d64e93332704ade6cc2078c0c37956e1c9e8af9e Mon Sep 17 00:00:00 2001 From: chriseth Date: Fri, 16 Mar 2018 11:57:47 +0100 Subject: [PATCH 144/197] Changelog entry. --- Changelog.md | 1 + 1 file changed, 1 insertion(+) diff --git a/Changelog.md b/Changelog.md index d0af80257..db080b050 100644 --- a/Changelog.md +++ b/Changelog.md @@ -30,6 +30,7 @@ Bugfixes: * Commandline interface: Support ``--evm-version constantinople`` properly. * DocString Parser: Fix error message for empty descriptions. * Gas Estimator: Correctly ignore costs of fallback function for other functions. + * Parser: Fix parsing of getters for function type variables. * Standard JSON: Support ``constantinople`` as ``evmVersion`` properly. * Type Checker: Fix detection of recursive structs. * Type Checker: Fix asymmetry bug when comparing with literal numbers. From 2ad1acaf721072d27783d586048d6377be6c3f99 Mon Sep 17 00:00:00 2001 From: chriseth Date: Fri, 16 Mar 2018 12:39:30 +0100 Subject: [PATCH 145/197] Warn if modifiers are applied to functions without implementation. --- Changelog.md | 1 + libsolidity/analysis/SyntaxChecker.cpp | 7 +++++++ libsolidity/parsing/Parser.cpp | 8 ++++---- .../modifiers_on_abstract_functions_050.sol | 10 ++++++++++ ...ifiers_on_abstract_functions_no_parser_error.sol | 13 +++++++++++++ 5 files changed, 35 insertions(+), 4 deletions(-) create mode 100644 test/libsolidity/syntaxTests/modifiers/modifiers_on_abstract_functions_050.sol create mode 100644 test/libsolidity/syntaxTests/modifiers/modifiers_on_abstract_functions_no_parser_error.sol diff --git a/Changelog.md b/Changelog.md index db080b050..e2174cfd4 100644 --- a/Changelog.md +++ b/Changelog.md @@ -14,6 +14,7 @@ Features: * Optimizer: Optimize across ``mload`` if ``msize()`` is not used. * Static Analyzer: Error on duplicated super constructor calls as experimental 0.5.0 feature. * Syntax Checker: Issue warning for empty structs (or error as experimental 0.5.0 feature). + * Syntax Checker: Warn about modifiers on functions without implementation (this will turn into an error with version 0.5.0). * Syntax Tests: Add source locations to syntax test expectations. * General: Introduce new constructor syntax using the ``constructor`` keyword as experimental 0.5.0 feature. * Inheritance: Error when using empty parentheses for base class constructors that require arguments as experimental 0.5.0 feature. diff --git a/libsolidity/analysis/SyntaxChecker.cpp b/libsolidity/analysis/SyntaxChecker.cpp index 343b4ba81..f648e5b40 100644 --- a/libsolidity/analysis/SyntaxChecker.cpp +++ b/libsolidity/analysis/SyntaxChecker.cpp @@ -232,6 +232,13 @@ bool SyntaxChecker::visit(FunctionDefinition const& _function) "Use \"constructor(...) { ... }\" instead." ); } + if (!_function.isImplemented() && !_function.modifiers().empty()) + { + 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." ); + } return true; } diff --git a/libsolidity/parsing/Parser.cpp b/libsolidity/parsing/Parser.cpp index bdb46ad63..ea092a74c 100644 --- a/libsolidity/parsing/Parser.cpp +++ b/libsolidity/parsing/Parser.cpp @@ -365,12 +365,12 @@ Parser::FunctionHeaderParserResult Parser::parseFunctionHeader( Token::Value token = m_scanner->currentToken(); if (_allowModifiers && token == Token::Identifier) { - // This can either be a modifier (function declaration) or the name of the - // variable (function type name plus variable). - if ( + // If the name is empty, then this can either be a modifier (fallback function declaration) + // or the name of the state variable (function type name plus variable). + if (result.name->empty() && ( m_scanner->peekNextToken() == Token::Semicolon || m_scanner->peekNextToken() == Token::Assign - ) + )) // Variable declaration, break here. break; else diff --git a/test/libsolidity/syntaxTests/modifiers/modifiers_on_abstract_functions_050.sol b/test/libsolidity/syntaxTests/modifiers/modifiers_on_abstract_functions_050.sol new file mode 100644 index 000000000..af1babbcc --- /dev/null +++ b/test/libsolidity/syntaxTests/modifiers/modifiers_on_abstract_functions_050.sol @@ -0,0 +1,10 @@ +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 new file mode 100644 index 000000000..e18c5cf98 --- /dev/null +++ b/test/libsolidity/syntaxTests/modifiers/modifiers_on_abstract_functions_no_parser_error.sol @@ -0,0 +1,13 @@ +// Previous versions of Solidity turned this +// into a parser error (they wrongly recognized +// these functions as state variables of +// function type). +contract C +{ + modifier only_owner() { _; } + function foo() only_owner public; + 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. From 4e1ea0866d889daa1144d7c4d89b16cd03cf7dc2 Mon Sep 17 00:00:00 2001 From: chriseth Date: Fri, 16 Mar 2018 12:45:12 +0100 Subject: [PATCH 146/197] Extract modifier tests. --- .../SolidityNameAndTypeResolution.cpp | 158 ------------------ .../base_constructor_double_invocation.sol | 6 + .../constructor_call_invalid_arg_count.sol | 9 + .../function_modifier_double_invocation.sol | 4 + .../function_modifier_invocation.sol | 5 + ...on_modifier_invocation_local_variables.sol | 4 + ...modifier_invocation_local_variables050.sol | 7 + ...unction_modifier_invocation_parameters.sol | 5 + .../modifiers/function_overrides_modifier.sol | 5 + .../modifiers/illegal_modifier_override.sol | 4 + .../invalid_function_modifier_type.sol | 6 + .../modifiers/legal_modifier_override.sol | 2 + .../modifiers/modifier_overrides_function.sol | 5 + .../modifiers/modifier_returns_value.sol | 6 + .../modifiers/modifier_without_underscore.sol | 5 + 15 files changed, 73 insertions(+), 158 deletions(-) create mode 100644 test/libsolidity/syntaxTests/modifiers/base_constructor_double_invocation.sol create mode 100644 test/libsolidity/syntaxTests/modifiers/constructor_call_invalid_arg_count.sol create mode 100644 test/libsolidity/syntaxTests/modifiers/function_modifier_double_invocation.sol create mode 100644 test/libsolidity/syntaxTests/modifiers/function_modifier_invocation.sol create mode 100644 test/libsolidity/syntaxTests/modifiers/function_modifier_invocation_local_variables.sol create mode 100644 test/libsolidity/syntaxTests/modifiers/function_modifier_invocation_local_variables050.sol create mode 100644 test/libsolidity/syntaxTests/modifiers/function_modifier_invocation_parameters.sol create mode 100644 test/libsolidity/syntaxTests/modifiers/function_overrides_modifier.sol create mode 100644 test/libsolidity/syntaxTests/modifiers/illegal_modifier_override.sol create mode 100644 test/libsolidity/syntaxTests/modifiers/invalid_function_modifier_type.sol create mode 100644 test/libsolidity/syntaxTests/modifiers/legal_modifier_override.sol create mode 100644 test/libsolidity/syntaxTests/modifiers/modifier_overrides_function.sol create mode 100644 test/libsolidity/syntaxTests/modifiers/modifier_returns_value.sol create mode 100644 test/libsolidity/syntaxTests/modifiers/modifier_without_underscore.sol diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index e5e5e246d..18a414e00 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -975,139 +975,6 @@ BOOST_AUTO_TEST_CASE(super_excludes_current_contract) CHECK_ERROR(text, TypeError, "Member \"f\" not found or not visible after argument-dependent lookup in contract super B"); } -BOOST_AUTO_TEST_CASE(function_modifier_invocation) -{ - char const* text = R"( - contract B { - function f() mod1(2, true) mod2("0123456") pure public { } - modifier mod1(uint a, bool b) { if (b) _; } - modifier mod2(bytes7 a) { while (a == "1234567") _; } - } - )"; - CHECK_SUCCESS(text); -} - -BOOST_AUTO_TEST_CASE(invalid_function_modifier_type) -{ - char const* text = R"( - contract B { - function f() mod1(true) public { } - modifier mod1(uint a) { if (a > 0) _; } - } - )"; - CHECK_ERROR(text, TypeError, "Invalid type for argument in modifier invocation. Invalid implicit conversion from bool to uint256 requested."); -} - -BOOST_AUTO_TEST_CASE(function_modifier_invocation_parameters) -{ - char const* text = R"( - contract B { - function f(uint8 a) mod1(a, true) mod2(r) public returns (bytes7 r) { } - modifier mod1(uint a, bool b) { if (b) _; } - modifier mod2(bytes7 a) { while (a == "1234567") _; } - } - )"; - CHECK_SUCCESS(text); -} - -BOOST_AUTO_TEST_CASE(function_modifier_invocation_local_variables) -{ - char const* text = R"( - contract B { - function f() mod(x) pure public { uint x = 7; } - modifier mod(uint a) { if (a > 0) _; } - } - )"; - CHECK_SUCCESS_NO_WARNINGS(text); -} - -BOOST_AUTO_TEST_CASE(function_modifier_invocation_local_variables050) -{ - char const* text = R"( - pragma experimental "v0.5.0"; - contract B { - function f() mod(x) pure public { uint x = 7; } - modifier mod(uint a) { if (a > 0) _; } - } - )"; - CHECK_ERROR(text, DeclarationError, "Undeclared identifier."); -} - -BOOST_AUTO_TEST_CASE(function_modifier_double_invocation) -{ - char const* text = R"( - contract B { - function f(uint x) mod(x) mod(2) public { } - modifier mod(uint a) { if (a > 0) _; } - } - )"; - CHECK_SUCCESS(text); -} - -BOOST_AUTO_TEST_CASE(base_constructor_double_invocation) -{ - char const* text = R"( - contract C { function C(uint a) public {} } - contract B is C { - function B() C(2) C(2) public {} - } - )"; - CHECK_ERROR(text, DeclarationError, "Base constructor already provided"); -} - -BOOST_AUTO_TEST_CASE(legal_modifier_override) -{ - char const* text = R"( - contract A { modifier mod(uint a) { _; } } - contract B is A { modifier mod(uint a) { _; } } - )"; - CHECK_SUCCESS(text); -} - -BOOST_AUTO_TEST_CASE(illegal_modifier_override) -{ - char const* text = R"( - contract A { modifier mod(uint a) { _; } } - contract B is A { modifier mod(uint8 a) { _; } } - )"; - CHECK_ERROR(text, TypeError, "Override changes modifier signature."); -} - -BOOST_AUTO_TEST_CASE(modifier_overrides_function) -{ - char const* text = R"( - contract A { modifier mod(uint a) { _; } } - contract B is A { function mod(uint a) public { } } - )"; - CHECK_ALLOW_MULTI(text, (vector>{ - {Error::Type::DeclarationError, "Identifier already declared"}, - {Error::Type::TypeError, "Override changes modifier to function"} - })); -} - -BOOST_AUTO_TEST_CASE(function_overrides_modifier) -{ - char const* text = R"( - contract A { function mod(uint a) public { } } - contract B is A { modifier mod(uint a) { _; } } - )"; - CHECK_ALLOW_MULTI(text, (vector>{ - {Error::Type::DeclarationError, "Identifier already declared"}, - {Error::Type::TypeError, "Override changes function to modifier"} - })); -} - -BOOST_AUTO_TEST_CASE(modifier_returns_value) -{ - char const* text = R"( - contract A { - function f(uint a) mod(2) public returns (uint r) { } - modifier mod(uint a) { _; return 7; } - } - )"; - CHECK_ERROR(text, TypeError, "Return arguments not allowed."); -} - BOOST_AUTO_TEST_CASE(state_variable_accessors) { char const* text = R"( @@ -4176,21 +4043,6 @@ BOOST_AUTO_TEST_CASE(conditional_with_all_types) CHECK_SUCCESS(text); } -BOOST_AUTO_TEST_CASE(constructor_call_invalid_arg_count) -{ - // This caused a segfault in an earlier version - char const* text = R"( - contract C { - function C(){} - } - contract D is C { - function D() C(5){} - } - )"; - - CHECK_ERROR(text, TypeError, "Wrong argument count for modifier invocation: 1 arguments given but expected 0."); -} - BOOST_AUTO_TEST_CASE(index_access_for_bytes) { char const* text = R"( @@ -5055,16 +4907,6 @@ BOOST_AUTO_TEST_CASE(no_warn_about_callcode_as_function) CHECK_SUCCESS_NO_WARNINGS(text); } -BOOST_AUTO_TEST_CASE(modifier_without_underscore) -{ - char const* text = R"( - contract test { - modifier m() {} - } - )"; - CHECK_ERROR(text, SyntaxError, "Modifier body does not contain '_'."); -} - BOOST_AUTO_TEST_CASE(payable_in_library) { char const* text = R"( diff --git a/test/libsolidity/syntaxTests/modifiers/base_constructor_double_invocation.sol b/test/libsolidity/syntaxTests/modifiers/base_constructor_double_invocation.sol new file mode 100644 index 000000000..a2a825219 --- /dev/null +++ b/test/libsolidity/syntaxTests/modifiers/base_constructor_double_invocation.sol @@ -0,0 +1,6 @@ +contract C { function C(uint a) public {} } +contract B is C { + function B() C(2) C(2) public {} +} +// ---- +// DeclarationError: Base constructor already provided. diff --git a/test/libsolidity/syntaxTests/modifiers/constructor_call_invalid_arg_count.sol b/test/libsolidity/syntaxTests/modifiers/constructor_call_invalid_arg_count.sol new file mode 100644 index 000000000..fac9b319b --- /dev/null +++ b/test/libsolidity/syntaxTests/modifiers/constructor_call_invalid_arg_count.sol @@ -0,0 +1,9 @@ +// This caused a segfault in an earlier version +contract C { + function C() public {} +} +contract D is C { + function D() C(5) public {} +} +// ---- +// TypeError: Wrong argument count for modifier invocation: 1 arguments given but expected 0. diff --git a/test/libsolidity/syntaxTests/modifiers/function_modifier_double_invocation.sol b/test/libsolidity/syntaxTests/modifiers/function_modifier_double_invocation.sol new file mode 100644 index 000000000..756241920 --- /dev/null +++ b/test/libsolidity/syntaxTests/modifiers/function_modifier_double_invocation.sol @@ -0,0 +1,4 @@ +contract B { + function f(uint x) mod(x) mod(2) public pure { } + modifier mod(uint a) { if (a > 0) _; } +} diff --git a/test/libsolidity/syntaxTests/modifiers/function_modifier_invocation.sol b/test/libsolidity/syntaxTests/modifiers/function_modifier_invocation.sol new file mode 100644 index 000000000..e15fcf49d --- /dev/null +++ b/test/libsolidity/syntaxTests/modifiers/function_modifier_invocation.sol @@ -0,0 +1,5 @@ +contract B { + function f() mod1(2, true) mod2("0123456") pure public { } + modifier mod1(uint a, bool b) { if (b) _; } + modifier mod2(bytes7 a) { while (a == "1234567") _; } +} diff --git a/test/libsolidity/syntaxTests/modifiers/function_modifier_invocation_local_variables.sol b/test/libsolidity/syntaxTests/modifiers/function_modifier_invocation_local_variables.sol new file mode 100644 index 000000000..000319245 --- /dev/null +++ b/test/libsolidity/syntaxTests/modifiers/function_modifier_invocation_local_variables.sol @@ -0,0 +1,4 @@ +contract B { + function f() mod(x) pure public { uint x = 7; } + modifier mod(uint a) { if (a > 0) _; } +} diff --git a/test/libsolidity/syntaxTests/modifiers/function_modifier_invocation_local_variables050.sol b/test/libsolidity/syntaxTests/modifiers/function_modifier_invocation_local_variables050.sol new file mode 100644 index 000000000..52439f2b8 --- /dev/null +++ b/test/libsolidity/syntaxTests/modifiers/function_modifier_invocation_local_variables050.sol @@ -0,0 +1,7 @@ +pragma experimental "v0.5.0"; +contract B { + function f() mod(x) pure public { uint x = 7; } + modifier mod(uint a) { if (a > 0) _; } +} +// ---- +// DeclarationError: Undeclared identifier. diff --git a/test/libsolidity/syntaxTests/modifiers/function_modifier_invocation_parameters.sol b/test/libsolidity/syntaxTests/modifiers/function_modifier_invocation_parameters.sol new file mode 100644 index 000000000..de2a8f48e --- /dev/null +++ b/test/libsolidity/syntaxTests/modifiers/function_modifier_invocation_parameters.sol @@ -0,0 +1,5 @@ +contract B { + function f(uint8 a) mod1(a, true) mod2(r) pure public returns (bytes7 r) { } + modifier mod1(uint a, bool b) { if (b) _; } + modifier mod2(bytes7 a) { while (a == "1234567") _; } +} diff --git a/test/libsolidity/syntaxTests/modifiers/function_overrides_modifier.sol b/test/libsolidity/syntaxTests/modifiers/function_overrides_modifier.sol new file mode 100644 index 000000000..25485bec6 --- /dev/null +++ b/test/libsolidity/syntaxTests/modifiers/function_overrides_modifier.sol @@ -0,0 +1,5 @@ +contract A { function mod(uint a) public { } } +contract B is A { modifier mod(uint a) { _; } } +// ---- +// DeclarationError: Identifier already declared. +// TypeError: Override changes function to modifier. diff --git a/test/libsolidity/syntaxTests/modifiers/illegal_modifier_override.sol b/test/libsolidity/syntaxTests/modifiers/illegal_modifier_override.sol new file mode 100644 index 000000000..f77a529b4 --- /dev/null +++ b/test/libsolidity/syntaxTests/modifiers/illegal_modifier_override.sol @@ -0,0 +1,4 @@ +contract A { modifier mod(uint a) { _; } } +contract B is A { modifier mod(uint8 a) { _; } } +// ---- +// TypeError: Override changes modifier signature. diff --git a/test/libsolidity/syntaxTests/modifiers/invalid_function_modifier_type.sol b/test/libsolidity/syntaxTests/modifiers/invalid_function_modifier_type.sol new file mode 100644 index 000000000..8d7d1f9f0 --- /dev/null +++ b/test/libsolidity/syntaxTests/modifiers/invalid_function_modifier_type.sol @@ -0,0 +1,6 @@ +contract B { + function f() mod1(true) public { } + modifier mod1(uint a) { if (a > 0) _; } +} +// ---- +// TypeError: Invalid type for argument in modifier invocation. Invalid implicit conversion from bool to uint256 requested. diff --git a/test/libsolidity/syntaxTests/modifiers/legal_modifier_override.sol b/test/libsolidity/syntaxTests/modifiers/legal_modifier_override.sol new file mode 100644 index 000000000..51c3fd801 --- /dev/null +++ b/test/libsolidity/syntaxTests/modifiers/legal_modifier_override.sol @@ -0,0 +1,2 @@ +contract A { modifier mod(uint a) { _; } } +contract B is A { modifier mod(uint a) { _; } } diff --git a/test/libsolidity/syntaxTests/modifiers/modifier_overrides_function.sol b/test/libsolidity/syntaxTests/modifiers/modifier_overrides_function.sol new file mode 100644 index 000000000..ebf36a0de --- /dev/null +++ b/test/libsolidity/syntaxTests/modifiers/modifier_overrides_function.sol @@ -0,0 +1,5 @@ +contract A { modifier mod(uint a) { _; } } +contract B is A { function mod(uint a) public { } } +// ---- +// DeclarationError: Identifier already declared. +// TypeError: Override changes modifier to function. diff --git a/test/libsolidity/syntaxTests/modifiers/modifier_returns_value.sol b/test/libsolidity/syntaxTests/modifiers/modifier_returns_value.sol new file mode 100644 index 000000000..0ae00b667 --- /dev/null +++ b/test/libsolidity/syntaxTests/modifiers/modifier_returns_value.sol @@ -0,0 +1,6 @@ +contract A { + function f(uint a) mod(2) public returns (uint r) { } + modifier mod(uint a) { _; return 7; } +} +// ---- +// TypeError: Return arguments not allowed. diff --git a/test/libsolidity/syntaxTests/modifiers/modifier_without_underscore.sol b/test/libsolidity/syntaxTests/modifiers/modifier_without_underscore.sol new file mode 100644 index 000000000..ae44c4c77 --- /dev/null +++ b/test/libsolidity/syntaxTests/modifiers/modifier_without_underscore.sol @@ -0,0 +1,5 @@ +contract test { + modifier m() {} +} +// ---- +// SyntaxError: Modifier body does not contain '_'. From 52f68d3b63d65c31215e683899b96b27e2e24ee1 Mon Sep 17 00:00:00 2001 From: chriseth Date: Wed, 11 Apr 2018 17:58:53 +0200 Subject: [PATCH 147/197] Update expectations. --- .../function_type_variable_external_internal.sol | 4 ++-- .../modifiers/base_constructor_double_invocation.sol | 7 ++++--- .../modifiers/constructor_call_invalid_arg_count.sol | 6 +++--- .../function_modifier_invocation_local_variables050.sol | 2 +- .../syntaxTests/modifiers/function_overrides_modifier.sol | 4 ++-- .../syntaxTests/modifiers/illegal_modifier_override.sol | 2 +- .../modifiers/invalid_function_modifier_type.sol | 2 +- .../syntaxTests/modifiers/modifier_overrides_function.sol | 4 ++-- .../syntaxTests/modifiers/modifier_returns_value.sol | 2 +- .../syntaxTests/modifiers/modifier_without_underscore.sol | 2 +- 10 files changed, 18 insertions(+), 17 deletions(-) diff --git a/test/libsolidity/syntaxTests/functionTypes/function_type_variable_external_internal.sol b/test/libsolidity/syntaxTests/functionTypes/function_type_variable_external_internal.sol index b42f69087..f02404724 100644 --- a/test/libsolidity/syntaxTests/functionTypes/function_type_variable_external_internal.sol +++ b/test/libsolidity/syntaxTests/functionTypes/function_type_variable_external_internal.sol @@ -1,6 +1,6 @@ contract test { - function fa(bytes memory) { } + function fa(bytes memory) public { } function(bytes memory) external internal a = fa; } // ---- -// TypeError: (99-101): Type function (bytes memory) is not implicitly convertible to expected type function (bytes memory) external. +// TypeError: (106-108): Type function (bytes memory) is not implicitly convertible to expected type function (bytes memory) external. diff --git a/test/libsolidity/syntaxTests/modifiers/base_constructor_double_invocation.sol b/test/libsolidity/syntaxTests/modifiers/base_constructor_double_invocation.sol index a2a825219..bdbab5d8c 100644 --- a/test/libsolidity/syntaxTests/modifiers/base_constructor_double_invocation.sol +++ b/test/libsolidity/syntaxTests/modifiers/base_constructor_double_invocation.sol @@ -1,6 +1,7 @@ -contract C { function C(uint a) public {} } +contract C { constructor(uint a) public {} } contract B is C { - function B() C(2) C(2) public {} + constructor() C(2) C(2) public {} } // ---- -// DeclarationError: Base constructor already provided. +// Warning: (81-85): Base constructor arguments given twice. +// DeclarationError: (86-90): Base constructor already provided. diff --git a/test/libsolidity/syntaxTests/modifiers/constructor_call_invalid_arg_count.sol b/test/libsolidity/syntaxTests/modifiers/constructor_call_invalid_arg_count.sol index fac9b319b..4a2b5c4a5 100644 --- a/test/libsolidity/syntaxTests/modifiers/constructor_call_invalid_arg_count.sol +++ b/test/libsolidity/syntaxTests/modifiers/constructor_call_invalid_arg_count.sol @@ -1,9 +1,9 @@ // This caused a segfault in an earlier version contract C { - function C() public {} + constructor() public {} } contract D is C { - function D() C(5) public {} + constructor() C(5) public {} } // ---- -// TypeError: Wrong argument count for modifier invocation: 1 arguments given but expected 0. +// TypeError: (127-131): Wrong argument count for modifier invocation: 1 arguments given but expected 0. diff --git a/test/libsolidity/syntaxTests/modifiers/function_modifier_invocation_local_variables050.sol b/test/libsolidity/syntaxTests/modifiers/function_modifier_invocation_local_variables050.sol index 52439f2b8..c19ccf2cf 100644 --- a/test/libsolidity/syntaxTests/modifiers/function_modifier_invocation_local_variables050.sol +++ b/test/libsolidity/syntaxTests/modifiers/function_modifier_invocation_local_variables050.sol @@ -4,4 +4,4 @@ contract B { modifier mod(uint a) { if (a > 0) _; } } // ---- -// DeclarationError: Undeclared identifier. +// DeclarationError: (64-65): Undeclared identifier. diff --git a/test/libsolidity/syntaxTests/modifiers/function_overrides_modifier.sol b/test/libsolidity/syntaxTests/modifiers/function_overrides_modifier.sol index 25485bec6..a64c2790d 100644 --- a/test/libsolidity/syntaxTests/modifiers/function_overrides_modifier.sol +++ b/test/libsolidity/syntaxTests/modifiers/function_overrides_modifier.sol @@ -1,5 +1,5 @@ contract A { function mod(uint a) public { } } contract B is A { modifier mod(uint a) { _; } } // ---- -// DeclarationError: Identifier already declared. -// TypeError: Override changes function to modifier. +// DeclarationError: (65-92): Identifier already declared. +// TypeError: (65-92): Override changes function to modifier. diff --git a/test/libsolidity/syntaxTests/modifiers/illegal_modifier_override.sol b/test/libsolidity/syntaxTests/modifiers/illegal_modifier_override.sol index f77a529b4..958be686c 100644 --- a/test/libsolidity/syntaxTests/modifiers/illegal_modifier_override.sol +++ b/test/libsolidity/syntaxTests/modifiers/illegal_modifier_override.sol @@ -1,4 +1,4 @@ contract A { modifier mod(uint a) { _; } } contract B is A { modifier mod(uint8 a) { _; } } // ---- -// TypeError: Override changes modifier signature. +// TypeError: (61-89): Override changes modifier signature. diff --git a/test/libsolidity/syntaxTests/modifiers/invalid_function_modifier_type.sol b/test/libsolidity/syntaxTests/modifiers/invalid_function_modifier_type.sol index 8d7d1f9f0..c1e3108bd 100644 --- a/test/libsolidity/syntaxTests/modifiers/invalid_function_modifier_type.sol +++ b/test/libsolidity/syntaxTests/modifiers/invalid_function_modifier_type.sol @@ -3,4 +3,4 @@ contract B { modifier mod1(uint a) { if (a > 0) _; } } // ---- -// TypeError: Invalid type for argument in modifier invocation. Invalid implicit conversion from bool to uint256 requested. +// TypeError: (35-39): Invalid type for argument in modifier invocation. Invalid implicit conversion from bool to uint256 requested. diff --git a/test/libsolidity/syntaxTests/modifiers/modifier_overrides_function.sol b/test/libsolidity/syntaxTests/modifiers/modifier_overrides_function.sol index ebf36a0de..a43646c33 100644 --- a/test/libsolidity/syntaxTests/modifiers/modifier_overrides_function.sol +++ b/test/libsolidity/syntaxTests/modifiers/modifier_overrides_function.sol @@ -1,5 +1,5 @@ contract A { modifier mod(uint a) { _; } } contract B is A { function mod(uint a) public { } } // ---- -// DeclarationError: Identifier already declared. -// TypeError: Override changes modifier to function. +// DeclarationError: (61-92): Identifier already declared. +// TypeError: (13-40): Override changes modifier to function. diff --git a/test/libsolidity/syntaxTests/modifiers/modifier_returns_value.sol b/test/libsolidity/syntaxTests/modifiers/modifier_returns_value.sol index 0ae00b667..d22e836c3 100644 --- a/test/libsolidity/syntaxTests/modifiers/modifier_returns_value.sol +++ b/test/libsolidity/syntaxTests/modifiers/modifier_returns_value.sol @@ -3,4 +3,4 @@ contract A { modifier mod(uint a) { _; return 7; } } // ---- -// TypeError: Return arguments not allowed. +// TypeError: (101-109): Return arguments not allowed. diff --git a/test/libsolidity/syntaxTests/modifiers/modifier_without_underscore.sol b/test/libsolidity/syntaxTests/modifiers/modifier_without_underscore.sol index ae44c4c77..6198d3c55 100644 --- a/test/libsolidity/syntaxTests/modifiers/modifier_without_underscore.sol +++ b/test/libsolidity/syntaxTests/modifiers/modifier_without_underscore.sol @@ -2,4 +2,4 @@ contract test { modifier m() {} } // ---- -// SyntaxError: Modifier body does not contain '_'. +// SyntaxError: (33-35): Modifier body does not contain '_'. From b5a696ad48780bf0614eef2a737a2e89963d4640 Mon Sep 17 00:00:00 2001 From: chriseth Date: Wed, 11 Apr 2018 17:44:44 +0200 Subject: [PATCH 148/197] Properly cope with constructor headers. --- libsolidity/parsing/Parser.cpp | 9 ++++++--- .../functionTypes/function_type_constructor.sol | 7 +++++++ .../functionTypes/function_type_constructor_local.sol | 8 ++++++++ 3 files changed, 21 insertions(+), 3 deletions(-) create mode 100644 test/libsolidity/syntaxTests/functionTypes/function_type_constructor.sol create mode 100644 test/libsolidity/syntaxTests/functionTypes/function_type_constructor_local.sol diff --git a/libsolidity/parsing/Parser.cpp b/libsolidity/parsing/Parser.cpp index ea092a74c..2d8ca7d3f 100644 --- a/libsolidity/parsing/Parser.cpp +++ b/libsolidity/parsing/Parser.cpp @@ -365,9 +365,10 @@ Parser::FunctionHeaderParserResult Parser::parseFunctionHeader( Token::Value token = m_scanner->currentToken(); if (_allowModifiers && token == Token::Identifier) { - // If the name is empty, then this can either be a modifier (fallback function declaration) + // If the name is empty (and this is not a constructor), + // then this can either be a modifier (fallback function declaration) // or the name of the state variable (function type name plus variable). - if (result.name->empty() && ( + if ((result.name->empty() && !result.isConstructor) && ( m_scanner->peekNextToken() == Token::Semicolon || m_scanner->peekNextToken() == Token::Assign )) @@ -385,7 +386,7 @@ Parser::FunctionHeaderParserResult Parser::parseFunctionHeader( if ( (result.visibility == Declaration::Visibility::External || result.visibility == Declaration::Visibility::Internal) && result.modifiers.empty() && - result.name->empty() + (result.name->empty() && !result.isConstructor) ) break; parserError(string( @@ -437,6 +438,7 @@ ASTPointer Parser::parseFunctionDefinitionOrFunctionTypeStateVariable(A FunctionHeaderParserResult header = parseFunctionHeader(false, true, _contractName); if ( + header.isConstructor || !header.modifiers.empty() || !header.name->empty() || m_scanner->currentToken() == Token::Semicolon || @@ -802,6 +804,7 @@ ASTPointer Parser::parseFunctionType() RecursionGuard recursionGuard(*this); ASTNodeFactory nodeFactory(*this); FunctionHeaderParserResult header = parseFunctionHeader(true, false); + solAssert(!header.isConstructor, "Tried to parse type as constructor."); return nodeFactory.createNode( header.parameters, header.returnParameters, diff --git a/test/libsolidity/syntaxTests/functionTypes/function_type_constructor.sol b/test/libsolidity/syntaxTests/functionTypes/function_type_constructor.sol new file mode 100644 index 000000000..95ebc1792 --- /dev/null +++ b/test/libsolidity/syntaxTests/functionTypes/function_type_constructor.sol @@ -0,0 +1,7 @@ +contract C { + // Fool parser into parsing a constructor as a function type. + constructor() x; +} +// ---- +// Warning: (83-99): Modifiers of functions without implementation are ignored. +// DeclarationError: (97-98): Undeclared identifier. diff --git a/test/libsolidity/syntaxTests/functionTypes/function_type_constructor_local.sol b/test/libsolidity/syntaxTests/functionTypes/function_type_constructor_local.sol new file mode 100644 index 000000000..b7763d282 --- /dev/null +++ b/test/libsolidity/syntaxTests/functionTypes/function_type_constructor_local.sol @@ -0,0 +1,8 @@ +contract C { + // Fool parser into parsing a constructor as a function type. + function f() { + constructor() x; + } +} +// ---- +// ParserError: (118-118): Expected token Semicolon got 'Identifier' From d56acb68abdda19d0e5a684cdf8d40c3d7ce277e Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Tue, 1 Aug 2017 00:55:13 +0100 Subject: [PATCH 149/197] Add abi.encode, abi.encodePacked, abi.encodeWithSelector and abi.encodeWithSignature. --- libsolidity/analysis/GlobalContext.cpp | 1 + libsolidity/analysis/ViewPureChecker.cpp | 7 +- libsolidity/ast/Types.cpp | 49 +++++++- libsolidity/ast/Types.h | 8 +- libsolidity/codegen/ExpressionCompiler.cpp | 102 +++++++++++++++++ test/libsolidity/SolidityEndToEndTest.cpp | 108 ++++++++++++++++++ .../SolidityNameAndTypeResolution.cpp | 16 +++ 7 files changed, 287 insertions(+), 4 deletions(-) diff --git a/libsolidity/analysis/GlobalContext.cpp b/libsolidity/analysis/GlobalContext.cpp index 6a858d36d..ef1a59fe3 100644 --- a/libsolidity/analysis/GlobalContext.cpp +++ b/libsolidity/analysis/GlobalContext.cpp @@ -35,6 +35,7 @@ namespace solidity GlobalContext::GlobalContext(): m_magicVariables(vector>{ + make_shared("abi", make_shared(MagicType::Kind::ABI)), make_shared("addmod", make_shared(strings{"uint256", "uint256", "uint256"}, strings{"uint256"}, FunctionType::Kind::AddMod, false, StateMutability::Pure)), make_shared("assert", make_shared(strings{"bool"}, strings{}, FunctionType::Kind::Assert, false, StateMutability::Pure)), make_shared("block", make_shared(MagicType::Kind::Block)), diff --git a/libsolidity/analysis/ViewPureChecker.cpp b/libsolidity/analysis/ViewPureChecker.cpp index 13c3ab68e..d98430120 100644 --- a/libsolidity/analysis/ViewPureChecker.cpp +++ b/libsolidity/analysis/ViewPureChecker.cpp @@ -305,10 +305,15 @@ void ViewPureChecker::endVisit(MemberAccess const& _memberAccess) mutability = StateMutability::View; break; case Type::Category::Magic: + { // we can ignore the kind of magic and only look at the name of the member - if (member != "data" && member != "sig" && member != "blockhash") + set static const pureMembers{ + "encode", "encodePacked", "encodeWithSelector", "encodeWithSignature", "data", "sig", "blockhash" + }; + if (!pureMembers.count(member)) mutability = StateMutability::View; break; + } case Type::Category::Struct: { if (_memberAccess.expression().annotation().type->dataStoredIn(DataLocation::Storage)) diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp index 21353080b..68b127770 100644 --- a/libsolidity/ast/Types.cpp +++ b/libsolidity/ast/Types.cpp @@ -2375,7 +2375,11 @@ string FunctionType::richIdentifier() const case Kind::ByteArrayPush: id += "bytearraypush"; break; case Kind::ObjectCreation: id += "objectcreation"; break; case Kind::Assert: id += "assert"; break; - case Kind::Require: id += "require";break; + case Kind::Require: id += "require"; break; + case Kind::ABIEncode: id += "abiencode"; break; + 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; } id += "_" + stateMutabilityToString(m_stateMutability); @@ -2996,6 +3000,8 @@ string MagicType::richIdentifier() const return "t_magic_message"; case Kind::Transaction: return "t_magic_transaction"; + case Kind::ABI: + return "t_magic_abi"; default: solAssert(false, "Unknown kind of magic"); } @@ -3036,6 +3042,45 @@ MemberList::MemberMap MagicType::nativeMembers(ContractDefinition const*) const {"origin", make_shared(160, IntegerType::Modifier::Address)}, {"gasprice", make_shared(256)} }); + case Kind::ABI: + return MemberList::MemberMap({ + {"encode", make_shared( + TypePointers(), + TypePointers{make_shared(DataLocation::Memory)}, + strings{}, + strings{}, + FunctionType::Kind::ABIEncode, + true, + StateMutability::Pure + )}, + {"encodePacked", make_shared( + TypePointers(), + TypePointers{make_shared(DataLocation::Memory)}, + strings{}, + strings{}, + FunctionType::Kind::ABIEncodePacked, + true, + StateMutability::Pure + )}, + {"encodeWithSelector", make_shared( + TypePointers{make_shared(4)}, + TypePointers{make_shared(DataLocation::Memory)}, + strings{}, + strings{}, + FunctionType::Kind::ABIEncodeWithSelector, + true, + StateMutability::Pure + )}, + {"encodeWithSignature", make_shared( + TypePointers{make_shared(DataLocation::Memory, true)}, + TypePointers{make_shared(DataLocation::Memory)}, + strings{}, + strings{}, + FunctionType::Kind::ABIEncodeWithSignature, + true, + StateMutability::Pure + )} + }); default: solAssert(false, "Unknown kind of magic."); } @@ -3051,6 +3096,8 @@ string MagicType::toString(bool) const return "msg"; case Kind::Transaction: return "tx"; + case Kind::ABI: + return "abi"; default: solAssert(false, "Unknown kind of magic."); } diff --git a/libsolidity/ast/Types.h b/libsolidity/ast/Types.h index 05f506f1c..47556c4d7 100644 --- a/libsolidity/ast/Types.h +++ b/libsolidity/ast/Types.h @@ -914,6 +914,10 @@ public: ObjectCreation, ///< array creation using new Assert, ///< assert() Require, ///< require() + ABIEncode, + ABIEncodePacked, + ABIEncodeWithSelector, + ABIEncodeWithSignature, GasLeft ///< gasleft() }; @@ -1049,7 +1053,7 @@ public: ASTPointer 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); } + bool padArguments() const { return !(m_kind == Kind::SHA3 || m_kind == Kind::SHA256 || m_kind == Kind::RIPEMD160 || m_kind == Kind::ABIEncodePacked); } bool takesArbitraryParameters() const { return m_arbitraryParameters; } bool gasSet() const { return m_gasSet; } bool valueSet() const { return m_valueSet; } @@ -1207,7 +1211,7 @@ private: class MagicType: public Type { public: - enum class Kind { Block, Message, Transaction }; + enum class Kind { Block, Message, Transaction, ABI }; virtual Category category() const override { return Category::Magic; } explicit MagicType(Kind _kind): m_kind(_kind) {} diff --git a/libsolidity/codegen/ExpressionCompiler.cpp b/libsolidity/codegen/ExpressionCompiler.cpp index 57d49ac66..051db5362 100644 --- a/libsolidity/codegen/ExpressionCompiler.cpp +++ b/libsolidity/codegen/ExpressionCompiler.cpp @@ -33,6 +33,8 @@ #include #include +#include + using namespace std; namespace dev @@ -912,6 +914,106 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) m_context << success; break; } + case FunctionType::Kind::ABIEncode: + case FunctionType::Kind::ABIEncodePacked: + case FunctionType::Kind::ABIEncodeWithSelector: + case FunctionType::Kind::ABIEncodeWithSignature: + { + bool const isPacked = function.kind() == FunctionType::Kind::ABIEncodePacked; + bool const hasSelectorOrSignature = + function.kind() == FunctionType::Kind::ABIEncodeWithSelector || + function.kind() == FunctionType::Kind::ABIEncodeWithSignature; + + TypePointers argumentTypes; + TypePointers targetTypes; + for (unsigned i = 0; i < arguments.size(); ++i) + { + arguments[i]->accept(*this); + // Do not keep the selector as part of the ABI encoded args + if (!hasSelectorOrSignature || i > 0) + argumentTypes.push_back(arguments[i]->annotation().type); + } + utils().fetchFreeMemoryPointer(); + // stack now: [] .. + + // adjust by 32(+4) bytes to accommodate the length(+selector) + m_context << u256(32 + (hasSelectorOrSignature ? 4 : 0)) << Instruction::ADD; + // stack now: [] .. + + if (isPacked) + { + solAssert(!function.padArguments(), ""); + utils().packedEncode(argumentTypes, TypePointers()); + } + else + { + solAssert(function.padArguments(), ""); + utils().abiEncode(argumentTypes, TypePointers()); + } + utils().fetchFreeMemoryPointer(); + // stack: [] + + // size is end minus start minus length slot + m_context.appendInlineAssembly(R"({ + mstore(mem_ptr, sub(sub(mem_end, mem_ptr), 0x20)) + })", {"mem_end", "mem_ptr"}); + m_context << Instruction::SWAP1; + utils().storeFreeMemoryPointer(); + // stack: [] + + if (hasSelectorOrSignature) + { + // stack: + solAssert(arguments.size() >= 1, ""); + TypePointer const& selectorType = arguments[0]->annotation().type; + utils().moveIntoStack(selectorType->sizeOnStack()); + TypePointer dataOnStack = selectorType; + // stack: + if (function.kind() == FunctionType::Kind::ABIEncodeWithSignature) + { + // hash the signature + if (auto const* stringType = dynamic_cast(selectorType.get())) + { + FixedHash<4> hash(dev::keccak256(stringType->value())); + m_context << (u256(FixedHash<4>::Arith(hash)) << (256 - 32)); + dataOnStack = make_shared(4); + } + else + { + utils().fetchFreeMemoryPointer(); + // stack: + utils().packedEncode(TypePointers{selectorType}, TypePointers()); + utils().toSizeAfterFreeMemoryPointer(); + m_context << Instruction::KECCAK256; + // stack: + + dataOnStack = make_shared(32); + } + } + else + { + solAssert(function.kind() == FunctionType::Kind::ABIEncodeWithSelector, ""); + } + + // Cleanup actually does not clean on shrinking the type. + utils().convertType(*dataOnStack, FixedBytesType(4), true); + + // stack: + + // load current memory, mask and combine the selector + string mask = formatNumber((u256(-1) >> 32)); + m_context.appendInlineAssembly(R"({ + let data_start := add(mem_ptr, 0x20) + let data := mload(data_start) + let mask := )" + mask + R"( + mstore(data_start, or(and(data, mask), and(selector, not(mask)))) + })", {"mem_ptr", "selector"}); + m_context << Instruction::POP; + } + + // stack now: + break; + } case FunctionType::Kind::GasLeft: m_context << Instruction::GAS; break; diff --git a/test/libsolidity/SolidityEndToEndTest.cpp b/test/libsolidity/SolidityEndToEndTest.cpp index 39f4b03e9..a5b990ace 100644 --- a/test/libsolidity/SolidityEndToEndTest.cpp +++ b/test/libsolidity/SolidityEndToEndTest.cpp @@ -3557,6 +3557,45 @@ BOOST_AUTO_TEST_CASE(empty_name_return_parameter) ABI_CHECK(callContractFunction("f(uint256)", 9), encodeArgs(9)); } +BOOST_AUTO_TEST_CASE(sha256_empty) +{ + char const* sourceCode = R"( + contract C { + function f() returns (bytes32) { + return sha256(); + } + } + )"; + compileAndRun(sourceCode); + ABI_CHECK(callContractFunction("f()"), fromHex("0xe3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855")); +} + +BOOST_AUTO_TEST_CASE(ripemd160_empty) +{ + char const* sourceCode = R"( + contract C { + function f() returns (bytes20) { + return ripemd160(); + } + } + )"; + compileAndRun(sourceCode); + ABI_CHECK(callContractFunction("f()"), fromHex("0x9c1185a5c5e9fc54612808977ee8f548b2258d31000000000000000000000000")); +} + +BOOST_AUTO_TEST_CASE(keccak256_empty) +{ + char const* sourceCode = R"( + contract C { + function f() returns (bytes32) { + return keccak256(); + } + } + )"; + compileAndRun(sourceCode); + ABI_CHECK(callContractFunction("f()"), fromHex("0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470")); +} + BOOST_AUTO_TEST_CASE(keccak256_multiple_arguments) { char const* sourceCode = R"( @@ -11052,6 +11091,75 @@ BOOST_AUTO_TEST_CASE(snark) BOOST_CHECK(callContractFunction("verifyTx()") == encodeArgs(true)); } +BOOST_AUTO_TEST_CASE(abi_encode) +{ + char const* sourceCode = R"( + contract C { + function f() returns (bytes) { + return abi.encode(1, 2); + } + function g() returns (bytes) { + return abi.encodePacked(uint256(1), uint256(2)); + } + } + )"; + compileAndRun(sourceCode, 0, "C"); + ABI_CHECK(callContractFunction("f()"), encodeArgs(u256(0x20), u256(0x40), u256(1), u256(2))); + ABI_CHECK(callContractFunction("g()"), encodeArgs(u256(0x20), u256(0x40), u256(1), u256(2))); +} + +BOOST_AUTO_TEST_CASE(abi_encode_complex_call) +{ + char const* sourceCode = R"T( + contract C { + function f(uint8 a, string b, string c) { + require(a == 42); + require(bytes(b).length == 2); + require(bytes(b)[0] == 72); // 'H' + require(bytes(b)[1] == 101); // 'e' + require(keccak256(b) == keccak256(c)); + } + function g(uint8 a, string b) returns (bool) { + bytes request = abi.encode( + bytes4(keccak256(abi.encodePacked("f(uint8,string)"))), + a, + b, + "He" + ); + return this.call(request); + } + } + )T"; + compileAndRun(sourceCode, 0, "C"); + ABI_CHECK(callContractFunction("g(uint8,string)", u256(42), u256(0x40), u256(2), string("He")), encodeArgs(u256(1))); +} + +BOOST_AUTO_TEST_CASE(abi_encodewithselector_complex_call) +{ + char const* sourceCode = R"T( + contract C { + function f(uint8 a, string b, string c) { + require(a == 42); + require(bytes(b).length == 2); + require(bytes(b)[0] == 72); // 'H' + require(bytes(b)[1] == 101); // 'e' + require(keccak256(b) == keccak256(c)); + } + function g(uint8 a, string b) returns (bool) { + bytes request = abi.encodeWithSignature( + "f(uint8,string)", + a, + b, + "He" + ); + return this.call(request); + } + } + )T"; + compileAndRun(sourceCode, 0, "C"); + ABI_CHECK(callContractFunction("g(uint8,string)", u256(42), u256(0x40), u256(2), string("He")), encodeArgs(u256(1))); +} + BOOST_AUTO_TEST_CASE(staticcall_for_view_and_pure) { char const* sourceCode = R"( diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index 18a414e00..7678bf4a9 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -7120,6 +7120,22 @@ BOOST_AUTO_TEST_CASE(tight_packing_literals) } )"; CHECK_WARNING(text, "The type of \"int_const 1\" was inferred as uint8."); + text = R"( + contract C { + function f() pure public returns (bytes) { + return abi.encode(1); + } + } + )"; + CHECK_SUCCESS_NO_WARNINGS(text); + text = R"( + contract C { + function f() pure public returns (bytes) { + return abi.encodePacked(1); + } + } + )"; + CHECK_WARNING(text, "The type of \"int_const 1\" was inferred as uint8."); } BOOST_AUTO_TEST_CASE(non_external_fallback) From 932915633bb7f5985023f80a8c704cf8c5f979fa Mon Sep 17 00:00:00 2001 From: Daniel Kirchner Date: Wed, 11 Apr 2018 23:20:40 +0200 Subject: [PATCH 150/197] Add tests for literal operations as well. --- .../syntaxTests/literalOperations/division_by_zero.sol | 5 +++++ .../literalOperations/division_by_zero_complex.sol | 5 +++++ test/libsolidity/syntaxTests/literalOperations/mod_zero.sol | 5 +++++ .../syntaxTests/literalOperations/mod_zero_complex.sol | 5 +++++ 4 files changed, 20 insertions(+) create mode 100644 test/libsolidity/syntaxTests/literalOperations/division_by_zero.sol create mode 100644 test/libsolidity/syntaxTests/literalOperations/division_by_zero_complex.sol create mode 100644 test/libsolidity/syntaxTests/literalOperations/mod_zero.sol create mode 100644 test/libsolidity/syntaxTests/literalOperations/mod_zero_complex.sol diff --git a/test/libsolidity/syntaxTests/literalOperations/division_by_zero.sol b/test/libsolidity/syntaxTests/literalOperations/division_by_zero.sol new file mode 100644 index 000000000..b52b4c512 --- /dev/null +++ b/test/libsolidity/syntaxTests/literalOperations/division_by_zero.sol @@ -0,0 +1,5 @@ +contract C { + uint constant a = 1 / 0; +} +// ---- +// TypeError: (35-40): Operator / not compatible with types int_const 1 and int_const 0 diff --git a/test/libsolidity/syntaxTests/literalOperations/division_by_zero_complex.sol b/test/libsolidity/syntaxTests/literalOperations/division_by_zero_complex.sol new file mode 100644 index 000000000..8cc3b6f2a --- /dev/null +++ b/test/libsolidity/syntaxTests/literalOperations/division_by_zero_complex.sol @@ -0,0 +1,5 @@ +contract C { + uint constant a = 1 / ((1+3)-4); +} +// ---- +// TypeError: (35-48): Operator / not compatible with types int_const 1 and int_const 0 diff --git a/test/libsolidity/syntaxTests/literalOperations/mod_zero.sol b/test/libsolidity/syntaxTests/literalOperations/mod_zero.sol new file mode 100644 index 000000000..1bbbc3fc9 --- /dev/null +++ b/test/libsolidity/syntaxTests/literalOperations/mod_zero.sol @@ -0,0 +1,5 @@ +contract C { + uint constant b3 = 1 % 0; +} +// ---- +// TypeError: (36-41): Operator % not compatible with types int_const 1 and int_const 0 diff --git a/test/libsolidity/syntaxTests/literalOperations/mod_zero_complex.sol b/test/libsolidity/syntaxTests/literalOperations/mod_zero_complex.sol new file mode 100644 index 000000000..4899cac37 --- /dev/null +++ b/test/libsolidity/syntaxTests/literalOperations/mod_zero_complex.sol @@ -0,0 +1,5 @@ +contract C { + uint constant b3 = 1 % (-4+((2)*2)); +} +// ---- +// TypeError: (36-52): Operator % not compatible with types int_const 1 and int_const 0 From 44c0d7ca5e1d97027e4f1e24fb02fd805eda578d Mon Sep 17 00:00:00 2001 From: chriseth Date: Wed, 21 Mar 2018 14:45:32 +0100 Subject: [PATCH 151/197] Tests. --- test/libsolidity/SolidityEndToEndTest.cpp | 217 +++++++++++++++--- .../SolidityNameAndTypeResolution.cpp | 63 ----- .../syntaxTests/tight_packing_literals.sol | 25 ++ .../tight_packing_literals_fine.sol | 11 + 4 files changed, 216 insertions(+), 100 deletions(-) create mode 100644 test/libsolidity/syntaxTests/tight_packing_literals.sol create mode 100644 test/libsolidity/syntaxTests/tight_packing_literals_fine.sol diff --git a/test/libsolidity/SolidityEndToEndTest.cpp b/test/libsolidity/SolidityEndToEndTest.cpp index a5b990ace..8b0e3f83f 100644 --- a/test/libsolidity/SolidityEndToEndTest.cpp +++ b/test/libsolidity/SolidityEndToEndTest.cpp @@ -11095,69 +11095,212 @@ BOOST_AUTO_TEST_CASE(abi_encode) { char const* sourceCode = R"( contract C { - function f() returns (bytes) { + function f0() returns (bytes) { + return abi.encode(); + } + function f1() returns (bytes) { return abi.encode(1, 2); } - function g() returns (bytes) { - return abi.encodePacked(uint256(1), uint256(2)); + function f2() returns (bytes) { + string memory x = "abc"; + return abi.encode(1, x, 2); + } + function f3() returns (bytes r) { + // test that memory is properly allocated + string memory x = "abc"; + r = abi.encode(1, x, 2); + bytes memory y = "def"; + require(y[0] == "d"); + y[0] = "e"; + require(y[0] == "e"); } } )"; compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("f()"), encodeArgs(u256(0x20), u256(0x40), u256(1), u256(2))); - ABI_CHECK(callContractFunction("g()"), encodeArgs(u256(0x20), u256(0x40), u256(1), u256(2))); + ABI_CHECK(callContractFunction("f0()"), encodeArgs(0x20, 0)); + ABI_CHECK(callContractFunction("f1()"), encodeArgs(0x20, 0x40, 1, 2)); + ABI_CHECK(callContractFunction("f2()"), encodeArgs(0x20, 0xa0, 1, 0x60, 2, 3, "abc")); + ABI_CHECK(callContractFunction("f3()"), encodeArgs(0x20, 0xa0, 1, 0x60, 2, 3, "abc")); } -BOOST_AUTO_TEST_CASE(abi_encode_complex_call) +BOOST_AUTO_TEST_CASE(abi_encode_v2) +{ + char const* sourceCode = R"( + pragma experimental ABIEncoderV2; + contract C { + struct S { uint a; uint[] b; } + function f0() public pure returns (bytes) { + return abi.encode(); + } + function f1() public pure returns (bytes) { + return abi.encode(1, 2); + } + function f2() public pure returns (bytes) { + string memory x = "abc"; + return abi.encode(1, x, 2); + } + function f3() public pure returns (bytes r) { + // test that memory is properly allocated + string memory x = "abc"; + r = abi.encode(1, x, 2); + bytes memory y = "def"; + require(y[0] == "d"); + y[0] = "e"; + require(y[0] == "e"); + } + S s; + function f4() public view returns (bytes r) { + string memory x = "abc"; + s.a = 7; + s.b.push(2); + s.b.push(3); + r = abi.encode(1, x, s, 2); + bytes memory y = "def"; + require(y[0] == "d"); + y[0] = "e"; + require(y[0] == "e"); + } + } + )"; + compileAndRun(sourceCode, 0, "C"); + ABI_CHECK(callContractFunction("f0()"), encodeArgs(0x20, 0)); + ABI_CHECK(callContractFunction("f1()"), encodeArgs(0x20, 0x40, 1, 2)); + ABI_CHECK(callContractFunction("f2()"), encodeArgs(0x20, 0xa0, 1, 0x60, 2, 3, "abc")); + ABI_CHECK(callContractFunction("f3()"), encodeArgs(0x20, 0xa0, 1, 0x60, 2, 3, "abc")); + ABI_CHECK(callContractFunction("f4()"), encodeArgs(0x20, 0x160, 1, 0x80, 0xc0, 2, 3, "abc", 7, 0x40, 2, 2, 3)); +} + + +BOOST_AUTO_TEST_CASE(abi_encodePacked) +{ + char const* sourceCode = R"( + contract C { + function f0() public pure returns (bytes) { + return abi.encodePacked(); + } + function f1() public pure returns (bytes) { + return abi.encodePacked(uint8(1), uint8(2)); + } + function f2() public pure returns (bytes) { + string memory x = "abc"; + return abi.encodePacked(uint8(1), x, uint8(2)); + } + function f3() public pure returns (bytes r) { + // test that memory is properly allocated + string memory x = "abc"; + r = abi.encodePacked(uint8(1), x, uint8(2)); + bytes memory y = "def"; + require(y[0] == "d"); + y[0] = "e"; + require(y[0] == "e"); + } + } + )"; + compileAndRun(sourceCode, 0, "C"); + ABI_CHECK(callContractFunction("f0()"), encodeArgs(0x20, 0)); + ABI_CHECK(callContractFunction("f1()"), encodeArgs(0x20, 2, "\x01\x02")); + ABI_CHECK(callContractFunction("f2()"), encodeArgs(0x20, 5, "\x01" "abc" "\x02")); + ABI_CHECK(callContractFunction("f3()"), encodeArgs(0x20, 5, "\x01" "abc" "\x02")); +} + +BOOST_AUTO_TEST_CASE(abi_encode_with_selector) +{ + char const* sourceCode = R"( + contract C { + function f0() public pure returns (bytes) { + return abi.encodeWithSelector(0x12345678); + } + function f1() public pure returns (bytes) { + return abi.encodeWithSelector(0x12345678, "abc"); + } + function f2() public pure returns (bytes) { + bytes4 x = 0x12345678; + return abi.encodeWithSelector(x, "abc"); + } + function f3() public pure returns (bytes) { + bytes4 x = 0x12345678; + return abi.encodeWithSelector(x, uint(-1)); + } + } + )"; + compileAndRun(sourceCode, 0, "C"); + ABI_CHECK(callContractFunction("f0()"), encodeArgs(0x20, 4, "\x12\x34\x56\x78")); + bytes expectation; + expectation = encodeArgs(0x20, 4 + 0x60) + bytes{0x12, 0x34, 0x56, 0x78} + encodeArgs(0x20, 3, "abc") + bytes(0x20 - 4); + ABI_CHECK(callContractFunction("f1()"), expectation); + expectation = encodeArgs(0x20, 4 + 0x60) + bytes{0x12, 0x34, 0x56, 0x78} + encodeArgs(0x20, 3, "abc") + bytes(0x20 - 4); + ABI_CHECK(callContractFunction("f2()"), expectation); + expectation = encodeArgs(0x20, 4 + 0x20) + bytes{0x12, 0x34, 0x56, 0x78} + encodeArgs(u256(-1)) + bytes(0x20 - 4); + ABI_CHECK(callContractFunction("f3()"), expectation); +} + +BOOST_AUTO_TEST_CASE(abi_encode_with_signature) { char const* sourceCode = R"T( contract C { - function f(uint8 a, string b, string c) { - require(a == 42); - require(bytes(b).length == 2); - require(bytes(b)[0] == 72); // 'H' - require(bytes(b)[1] == 101); // 'e' - require(keccak256(b) == keccak256(c)); + function f0() public pure returns (bytes) { + return abi.encodeWithSignature("f(uint256)"); } - function g(uint8 a, string b) returns (bool) { - bytes request = abi.encode( - bytes4(keccak256(abi.encodePacked("f(uint8,string)"))), - a, - b, - "He" - ); - return this.call(request); + function f1() public pure returns (bytes) { + string memory x = "f(uint256)"; + return abi.encodeWithSignature(x, "abc"); + } + string xstor; + function f1s() public returns (bytes) { + xstor = "f(uint256)"; + return abi.encodeWithSignature(xstor, "abc"); + } + function f2() public pure returns (bytes r, uint[] ar) { + string memory x = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua."; + uint[] memory y = new uint[](4); + y[0] = uint(-1); + y[1] = uint(-2); + y[2] = uint(-3); + y[3] = uint(-4); + r = abi.encodeWithSignature(x, y); + // The hash uses temporary memory. This allocation re-uses the memory + // and should initialize it properly. + ar = new uint[](2); } } )T"; compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("g(uint8,string)", u256(42), u256(0x40), u256(2), string("He")), encodeArgs(u256(1))); + ABI_CHECK(callContractFunction("f0()"), encodeArgs(0x20, 4, "\xb3\xde\x64\x8b")); + bytes expectation; + expectation = encodeArgs(0x20, 4 + 0x60) + bytes{0xb3, 0xde, 0x64, 0x8b} + encodeArgs(0x20, 3, "abc") + bytes(0x20 - 4); + ABI_CHECK(callContractFunction("f1()"), expectation); + ABI_CHECK(callContractFunction("f1s()"), expectation); + expectation = + encodeArgs(0x40, 0x140, 4 + 0xc0) + + (bytes{0xe9, 0xc9, 0x21, 0xcd} + encodeArgs(0x20, 4, u256(-1), u256(-2), u256(-3), u256(-4)) + bytes(0x20 - 4)) + + encodeArgs(2, 0, 0); + ABI_CHECK(callContractFunction("f2()"), expectation); } -BOOST_AUTO_TEST_CASE(abi_encodewithselector_complex_call) +BOOST_AUTO_TEST_CASE(abi_encode_call) { char const* sourceCode = R"T( contract C { - function f(uint8 a, string b, string c) { - require(a == 42); - require(bytes(b).length == 2); - require(bytes(b)[0] == 72); // 'H' - require(bytes(b)[1] == 101); // 'e' - require(keccak256(b) == keccak256(c)); + bool x; + function c(uint a, uint[] b) public { + require(a == 5); + require(b.length == 2); + require(b[0] == 6); + require(b[1] == 7); + x = true; } - function g(uint8 a, string b) returns (bool) { - bytes request = abi.encodeWithSignature( - "f(uint8,string)", - a, - b, - "He" - ); - return this.call(request); + function f() public returns (bool) { + uint a = 5; + uint[] memory b = new uint[](2); + b[0] = 6; + b[1] = 7; + require(this.call(abi.encodeWithSignature("c(uint256,uint256[])", a, b))); + return x; } } )T"; compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("g(uint8,string)", u256(42), u256(0x40), u256(2), string("He")), encodeArgs(u256(1))); + ABI_CHECK(callContractFunction("f()"), encodeArgs(true)); } BOOST_AUTO_TEST_CASE(staticcall_for_view_and_pure) diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index 7678bf4a9..a301e6d2e 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -7075,69 +7075,6 @@ BOOST_AUTO_TEST_CASE(reject_interface_constructors) CHECK_ERROR(text, TypeError, "Wrong argument count for constructor call: 1 arguments given but expected 0."); } -BOOST_AUTO_TEST_CASE(tight_packing_literals) -{ - char const* text = R"( - contract C { - function f() pure public returns (bytes32) { - return keccak256(1); - } - } - )"; - CHECK_WARNING(text, "The type of \"int_const 1\" was inferred as uint8."); - text = R"( - contract C { - function f() pure public returns (bytes32) { - return keccak256(uint8(1)); - } - } - )"; - CHECK_SUCCESS_NO_WARNINGS(text); - text = R"( - contract C { - function f() pure public returns (bytes32) { - return sha3(1); - } - } - )"; - CHECK_WARNING_ALLOW_MULTI(text, (std::vector{ - "The type of \"int_const 1\" was inferred as uint8.", - "\"sha3\" has been deprecated in favour of \"keccak256\"" - })); - text = R"( - contract C { - function f() pure public returns (bytes32) { - return sha256(1); - } - } - )"; - CHECK_WARNING(text, "The type of \"int_const 1\" was inferred as uint8."); - text = R"( - contract C { - function f() pure public returns (bytes32) { - return ripemd160(1); - } - } - )"; - CHECK_WARNING(text, "The type of \"int_const 1\" was inferred as uint8."); - text = R"( - contract C { - function f() pure public returns (bytes) { - return abi.encode(1); - } - } - )"; - CHECK_SUCCESS_NO_WARNINGS(text); - text = R"( - contract C { - function f() pure public returns (bytes) { - return abi.encodePacked(1); - } - } - )"; - CHECK_WARNING(text, "The type of \"int_const 1\" was inferred as uint8."); -} - BOOST_AUTO_TEST_CASE(non_external_fallback) { char const* text = R"( diff --git a/test/libsolidity/syntaxTests/tight_packing_literals.sol b/test/libsolidity/syntaxTests/tight_packing_literals.sol new file mode 100644 index 000000000..8258a8a6c --- /dev/null +++ b/test/libsolidity/syntaxTests/tight_packing_literals.sol @@ -0,0 +1,25 @@ +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); + } +} + +// ---- +// 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: (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: (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: (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: (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. diff --git a/test/libsolidity/syntaxTests/tight_packing_literals_fine.sol b/test/libsolidity/syntaxTests/tight_packing_literals_fine.sol new file mode 100644 index 000000000..46407f718 --- /dev/null +++ b/test/libsolidity/syntaxTests/tight_packing_literals_fine.sol @@ -0,0 +1,11 @@ +contract C { + function f() pure public returns (bytes32) { + return keccak256(uint8(1)); + } + function g() pure public returns (bytes) { + return abi.encode(1); + } + function h() pure public returns (bytes) { + return abi.encodePacked(uint8(1)); + } +} From c4a6a63f362f478414ac886ed6e5e159038460aa Mon Sep 17 00:00:00 2001 From: chriseth Date: Wed, 21 Mar 2018 18:38:06 +0100 Subject: [PATCH 152/197] Tests for view and pure. --- .../viewPure/view_pure_abi_encode.sol | 8 +++++ .../view_pure_abi_encode_arguments.sol | 36 +++++++++++++++++++ 2 files changed, 44 insertions(+) create mode 100644 test/libsolidity/syntaxTests/viewPure/view_pure_abi_encode.sol create mode 100644 test/libsolidity/syntaxTests/viewPure/view_pure_abi_encode_arguments.sol diff --git a/test/libsolidity/syntaxTests/viewPure/view_pure_abi_encode.sol b/test/libsolidity/syntaxTests/viewPure/view_pure_abi_encode.sol new file mode 100644 index 000000000..ca7db42e4 --- /dev/null +++ b/test/libsolidity/syntaxTests/viewPure/view_pure_abi_encode.sol @@ -0,0 +1,8 @@ +contract C { + function f() pure public returns (bytes r) { + r = abi.encode(1, 2); + r = abi.encodePacked(f()); + r = abi.encodeWithSelector(0x12345678, 1); + r = abi.encodeWithSignature("f(uint256)", 4); + } +} diff --git a/test/libsolidity/syntaxTests/viewPure/view_pure_abi_encode_arguments.sol b/test/libsolidity/syntaxTests/viewPure/view_pure_abi_encode_arguments.sol new file mode 100644 index 000000000..547362c3f --- /dev/null +++ b/test/libsolidity/syntaxTests/viewPure/view_pure_abi_encode_arguments.sol @@ -0,0 +1,36 @@ +contract C { + uint x; + function gView() public view returns (uint) { return x; } + function gNonPayable() public returns (uint) { x = 4; return 0; } + + function f1() view public returns (bytes) { + return abi.encode(gView()); + } + function f2() view public returns (bytes) { + return abi.encodePacked(gView()); + } + function f3() view public returns (bytes) { + return abi.encodeWithSelector(0x12345678, gView()); + } + function f4() view public returns (bytes) { + return abi.encodeWithSignature("f(uint256)", gView()); + } + function g1() public returns (bytes) { + return abi.encode(gNonPayable()); + } + function g2() public returns (bytes) { + return abi.encodePacked(gNonPayable()); + } + function g3() public returns (bytes) { + return abi.encodeWithSelector(0x12345678, gNonPayable()); + } + function g4() public returns (bytes) { + return abi.encodeWithSignature("f(uint256)", gNonPayable()); + } + // This will generate the only warning. + function check() public returns (bytes) { + return abi.encode(2); + } +} +// ---- +// Warning: (1044-1121): Function state mutability can be restricted to pure From 7343c4028365d3fbc9fa46fca6078971a2bed426 Mon Sep 17 00:00:00 2001 From: chriseth Date: Wed, 21 Mar 2018 19:07:15 +0100 Subject: [PATCH 153/197] Check partial function parameters if rest is arbitrary. --- libsolidity/analysis/TypeChecker.cpp | 17 ++++++++++++++--- ...ary_parameters_but_restricted_first_type.sol | 13 +++++++++++++ 2 files changed, 27 insertions(+), 3 deletions(-) create mode 100644 test/libsolidity/syntaxTests/functionCalls/arbitrary_parameters_but_restricted_first_type.sol diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index 8b57fc158..df08598c9 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -1688,7 +1688,19 @@ bool TypeChecker::visit(FunctionCall const& _functionCall) } } - if (!functionType->takesArbitraryParameters() && parameterTypes.size() != arguments.size()) + if (functionType->takesArbitraryParameters() && arguments.size() < parameterTypes.size()) + { + 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()) + + "." + ); + } + else if (!functionType->takesArbitraryParameters() && parameterTypes.size() != arguments.size()) { bool isStructConstructorCall = _functionCall.annotation().kind == FunctionCallKind::StructConstructorCall; @@ -1711,11 +1723,10 @@ bool TypeChecker::visit(FunctionCall const& _functionCall) } else if (isPositionalCall) { - // call by positional arguments for (size_t i = 0; i < arguments.size(); ++i) { auto const& argType = type(*arguments[i]); - if (functionType->takesArbitraryParameters()) + if (functionType->takesArbitraryParameters() && i >= parameterTypes.size()) { bool errored = false; if (auto t = dynamic_cast(argType.get())) diff --git a/test/libsolidity/syntaxTests/functionCalls/arbitrary_parameters_but_restricted_first_type.sol b/test/libsolidity/syntaxTests/functionCalls/arbitrary_parameters_but_restricted_first_type.sol new file mode 100644 index 000000000..94da5881d --- /dev/null +++ b/test/libsolidity/syntaxTests/functionCalls/arbitrary_parameters_but_restricted_first_type.sol @@ -0,0 +1,13 @@ +contract C { + function f() pure public { + abi.encodeWithSelector(); + abi.encodeWithSignature(); + abi.encodeWithSelector(uint(2), 2); + abi.encodeWithSignature(uint(2), 2); + } +} +// ---- +// TypeError: (52-76): Need at least 1 arguments for function call, but provided only 0. +// TypeError: (86-111): Need at least 1 arguments for function call, but provided only 0. +// TypeError: (144-151): Invalid type for argument in function call. Invalid implicit conversion from uint256 to bytes4 requested. +// TypeError: (189-196): Invalid type for argument in function call. Invalid implicit conversion from uint256 to string memory requested. From 2192e4035ab1ec9a8d1f1d2ebc9c918c5b725dcb Mon Sep 17 00:00:00 2001 From: Daniel Kirchner Date: Fri, 6 Apr 2018 14:13:58 +0200 Subject: [PATCH 154/197] Update version pragmas from >0.4.21 to ^0.4.22. --- docs/contracts.rst | 4 ++-- std/StandardToken.sol | 2 +- std/owned.sol | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/contracts.rst b/docs/contracts.rst index 0dd9845c0..6798444c1 100644 --- a/docs/contracts.rst +++ b/docs/contracts.rst @@ -40,7 +40,7 @@ This means that cyclic creation dependencies are impossible. :: - pragma solidity >0.4.21; + pragma solidity ^0.4.22; contract OwnedToken { // TokenCreator is a contract type that is defined below. @@ -983,7 +983,7 @@ default constructor: ``contructor() public {}``. :: - pragma solidity >0.4.21; + pragma solidity ^0.4.22; contract A { uint public a; diff --git a/std/StandardToken.sol b/std/StandardToken.sol index 5afc9747c..ca0658f2c 100644 --- a/std/StandardToken.sol +++ b/std/StandardToken.sol @@ -1,4 +1,4 @@ -pragma solidity >0.4.21; +pragma solidity ^0.4.22; import "./Token.sol"; diff --git a/std/owned.sol b/std/owned.sol index 8e1d59172..75007f3ec 100644 --- a/std/owned.sol +++ b/std/owned.sol @@ -1,4 +1,4 @@ -pragma solidity >0.4.21; +pragma solidity ^0.4.22; contract owned { address owner; From d42476e241489447e5dc4f5b1fafc8000e635fbc Mon Sep 17 00:00:00 2001 From: Daniel Kirchner Date: Thu, 12 Apr 2018 10:39:13 +0200 Subject: [PATCH 155/197] Add test to check ConstantEvaluator for pure non-rational functions. --- .../syntaxTests/constants/pure_non_rational.sol | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 test/libsolidity/syntaxTests/constants/pure_non_rational.sol diff --git a/test/libsolidity/syntaxTests/constants/pure_non_rational.sol b/test/libsolidity/syntaxTests/constants/pure_non_rational.sol new file mode 100644 index 000000000..4b96f1c7d --- /dev/null +++ b/test/libsolidity/syntaxTests/constants/pure_non_rational.sol @@ -0,0 +1,11 @@ +// Tests that the ConstantEvaluator does not crash for pure non-rational functions. +// Currently it does not evaluate such functions, but this may change in the future +// causing a division by zero error for a. +contract C { + uint constant a = 1 / (uint(keccak256([0])[0]) - uint(keccak256([0])[0])); + uint constant b = 1 / uint(keccak256([0])); + uint constant c = uint(keccak256([0])); + uint[c] mem; +} +// ---- +// TypeError: (392-393): Invalid array length, expected integer literal or constant expression. From 17fc0f54b5dbf6b49e0500e3fca7b681316071f6 Mon Sep 17 00:00:00 2001 From: chriseth Date: Sat, 30 Dec 2017 13:44:09 +0100 Subject: [PATCH 156/197] Use FunctionTypePointer (adds ``const``). --- libsolidity/ast/AST.cpp | 7 ++++--- libsolidity/ast/AST.h | 8 ++++---- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/libsolidity/ast/AST.cpp b/libsolidity/ast/AST.cpp index d8ad009d1..80f5d6424 100644 --- a/libsolidity/ast/AST.cpp +++ b/libsolidity/ast/AST.cpp @@ -297,7 +297,7 @@ ContractDefinition::ContractKind FunctionDefinition::inContractKind() const return contractDef->contractKind(); } -shared_ptr FunctionDefinition::functionType(bool _internal) const +FunctionTypePointer FunctionDefinition::functionType(bool _internal) const { if (_internal) { @@ -338,6 +338,7 @@ shared_ptr FunctionDefinition::functionType(bool _internal) const TypePointer FunctionDefinition::type() const { + solAssert(visibility() != Declaration::Visibility::External, ""); return make_shared(*this); } @@ -379,7 +380,7 @@ TypePointer EventDefinition::type() const return make_shared(*this); } -std::shared_ptr EventDefinition::functionType(bool _internal) const +FunctionTypePointer EventDefinition::functionType(bool _internal) const { if (_internal) return make_shared(*this); @@ -484,7 +485,7 @@ TypePointer VariableDeclaration::type() const return annotation().type; } -shared_ptr VariableDeclaration::functionType(bool _internal) const +FunctionTypePointer VariableDeclaration::functionType(bool _internal) const { if (_internal) return {}; diff --git a/libsolidity/ast/AST.h b/libsolidity/ast/AST.h index ae253f0c8..b648e08b2 100644 --- a/libsolidity/ast/AST.h +++ b/libsolidity/ast/AST.h @@ -218,7 +218,7 @@ public: /// @param _internal false indicates external interface is concerned, true indicates internal interface is concerned. /// @returns null when it is not accessible as a function. - virtual std::shared_ptr functionType(bool /*_internal*/) const { return {}; } + virtual FunctionTypePointer functionType(bool /*_internal*/) const { return {}; } protected: virtual Visibility defaultVisibility() const { return Visibility::Public; } @@ -634,7 +634,7 @@ public: /// @param _internal false indicates external interface is concerned, true indicates internal interface is concerned. /// @returns null when it is not accessible as a function. - virtual std::shared_ptr functionType(bool /*_internal*/) const override; + virtual FunctionTypePointer functionType(bool /*_internal*/) const override; virtual FunctionDefinitionAnnotation& annotation() const override; @@ -703,7 +703,7 @@ public: /// @param _internal false indicates external interface is concerned, true indicates internal interface is concerned. /// @returns null when it is not accessible as a function. - virtual std::shared_ptr functionType(bool /*_internal*/) const override; + virtual FunctionTypePointer functionType(bool /*_internal*/) const override; virtual VariableDeclarationAnnotation& annotation() const override; @@ -805,7 +805,7 @@ public: bool isAnonymous() const { return m_anonymous; } virtual TypePointer type() const override; - virtual std::shared_ptr functionType(bool /*_internal*/) const override; + virtual FunctionTypePointer functionType(bool /*_internal*/) const override; virtual EventDefinitionAnnotation& annotation() const override; From f00bb4359375cd03e9ff6373b5efa41a37ce2876 Mon Sep 17 00:00:00 2001 From: chriseth Date: Sat, 30 Dec 2017 13:46:53 +0100 Subject: [PATCH 157/197] Allow function overloads involving MagicVariableDeclarations. --- libsolidity/analysis/DeclarationContainer.cpp | 8 +++++++- libsolidity/analysis/NameAndTypeResolver.cpp | 5 +++-- libsolidity/analysis/TypeChecker.cpp | 7 +++---- libsolidity/ast/AST.h | 5 +++++ 4 files changed, 18 insertions(+), 7 deletions(-) diff --git a/libsolidity/analysis/DeclarationContainer.cpp b/libsolidity/analysis/DeclarationContainer.cpp index c7ba78d60..786272e45 100644 --- a/libsolidity/analysis/DeclarationContainer.cpp +++ b/libsolidity/analysis/DeclarationContainer.cpp @@ -45,7 +45,8 @@ Declaration const* DeclarationContainer::conflictingDeclaration( if ( dynamic_cast(&_declaration) || - dynamic_cast(&_declaration) + dynamic_cast(&_declaration) || + dynamic_cast(&_declaration) ) { // check that all other declarations with the same name are functions or a public state variable or events. @@ -68,6 +69,11 @@ Declaration const* DeclarationContainer::conflictingDeclaration( !dynamic_cast(declaration) ) return declaration; + if ( + dynamic_cast(&_declaration) && + !dynamic_cast(declaration) + ) + return declaration; // Or, continue. } } diff --git a/libsolidity/analysis/NameAndTypeResolver.cpp b/libsolidity/analysis/NameAndTypeResolver.cpp index 2f6751351..5f9e0e995 100644 --- a/libsolidity/analysis/NameAndTypeResolver.cpp +++ b/libsolidity/analysis/NameAndTypeResolver.cpp @@ -202,8 +202,9 @@ vector NameAndTypeResolver::cleanedDeclarations( solAssert( dynamic_cast(declaration) || dynamic_cast(declaration) || - dynamic_cast(declaration), - "Found overloading involving something not a function or a variable." + dynamic_cast(declaration) || + dynamic_cast(declaration), + "Found overloading involving something not a function, event or a (magic) variable." ); FunctionTypePointer functionType { declaration->functionType(false) }; diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index 8b57fc158..4b1ef55b9 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -2124,10 +2124,9 @@ bool TypeChecker::visit(Identifier const& _identifier) for (Declaration const* declaration: annotation.overloadedDeclarations) { - TypePointer function = declaration->type(); - solAssert(!!function, "Requested type not present."); - auto const* functionType = dynamic_cast(function.get()); - if (functionType && functionType->canTakeArguments(*annotation.argumentTypes)) + FunctionTypePointer functionType = declaration->functionType(true); + solAssert(!!functionType, "Requested type not present."); + if (functionType->canTakeArguments(*annotation.argumentTypes)) candidates.push_back(declaration); } if (candidates.empty()) diff --git a/libsolidity/ast/AST.h b/libsolidity/ast/AST.h index b648e08b2..a53987bf5 100644 --- a/libsolidity/ast/AST.h +++ b/libsolidity/ast/AST.h @@ -831,6 +831,11 @@ public: solAssert(false, "MagicVariableDeclaration used inside real AST."); } + virtual FunctionTypePointer functionType(bool) const override + { + solAssert(m_type->category() == Type::Category::Function, ""); + return std::dynamic_pointer_cast(m_type); + } virtual TypePointer type() const override { return m_type; } private: From 8ab7dc036aafd5781118fc16f05df5b0a8a5550e Mon Sep 17 00:00:00 2001 From: chriseth Date: Sat, 30 Dec 2017 13:47:15 +0100 Subject: [PATCH 158/197] Register overload for ``revert()`` that can receive a reason string. --- libsolidity/analysis/GlobalContext.cpp | 1 + libsolidity/analysis/NameAndTypeResolver.cpp | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/libsolidity/analysis/GlobalContext.cpp b/libsolidity/analysis/GlobalContext.cpp index 6a858d36d..822674aff 100644 --- a/libsolidity/analysis/GlobalContext.cpp +++ b/libsolidity/analysis/GlobalContext.cpp @@ -52,6 +52,7 @@ m_magicVariables(vector>{ make_shared("now", make_shared(256)), make_shared("require", make_shared(strings{"bool"}, strings{}, FunctionType::Kind::Require, false, StateMutability::Pure)), make_shared("revert", make_shared(strings(), strings(), FunctionType::Kind::Revert, false, StateMutability::Pure)), + make_shared("revert", make_shared(strings{"string memory"}, strings(), FunctionType::Kind::Revert, false, StateMutability::Pure)), make_shared("ripemd160", make_shared(strings(), strings{"bytes20"}, FunctionType::Kind::RIPEMD160, true, StateMutability::Pure)), make_shared("selfdestruct", make_shared(strings{"address"}, strings{}, FunctionType::Kind::Selfdestruct)), make_shared("sha256", make_shared(strings(), strings{"bytes32"}, FunctionType::Kind::SHA256, true, StateMutability::Pure)), diff --git a/libsolidity/analysis/NameAndTypeResolver.cpp b/libsolidity/analysis/NameAndTypeResolver.cpp index 5f9e0e995..0a356f04e 100644 --- a/libsolidity/analysis/NameAndTypeResolver.cpp +++ b/libsolidity/analysis/NameAndTypeResolver.cpp @@ -47,7 +47,9 @@ NameAndTypeResolver::NameAndTypeResolver( if (!m_scopes[nullptr]) m_scopes[nullptr].reset(new DeclarationContainer()); for (Declaration const* declaration: _globals) - m_scopes[nullptr]->registerDeclaration(*declaration); + { + solAssert(m_scopes[nullptr]->registerDeclaration(*declaration), "Unable to register global declaration."); + } } bool NameAndTypeResolver::registerDeclarations(SourceUnit& _sourceUnit, ASTNode const* _currentScope) From 012ab37fe3984a692dcdda6ef516e4588a8721b3 Mon Sep 17 00:00:00 2001 From: chriseth Date: Sat, 30 Dec 2017 13:47:51 +0100 Subject: [PATCH 159/197] Code generator for revert with reason string. --- libsolidity/codegen/ExpressionCompiler.cpp | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/libsolidity/codegen/ExpressionCompiler.cpp b/libsolidity/codegen/ExpressionCompiler.cpp index 57d49ac66..dc9fae21a 100644 --- a/libsolidity/codegen/ExpressionCompiler.cpp +++ b/libsolidity/codegen/ExpressionCompiler.cpp @@ -680,8 +680,25 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) m_context << Instruction::SELFDESTRUCT; break; case FunctionType::Kind::Revert: - m_context.appendRevert(); + { + if (!arguments.empty()) + { + solAssert(arguments.size() == 1, ""); + solAssert(function.parameterTypes().size() == 1, ""); + m_context << u256(0); + arguments.front()->accept(*this); + utils().fetchFreeMemoryPointer(); + utils().abiEncode( + {make_shared(256), arguments.front()->annotation().type}, + {make_shared(256), make_shared(DataLocation::Memory, true)} + ); + utils().toSizeAfterFreeMemoryPointer(); + m_context << Instruction::REVERT; + } + else + m_context.appendRevert(); break; + } case FunctionType::Kind::SHA3: { TypePointers argumentTypes; From a06249c98484f8ca3772b09f66bc68dd30c13c4d Mon Sep 17 00:00:00 2001 From: chriseth Date: Sat, 30 Dec 2017 13:48:07 +0100 Subject: [PATCH 160/197] Tests for revert with reason string. --- test/libsolidity/SolidityEndToEndTest.cpp | 37 +++++++++++++++++++ .../SolidityNameAndTypeResolution.cpp | 17 ++++++++- 2 files changed, 53 insertions(+), 1 deletion(-) diff --git a/test/libsolidity/SolidityEndToEndTest.cpp b/test/libsolidity/SolidityEndToEndTest.cpp index f7f1062d1..82ad2917d 100644 --- a/test/libsolidity/SolidityEndToEndTest.cpp +++ b/test/libsolidity/SolidityEndToEndTest.cpp @@ -10419,6 +10419,43 @@ BOOST_AUTO_TEST_CASE(revert) ABI_CHECK(callContractFunction("a()"), encodeArgs(u256(42))); } +BOOST_AUTO_TEST_CASE(revert_with_cause) +{ + char const* sourceCode = R"( + contract D { + function f() public { + revert("test123"); + } + function g() public { + revert("test1234567890123456789012345678901234567890"); + } + } + contract C { + D d = new D(); + function forward(address target, bytes data) internal returns (bool success, bytes retval) { + uint retsize; + assembly { + success := call(not(0), target, 0, add(data, 0x20), mload(data), 0, 0) + retsize := returndatasize() + } + retval = new bytes(retsize); + assembly { + returndatacopy(add(retval, 0x20), 0, returndatasize()) + } + } + function f() public returns (bool, bytes) { + return forward(address(d), msg.data); + } + function g() public returns (bool, bytes) { + return forward(address(d), msg.data); + } + } + )"; + compileAndRun(sourceCode, 0, "C"); + ABI_CHECK(callContractFunction("f()"), encodeArgs(0, 0x40, 0x80, 0, 0x40, 7, "test123")); + ABI_CHECK(callContractFunction("g()"), encodeArgs(0, 0x40, 0xa0, 0, 0x40, 44, "test1234567890123456789012345678901234567890")); +} + BOOST_AUTO_TEST_CASE(negative_stack_height) { // This code was causing negative stack height during code generation diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index 6b6c86a1e..2bf718867 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -5987,7 +5987,22 @@ BOOST_AUTO_TEST_CASE(bare_revert) } } )"; - CHECK_WARNING(text, "Statement has no effect."); + 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) From 3da16b3e8af41a1d743a94d2e19822e82440a63d Mon Sep 17 00:00:00 2001 From: chriseth Date: Sat, 30 Dec 2017 13:46:02 +0100 Subject: [PATCH 161/197] Documentation for revert with reason string. --- Changelog.md | 1 + docs/control-structures.rst | 26 ++++++++++++++++++++++++-- docs/miscellaneous.rst | 1 + docs/units-and-global-variables.rst | 2 ++ 4 files changed, 28 insertions(+), 2 deletions(-) diff --git a/Changelog.md b/Changelog.md index 6288e8488..60f28db40 100644 --- a/Changelog.md +++ b/Changelog.md @@ -5,6 +5,7 @@ Features: * Code Generator: More specialized and thus optimized implementation for ``x.push(...)`` * Commandline interface: Error when missing or inaccessible file detected. Suppress it with the ``--ignore-missing`` flag. * Constant Evaluator: Fix evaluation of single element tuples. + * General: Allow providing reason string for ``revert()``. * General: Limit the number of errors output in a single run to 256. * General: Support accessing dynamic return data in post-byzantium EVMs. * Interfaces: Allow overriding external functions in interfaces with public in an implementing contract. diff --git a/docs/control-structures.rst b/docs/control-structures.rst index 40070a207..18a025725 100644 --- a/docs/control-structures.rst +++ b/docs/control-structures.rst @@ -455,8 +455,9 @@ The ``require`` function should be used to ensure valid conditions, such as inpu If used properly, analysis tools can evaluate your contract to identify the conditions and function calls which will reach a failing ``assert``. Properly functioning code should never reach a failing assert statement; if this happens there is a bug in your contract which you should fix. There are two other ways to trigger exceptions: The ``revert`` function can be used to flag an error and -revert the current call. In the future it might be possible to also include details about the error -in a call to ``revert``. The ``throw`` keyword can also be used as an alternative to ``revert()``. +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 ``throw`` keyword can also be used as an alternative to ``revert()``, but is deprecated. .. note:: From version 0.4.13 the ``throw`` keyword is deprecated and will be phased out in the future. @@ -515,3 +516,24 @@ the EVM to revert all changes made to the state. The reason for reverting is tha did not occur. Because we want to retain the atomicity of transactions, the safest thing to do is to revert all changes and make the whole transaction (or at least call) without effect. Note that ``assert``-style exceptions consume all gas available to the call, while ``require``-style exceptions will not consume any gas starting from the Metropolis release. + +The following example shows how an error string can be used together with revert: + +:: + + pragma solidity ^0.4.0; + + contract VendingMachine { + function buy(uint amount) payable { + if (amount > msg.value / 2 ether) + revert("Not enough Ether provided."); + // Perform the purchase. + } + } + +The provided string will be abi-encoded together with a uint value that will always be zero. +This means that if a string ``x`` is provided, ``(0, x)`` will be encoded as if a function with arguments +``(uint256, string)`` would be called. This zero is used as a version identifier. Future extensions +of this feature might provide actual exception payload of dynamic type and this number could be used +to encode the type or also as a simple numeric error code. Until this is specified, a zero will +be used in all cases. \ No newline at end of file diff --git a/docs/miscellaneous.rst b/docs/miscellaneous.rst index 20400aa26..c5178b511 100644 --- a/docs/miscellaneous.rst +++ b/docs/miscellaneous.rst @@ -334,6 +334,7 @@ Global Variables - ``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) - ``revert()``: abort execution and revert state changes +- ``revert(string)``: 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 ` - ``sha3(...) returns (bytes32)``: an alias to ``keccak256`` diff --git a/docs/units-and-global-variables.rst b/docs/units-and-global-variables.rst index e7f41ed15..aad00ba2d 100644 --- a/docs/units-and-global-variables.rst +++ b/docs/units-and-global-variables.rst @@ -101,6 +101,8 @@ Error Handling throws if the condition is not met - to be used for errors in inputs or external components. ``revert()``: abort execution and revert state changes +``revert(string reason)``: + abort execution and revert state changes, providing an explanatory string .. index:: keccak256, ripemd160, sha256, ecrecover, addmod, mulmod, cryptography, From ae1d040285d97c2be0eb9d3e94a983975459f879 Mon Sep 17 00:00:00 2001 From: chriseth Date: Sat, 30 Dec 2017 14:35:45 +0100 Subject: [PATCH 162/197] Allow error string for ``require``. --- libsolidity/analysis/GlobalContext.cpp | 1 + libsolidity/codegen/ExpressionCompiler.cpp | 26 ++++++++++ test/libsolidity/SolidityEndToEndTest.cpp | 56 ++++++++++++++++++++++ 3 files changed, 83 insertions(+) diff --git a/libsolidity/analysis/GlobalContext.cpp b/libsolidity/analysis/GlobalContext.cpp index 822674aff..c58d99fb3 100644 --- a/libsolidity/analysis/GlobalContext.cpp +++ b/libsolidity/analysis/GlobalContext.cpp @@ -51,6 +51,7 @@ m_magicVariables(vector>{ make_shared("mulmod", make_shared(strings{"uint256", "uint256", "uint256"}, strings{"uint256"}, FunctionType::Kind::MulMod, false, StateMutability::Pure)), make_shared("now", make_shared(256)), make_shared("require", make_shared(strings{"bool"}, strings{}, FunctionType::Kind::Require, false, StateMutability::Pure)), + make_shared("require", make_shared(strings{"bool", "string memory"}, strings{}, FunctionType::Kind::Require, false, StateMutability::Pure)), make_shared("revert", make_shared(strings(), strings(), FunctionType::Kind::Revert, false, StateMutability::Pure)), make_shared("revert", make_shared(strings{"string memory"}, strings(), FunctionType::Kind::Revert, false, StateMutability::Pure)), make_shared("ripemd160", make_shared(strings(), strings{"bytes20"}, FunctionType::Kind::RIPEMD160, true, StateMutability::Pure)), diff --git a/libsolidity/codegen/ExpressionCompiler.cpp b/libsolidity/codegen/ExpressionCompiler.cpp index dc9fae21a..cb92b030b 100644 --- a/libsolidity/codegen/ExpressionCompiler.cpp +++ b/libsolidity/codegen/ExpressionCompiler.cpp @@ -917,16 +917,42 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) { arguments.front()->accept(*this); utils().convertType(*arguments.front()->annotation().type, *function.parameterTypes().front(), false); + if (arguments.size() > 1) + { + // Users probably expect the second argument to be evaluated + // even if the condition is false, as would be the case for an actual + // function call. + solAssert(arguments.size() == 2, ""); + solAssert(function.kind() == FunctionType::Kind::Require, ""); + arguments.at(1)->accept(*this); + utils().moveIntoStack(1, arguments.at(1)->annotation().type->sizeOnStack()); + } + // Stack: // jump if condition was met m_context << Instruction::ISZERO << Instruction::ISZERO; auto success = m_context.appendConditionalJump(); if (function.kind() == FunctionType::Kind::Assert) // condition was not met, flag an error m_context.appendInvalid(); + else if (arguments.size() > 1) + { + m_context << u256(0); + utils().moveIntoStack(arguments.at(1)->annotation().type->sizeOnStack(), 1); + utils().fetchFreeMemoryPointer(); + utils().abiEncode( + {make_shared(256), arguments.at(1)->annotation().type}, + {make_shared(256), make_shared(DataLocation::Memory, true)} + ); + utils().toSizeAfterFreeMemoryPointer(); + m_context << Instruction::REVERT; + m_context.adjustStackOffset(arguments.at(1)->annotation().type->sizeOnStack()); + } else m_context.appendRevert(); // the success branch m_context << success; + if (arguments.size() > 1) + utils().popStackElement(*arguments.at(1)->annotation().type); break; } case FunctionType::Kind::GasLeft: diff --git a/test/libsolidity/SolidityEndToEndTest.cpp b/test/libsolidity/SolidityEndToEndTest.cpp index 82ad2917d..b6b26f496 100644 --- a/test/libsolidity/SolidityEndToEndTest.cpp +++ b/test/libsolidity/SolidityEndToEndTest.cpp @@ -10456,6 +10456,62 @@ BOOST_AUTO_TEST_CASE(revert_with_cause) ABI_CHECK(callContractFunction("g()"), encodeArgs(0, 0x40, 0xa0, 0, 0x40, 44, "test1234567890123456789012345678901234567890")); } +BOOST_AUTO_TEST_CASE(require_with_message) +{ + char const* sourceCode = R"( + contract D { + bool flag = false; + string storageError = "abc"; + function f(uint x) public { + require(x > 7, "failed"); + } + function g() public { + // As a side-effect of internalFun, the flag will be set to true + // (even if the condition is true), + // but it will only throw in the next evaluation. + bool flagCopy = flag; + require(flagCopy == false, internalFun()); + } + function internalFun() returns (string) { + flag = true; + return "only on second run"; + } + function h() public { + require(false, storageError); + } + } + contract C { + D d = new D(); + function forward(address target, bytes data) internal returns (bool success, bytes retval) { + uint retsize; + assembly { + success := call(not(0), target, 0, add(data, 0x20), mload(data), 0, 0) + retsize := returndatasize() + } + retval = new bytes(retsize); + assembly { + returndatacopy(add(retval, 0x20), 0, returndatasize()) + } + } + function f(uint x) public returns (bool, bytes) { + return forward(address(d), msg.data); + } + function g() public returns (bool, bytes) { + return forward(address(d), msg.data); + } + function h() public returns (bool, bytes) { + return forward(address(d), msg.data); + } + } + )"; + compileAndRun(sourceCode, 0, "C"); + ABI_CHECK(callContractFunction("f(uint256)", 8), encodeArgs(1, 0x40, 0)); + ABI_CHECK(callContractFunction("f(uint256)", 5), encodeArgs(0, 0x40, 0x80, 0, 0x40, 6, "failed")); + ABI_CHECK(callContractFunction("g()"), encodeArgs(1, 0x40, 0)); + ABI_CHECK(callContractFunction("g()"), encodeArgs(0, 0x40, 0x80, 0, 0x40, 18 , "only on second run")); + ABI_CHECK(callContractFunction("h()"), encodeArgs(0, 0x40, 0x80, 0, 0x40, 3, "abc")); +} + BOOST_AUTO_TEST_CASE(negative_stack_height) { // This code was causing negative stack height during code generation From 7a9ee69e986cf58c8b2a6cabec2c59b0eb2fbb57 Mon Sep 17 00:00:00 2001 From: chriseth Date: Sat, 30 Dec 2017 20:13:41 +0100 Subject: [PATCH 163/197] Bubble up error messages. --- libsolidity/codegen/CompilerContext.cpp | 18 ++++++++--- libsolidity/codegen/CompilerContext.h | 5 +-- libsolidity/codegen/CompilerUtils.cpp | 1 + libsolidity/codegen/ContractCompiler.cpp | 2 ++ libsolidity/codegen/ExpressionCompiler.cpp | 9 ++++-- test/libsolidity/SolidityEndToEndTest.cpp | 37 ++++++++++++++++++++++ 6 files changed, 62 insertions(+), 10 deletions(-) diff --git a/libsolidity/codegen/CompilerContext.cpp b/libsolidity/codegen/CompilerContext.cpp index 473330466..2cd256f3d 100644 --- a/libsolidity/codegen/CompilerContext.cpp +++ b/libsolidity/codegen/CompilerContext.cpp @@ -262,12 +262,20 @@ CompilerContext& CompilerContext::appendRevert() return *this << u256(0) << u256(0) << Instruction::REVERT; } -CompilerContext& CompilerContext::appendConditionalRevert() +CompilerContext& CompilerContext::appendConditionalRevert(bool _forwardReturnData) { - *this << Instruction::ISZERO; - eth::AssemblyItem afterTag = appendConditionalJump(); - appendRevert(); - *this << afterTag; + if (_forwardReturnData) + appendInlineAssembly(R"({ + if condition { + returndatacopy(0, 0, returndatasize()) + revert(0, returndatasize()) + } + })", {"condition"}); + else + appendInlineAssembly(R"({ + if condition { revert(0, 0) } + })", {"condition"}); + *this << Instruction::POP; return *this; } diff --git a/libsolidity/codegen/CompilerContext.h b/libsolidity/codegen/CompilerContext.h index 7b6632774..c6f2f3be5 100644 --- a/libsolidity/codegen/CompilerContext.h +++ b/libsolidity/codegen/CompilerContext.h @@ -156,8 +156,9 @@ public: CompilerContext& appendConditionalInvalid(); /// Appends a REVERT(0, 0) call CompilerContext& appendRevert(); - /// Appends a conditional REVERT(0, 0) call - CompilerContext& appendConditionalRevert(); + /// Appends a conditional REVERT-call, either forwarding the RETURNDATA or providing the + /// empty string. Consumes the condition. + CompilerContext& appendConditionalRevert(bool _forwardReturnData = false); /// Appends a JUMP to a specific tag CompilerContext& appendJumpTo(eth::AssemblyItem const& _tag) { m_asm->appendJump(_tag); return *this; } /// Appends pushing of a new tag and @returns the new tag. diff --git a/libsolidity/codegen/CompilerUtils.cpp b/libsolidity/codegen/CompilerUtils.cpp index 79aef7b06..34337d7d7 100644 --- a/libsolidity/codegen/CompilerUtils.cpp +++ b/libsolidity/codegen/CompilerUtils.cpp @@ -691,6 +691,7 @@ void CompilerUtils::convertType( solAssert(enumType.numberOfMembers() > 0, "empty enum should have caused a parser error."); m_context << u256(enumType.numberOfMembers() - 1) << Instruction::DUP2 << Instruction::GT; if (_asPartOfArgumentDecoding) + // TODO: error message? m_context.appendConditionalRevert(); else m_context.appendConditionalInvalid(); diff --git a/libsolidity/codegen/ContractCompiler.cpp b/libsolidity/codegen/ContractCompiler.cpp index 5cb371035..0889ac7ce 100644 --- a/libsolidity/codegen/ContractCompiler.cpp +++ b/libsolidity/codegen/ContractCompiler.cpp @@ -128,6 +128,7 @@ void ContractCompiler::appendCallValueCheck() { // Throw if function is not payable but call contained ether. m_context << Instruction::CALLVALUE; + // TODO: error message? m_context.appendConditionalRevert(); } @@ -327,6 +328,7 @@ void ContractCompiler::appendFunctionSelector(ContractDefinition const& _contrac m_context << Instruction::STOP; } else + // TODO: error message here? m_context.appendRevert(); for (auto const& it: interfaceFunctions) diff --git a/libsolidity/codegen/ExpressionCompiler.cpp b/libsolidity/codegen/ExpressionCompiler.cpp index cb92b030b..3f521f2d6 100644 --- a/libsolidity/codegen/ExpressionCompiler.cpp +++ b/libsolidity/codegen/ExpressionCompiler.cpp @@ -608,7 +608,8 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) m_context << Instruction::CREATE; // Check if zero (out of stack or not enough balance). m_context << Instruction::DUP1 << Instruction::ISZERO; - m_context.appendConditionalRevert(); + // TODO: Can we bubble up here? There might be different reasons for failure, I think. + m_context.appendConditionalRevert(true); if (function.valueSet()) m_context << swapInstruction(1) << Instruction::POP; break; @@ -670,8 +671,9 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) if (function.kind() == FunctionType::Kind::Transfer) { // Check if zero (out of stack or not enough balance). + // TODO: bubble up here, but might also be different error. m_context << Instruction::ISZERO; - m_context.appendConditionalRevert(); + m_context.appendConditionalRevert(true); } break; case FunctionType::Kind::Selfdestruct: @@ -1823,6 +1825,7 @@ void ExpressionCompiler::appendExternalFunctionCall( if (funKind == FunctionType::Kind::External || funKind == FunctionType::Kind::CallCode || funKind == FunctionType::Kind::DelegateCall) { m_context << Instruction::DUP1 << Instruction::EXTCODESIZE << Instruction::ISZERO; + // TODO: error message? m_context.appendConditionalRevert(); existenceChecked = true; } @@ -1865,7 +1868,7 @@ void ExpressionCompiler::appendExternalFunctionCall( { //Propagate error condition (if CALL pushes 0 on stack). m_context << Instruction::ISZERO; - m_context.appendConditionalRevert(); + m_context.appendConditionalRevert(true); } utils().popStackSlots(remainsSize); diff --git a/test/libsolidity/SolidityEndToEndTest.cpp b/test/libsolidity/SolidityEndToEndTest.cpp index b6b26f496..41ebe4628 100644 --- a/test/libsolidity/SolidityEndToEndTest.cpp +++ b/test/libsolidity/SolidityEndToEndTest.cpp @@ -10512,6 +10512,43 @@ BOOST_AUTO_TEST_CASE(require_with_message) ABI_CHECK(callContractFunction("h()"), encodeArgs(0, 0x40, 0x80, 0, 0x40, 3, "abc")); } +BOOST_AUTO_TEST_CASE(bubble_up_error_messages) +{ + char const* sourceCode = R"( + contract D { + function f() public { + revert("message"); + } + function g() public { + this.f(); + } + } + contract C { + D d = new D(); + function forward(address target, bytes data) internal returns (bool success, bytes retval) { + uint retsize; + assembly { + success := call(not(0), target, 0, add(data, 0x20), mload(data), 0, 0) + retsize := returndatasize() + } + retval = new bytes(retsize); + assembly { + returndatacopy(add(retval, 0x20), 0, returndatasize()) + } + } + function f() public returns (bool, bytes) { + return forward(address(d), msg.data); + } + function g() public returns (bool, bytes) { + return forward(address(d), msg.data); + } + } + )"; + compileAndRun(sourceCode, 0, "C"); + ABI_CHECK(callContractFunction("f()"), encodeArgs(0, 0x40, 0x80, 0, 0x40, 3, "message")); + ABI_CHECK(callContractFunction("g()"), encodeArgs(0, 0x40, 0x80, 0, 0x40, 3, "message")); +} + BOOST_AUTO_TEST_CASE(negative_stack_height) { // This code was causing negative stack height during code generation From aa715f8759934ac68b76a2bef84c460b68be636a Mon Sep 17 00:00:00 2001 From: chriseth Date: Wed, 3 Jan 2018 15:07:25 +0100 Subject: [PATCH 164/197] Tests about error bubbling for create and transfer. --- test/libsolidity/SolidityEndToEndTest.cpp | 72 ++++++++++++++++++++++- 1 file changed, 70 insertions(+), 2 deletions(-) diff --git a/test/libsolidity/SolidityEndToEndTest.cpp b/test/libsolidity/SolidityEndToEndTest.cpp index 41ebe4628..c7d8f9175 100644 --- a/test/libsolidity/SolidityEndToEndTest.cpp +++ b/test/libsolidity/SolidityEndToEndTest.cpp @@ -10545,8 +10545,76 @@ BOOST_AUTO_TEST_CASE(bubble_up_error_messages) } )"; compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("f()"), encodeArgs(0, 0x40, 0x80, 0, 0x40, 3, "message")); - ABI_CHECK(callContractFunction("g()"), encodeArgs(0, 0x40, 0x80, 0, 0x40, 3, "message")); + ABI_CHECK(callContractFunction("f()"), encodeArgs(0, 0x40, 0x80, 0, 0x40, 7, "message")); + ABI_CHECK(callContractFunction("g()"), encodeArgs(0, 0x40, 0x80, 0, 0x40, 7, "message")); +} + +BOOST_AUTO_TEST_CASE(bubble_up_error_messages_through_transfer) +{ + char const* sourceCode = R"( + contract D { + function() public payable { + revert("message"); + } + function f() public { + this.transfer(0); + } + } + contract C { + D d = new D(); + function forward(address target, bytes data) internal returns (bool success, bytes retval) { + uint retsize; + assembly { + success := call(not(0), target, 0, add(data, 0x20), mload(data), 0, 0) + retsize := returndatasize() + } + retval = new bytes(retsize); + assembly { + returndatacopy(add(retval, 0x20), 0, returndatasize()) + } + } + function f() public returns (bool, bytes) { + return forward(address(d), msg.data); + } + } + )"; + compileAndRun(sourceCode, 0, "C"); + ABI_CHECK(callContractFunction("f()"), encodeArgs(0, 0x40, 0x80, 0, 0x40, 7, "message")); +} + +BOOST_AUTO_TEST_CASE(bubble_up_error_messages_through_create) +{ + char const* sourceCode = R"( + contract E { + function E() { + revert("message"); + } + } + contract D { + function f() public { + var x = new E(); + } + } + contract C { + D d = new D(); + function forward(address target, bytes data) internal returns (bool success, bytes retval) { + uint retsize; + assembly { + success := call(not(0), target, 0, add(data, 0x20), mload(data), 0, 0) + retsize := returndatasize() + } + retval = new bytes(retsize); + assembly { + returndatacopy(add(retval, 0x20), 0, returndatasize()) + } + } + function f() public returns (bool, bytes) { + return forward(address(d), msg.data); + } + } + )"; + compileAndRun(sourceCode, 0, "C"); + ABI_CHECK(callContractFunction("f()"), encodeArgs(0, 0x40, 0x80, 0, 0x40, 7, "message")); } BOOST_AUTO_TEST_CASE(negative_stack_height) From 344a388d4461abd7369ea44b123f5afe549dc8f7 Mon Sep 17 00:00:00 2001 From: chriseth Date: Wed, 3 Jan 2018 15:30:01 +0100 Subject: [PATCH 165/197] Update documentation. --- docs/common-patterns.rst | 20 +++++++-- docs/contracts.rst | 10 ++++- docs/control-structures.rst | 16 +++++-- docs/miscellaneous.rst | 3 +- docs/solidity-by-example.rst | 66 +++++++++++++++++++---------- docs/structure-of-a-contract.rst | 5 ++- docs/types.rst | 5 ++- docs/units-and-global-variables.rst | 2 + 8 files changed, 92 insertions(+), 35 deletions(-) diff --git a/docs/common-patterns.rst b/docs/common-patterns.rst index c62b5acab..233bdc4e9 100644 --- a/docs/common-patterns.rst +++ b/docs/common-patterns.rst @@ -147,7 +147,10 @@ restrictions highly readable. // a certain address. modifier onlyBy(address _account) { - require(msg.sender == _account); + require( + msg.sender == _account, + "Sender not authorized." + ); // Do not forget the "_;"! It will // be replaced by the actual function // body when the modifier is used. @@ -164,7 +167,10 @@ restrictions highly readable. } modifier onlyAfter(uint _time) { - require(now >= _time); + require( + now >= _time, + "Function called too early." + ); _; } @@ -186,7 +192,10 @@ restrictions highly readable. // This was dangerous before Solidity version 0.4.0, // where it was possible to skip the part after `_;`. modifier costs(uint _amount) { - require(msg.value >= _amount); + require( + msg.value >= _amount, + "Not enough Ether provided." + ); _; if (msg.value > _amount) msg.sender.send(msg.value - _amount); @@ -290,7 +299,10 @@ function finishes. uint public creationTime = now; modifier atStage(Stages _stage) { - require(stage == _stage); + require( + stage == _stage, + "Function cannot be called at this time." + ); _; } diff --git a/docs/contracts.rst b/docs/contracts.rst index 0dd9845c0..0c697dd6b 100644 --- a/docs/contracts.rst +++ b/docs/contracts.rst @@ -315,7 +315,10 @@ inheritable properties of contracts and may be overridden by derived contracts. // function is executed and otherwise, an exception is // thrown. modifier onlyOwner { - require(msg.sender == owner); + require( + msg.sender == owner, + "Only owner can call this function." + ); _; } } @@ -360,7 +363,10 @@ inheritable properties of contracts and may be overridden by derived contracts. contract Mutex { bool locked; modifier noReentrancy() { - require(!locked); + require( + !locked, + "Reentrant call." + ); locked = true; _; locked = false; diff --git a/docs/control-structures.rst b/docs/control-structures.rst index 18a025725..7e3027a06 100644 --- a/docs/control-structures.rst +++ b/docs/control-structures.rst @@ -472,13 +472,16 @@ of an exception instead of "bubbling up". Catching exceptions is not yet possible. In the following example, you can see how ``require`` can be used to easily check conditions on inputs -and how ``assert`` can be used for internal error checking:: +and how ``assert`` can be used for internal error checking. Note that you can optionally provide +a message string for require, but not for assert. + +:: pragma solidity ^0.4.0; contract Sharer { function sendHalf(address addr) public payable returns (uint balance) { - require(msg.value % 2 == 0); // Only allow even numbers + require(msg.value % 2 == 0, "Even value required."); uint balanceBeforeTransfer = this.balance; addr.transfer(msg.value / 2); // Since transfer throws an exception on failure and @@ -517,7 +520,7 @@ did not occur. Because we want to retain the atomicity of transactions, the safe (or at least call) without effect. Note that ``assert``-style exceptions consume all gas available to the call, while ``require``-style exceptions will not consume any gas starting from the Metropolis release. -The following example shows how an error string can be used together with revert: +The following example shows how an error string can be used together with revert and require: :: @@ -527,13 +530,18 @@ The following example shows how an error string can be used together with revert function buy(uint amount) payable { if (amount > msg.value / 2 ether) revert("Not enough Ether provided."); + // Alternative way to do it: + require( + amount <= msg.value / 2 ether, + "Not enough Ether provided." + ); // Perform the purchase. } } The provided string will be abi-encoded together with a uint value that will always be zero. This means that if a string ``x`` is provided, ``(0, x)`` will be encoded as if a function with arguments -``(uint256, string)`` would be called. This zero is used as a version identifier. Future extensions +``(uint256, string)`` was called. This zero is used as a version identifier. Future extensions of this feature might provide actual exception payload of dynamic type and this number could be used to encode the type or also as a simple numeric error code. Until this is specified, a zero will be used in all cases. \ No newline at end of file diff --git a/docs/miscellaneous.rst b/docs/miscellaneous.rst index c5178b511..8270727f9 100644 --- a/docs/miscellaneous.rst +++ b/docs/miscellaneous.rst @@ -333,8 +333,9 @@ Global Variables - ``tx.origin`` (``address``): sender of the transaction (full call chain) - ``assert(bool condition)``: abort execution and revert state changes if condition is ``false`` (use for internal error) - ``require(bool condition)``: abort execution and revert state changes if condition is ``false`` (use for malformed input or error in external component) +- ``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. - ``revert()``: abort execution and revert state changes -- ``revert(string)``: abort execution and revert state changes providing an explanatory string +- ``revert(string 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 ` - ``sha3(...) returns (bytes32)``: an alias to ``keccak256`` diff --git a/docs/solidity-by-example.rst b/docs/solidity-by-example.rst index 3636a3328..3cbfcd666 100644 --- a/docs/solidity-by-example.rst +++ b/docs/solidity-by-example.rst @@ -87,17 +87,25 @@ of votes. // Give `voter` the right to vote on this ballot. // May only be called by `chairperson`. function giveRightToVote(address voter) public { - // If the argument of `require` evaluates to `false`, - // it terminates and reverts all changes to - // the state and to Ether balances. - // This consumes all gas in old EVM versions, but not anymore. - // It is often a good idea to use this if functions are - // called incorrectly. + // If the first argument of `require` evaluates + // to `false`, execution terminates and all + // changes to the state and to Ether balances + // 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 + // functions are called correctly. + // As a second argument, you can also provide an + // explanation about what went wrong. require( - (msg.sender == chairperson) && - !voters[voter].voted && - (voters[voter].weight == 0) + msg.sender == chairperson, + "Only chairperson can give right to vote." ); + require( + !voters[voter].voted, + "The voter already voted." + ); + require(voters[voter].weight == 0); voters[voter].weight = 1; } @@ -105,10 +113,9 @@ of votes. function delegate(address to) public { // assigns reference Voter storage sender = voters[msg.sender]; - require(!sender.voted); + require(!sender.voted, "You already voted."); - // Self-delegation is not allowed. - require(to != msg.sender); + require(to != msg.sender, "Self-delegation is disallowed."); // Forward the delegation as long as // `to` also delegated. @@ -122,7 +129,7 @@ of votes. to = voters[to].delegate; // We found a loop in the delegation, not allowed. - require(to != msg.sender); + require(to != msg.sender, "Found loop in delegation."); } // Since `sender` is a reference, this @@ -145,7 +152,7 @@ of votes. /// to proposal `proposals[proposal].name`. function vote(uint proposal) public { Voter storage sender = voters[msg.sender]; - require(!sender.voted); + require(!sender.voted, "Already voted."); sender.voted = true; sender.vote = proposal; @@ -270,11 +277,17 @@ activate themselves. // Revert the call if the bidding // period is over. - require(now <= auctionEnd); + require( + now <= auctionEnd, + "Auction already ended." + ); // If the bid is not higher, send the // money back. - require(msg.value > highestBid); + require( + msg.value > highestBid, + "There already is a higher bid." + ); if (highestBid != 0) { // Sending back the money by simply using @@ -324,8 +337,8 @@ activate themselves. // external contracts. // 1. Conditions - require(now >= auctionEnd); // auction did not yet end - require(!ended); // this function has already been called + require(now >= auctionEnd, "Auction not yet ended."); + require(!ended, "auctionEnd has already been called."); // 2. Effects ended = true; @@ -543,7 +556,7 @@ Safe Remote Purchase function Purchase() public payable { seller = msg.sender; value = msg.value / 2; - require((2 * value) == msg.value); + require((2 * value) == msg.value, "Value has to be even."); } modifier condition(bool _condition) { @@ -552,17 +565,26 @@ Safe Remote Purchase } modifier onlyBuyer() { - require(msg.sender == buyer); + require( + msg.sender == buyer, + "Only buyer can call this." + ); _; } modifier onlySeller() { - require(msg.sender == seller); + require( + msg.sender == seller, + "Only seller can call this." + ); _; } modifier inState(State _state) { - require(state == _state); + require( + state == _state, + "Invalid state." + ); _; } diff --git a/docs/structure-of-a-contract.rst b/docs/structure-of-a-contract.rst index df40b1d01..9e5eacbbd 100644 --- a/docs/structure-of-a-contract.rst +++ b/docs/structure-of-a-contract.rst @@ -68,7 +68,10 @@ Function modifiers can be used to amend the semantics of functions in a declarat address public seller; modifier onlySeller() { // Modifier - require(msg.sender == seller); + require( + msg.sender == seller, + "Only seller can call this." + ); _; } diff --git a/docs/types.rst b/docs/types.rst index 5de6d07e9..07421bdfb 100644 --- a/docs/types.rst +++ b/docs/types.rst @@ -495,7 +495,10 @@ Another example that uses external function types:: oracle.query("USD", this.oracleResponse); } function oracleResponse(bytes response) public { - require(msg.sender == address(oracle)); + require( + msg.sender == address(oracle), + "Only oracle can call this." + ); // Use the data } } diff --git a/docs/units-and-global-variables.rst b/docs/units-and-global-variables.rst index aad00ba2d..9d5821d58 100644 --- a/docs/units-and-global-variables.rst +++ b/docs/units-and-global-variables.rst @@ -99,6 +99,8 @@ Error Handling throws if the condition is not met - to be used for internal errors. ``require(bool condition)``: throws if the condition is not met - to be used for errors in inputs or external components. +``require(bool condition, string message)``: + throws 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)``: From 43b1dd758b3f3194907fad4be1c35bbc374cdd71 Mon Sep 17 00:00:00 2001 From: chriseth Date: Wed, 3 Jan 2018 15:30:35 +0100 Subject: [PATCH 166/197] Changelog entry. --- Changelog.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Changelog.md b/Changelog.md index 60f28db40..4011270ee 100644 --- a/Changelog.md +++ b/Changelog.md @@ -5,7 +5,7 @@ Features: * Code Generator: More specialized and thus optimized implementation for ``x.push(...)`` * Commandline interface: Error when missing or inaccessible file detected. Suppress it with the ``--ignore-missing`` flag. * Constant Evaluator: Fix evaluation of single element tuples. - * General: Allow providing reason string for ``revert()``. + * General: Allow providing reason string for ``revert()`` and ``require()``. * General: Limit the number of errors output in a single run to 256. * General: Support accessing dynamic return data in post-byzantium EVMs. * Interfaces: Allow overriding external functions in interfaces with public in an implementing contract. From 167ee2fcbb1000e6387142892c4252f8597a4481 Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 4 Jan 2018 14:24:45 +0100 Subject: [PATCH 167/197] Update source location tests. --- test/libsolidity/Assembly.cpp | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/test/libsolidity/Assembly.cpp b/test/libsolidity/Assembly.cpp index 5519ae0d9..59993f66f 100644 --- a/test/libsolidity/Assembly.cpp +++ b/test/libsolidity/Assembly.cpp @@ -158,13 +158,24 @@ BOOST_AUTO_TEST_CASE(location_test) } )"; shared_ptr n = make_shared(""); + shared_ptr codegen = make_shared("--CODEGEN--:8-17"); AssemblyItems items = compileContract(sourceCode); vector locations = - vector(24, SourceLocation(2, 75, n)) + - vector(32, SourceLocation(20, 72, n)) + - vector{SourceLocation(42, 51, n), SourceLocation(65, 67, n)} + - vector(2, SourceLocation(58, 67, n)) + - vector(2, SourceLocation(20, 72, n)); + vector(24, SourceLocation(2, 75, make_shared(""))) + + vector(2, SourceLocation(20, 72, make_shared(""))) + + vector(1, SourceLocation(8, 17, make_shared("--CODEGEN--"))) + + vector(3, SourceLocation(5, 7, make_shared("--CODEGEN--"))) + + vector(1, SourceLocation(30, 31, make_shared("--CODEGEN--"))) + + vector(1, SourceLocation(27, 28, make_shared("--CODEGEN--"))) + + vector(1, SourceLocation(20, 32, make_shared("--CODEGEN--"))) + + vector(1, SourceLocation(5, 7, make_shared("--CODEGEN--"))) + + vector(24, SourceLocation(20, 72, make_shared(""))) + + vector(1, SourceLocation(42, 51, make_shared(""))) + + vector(1, SourceLocation(65, 67, make_shared(""))) + + vector(2, SourceLocation(58, 67, make_shared(""))) + + vector(2, SourceLocation(20, 72, make_shared(""))); + + checkAssemblyLocations(items, locations); } From 42c4c78390b6330e8bc4558b4be5d580251abcba Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 4 Jan 2018 17:08:47 +0100 Subject: [PATCH 168/197] Adjust tests. --- test/libsolidity/JSONCompiler.cpp | 8 ++++---- .../SolidityNameAndTypeResolution.cpp | 5 +++-- test/libsolidity/StandardCompiler.cpp | 19 ++++++++++++------- 3 files changed, 19 insertions(+), 13 deletions(-) diff --git a/test/libsolidity/JSONCompiler.cpp b/test/libsolidity/JSONCompiler.cpp index cdcc22a65..2b3df3a73 100644 --- a/test/libsolidity/JSONCompiler.cpp +++ b/test/libsolidity/JSONCompiler.cpp @@ -111,7 +111,7 @@ BOOST_AUTO_TEST_CASE(basic_compilation) BOOST_CHECK(contract["bytecode"].isString()); BOOST_CHECK_EQUAL( dev::test::bytecodeSansMetadata(contract["bytecode"].asString()), - "60806040523415600e57600080fd5b603580601b6000396000f3006080604052600080fd00" + "6080604052348015600f57600080fd5b50603580601d6000396000f3006080604052600080fd00" ); BOOST_CHECK(contract["runtimeBytecode"].isString()); BOOST_CHECK_EQUAL( @@ -122,7 +122,7 @@ BOOST_AUTO_TEST_CASE(basic_compilation) BOOST_CHECK(contract["gasEstimates"].isObject()); BOOST_CHECK_EQUAL( dev::jsonCompactPrint(contract["gasEstimates"]), - "{\"creation\":[61,10600],\"external\":{},\"internal\":{}}" + "{\"creation\":[66,10600],\"external\":{},\"internal\":{}}" ); BOOST_CHECK(contract["metadata"].isString()); BOOST_CHECK(dev::test::isValidMetadata(contract["metadata"].asString())); @@ -153,7 +153,7 @@ BOOST_AUTO_TEST_CASE(single_compilation) BOOST_CHECK(contract["bytecode"].isString()); BOOST_CHECK_EQUAL( dev::test::bytecodeSansMetadata(contract["bytecode"].asString()), - "60806040523415600e57600080fd5b603580601b6000396000f3006080604052600080fd00" + "6080604052348015600f57600080fd5b50603580601d6000396000f3006080604052600080fd00" ); BOOST_CHECK(contract["runtimeBytecode"].isString()); BOOST_CHECK_EQUAL( @@ -164,7 +164,7 @@ BOOST_AUTO_TEST_CASE(single_compilation) BOOST_CHECK(contract["gasEstimates"].isObject()); BOOST_CHECK_EQUAL( dev::jsonCompactPrint(contract["gasEstimates"]), - "{\"creation\":[61,10600],\"external\":{},\"internal\":{}}" + "{\"creation\":[66,10600],\"external\":{},\"internal\":{}}" ); BOOST_CHECK(contract["metadata"].isString()); BOOST_CHECK(dev::test::isValidMetadata(contract["metadata"].asString())); diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index 2bf718867..5688267a9 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -6009,7 +6009,8 @@ 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."); - CHECK_WARNING("contract C { function f() pure public { require; } }", "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 { suicide; } }", "Statement has no effect."); } @@ -6508,7 +6509,7 @@ BOOST_AUTO_TEST_CASE(does_not_error_transfer_regular_function) CHECK_SUCCESS_NO_WARNINGS(text); } -BOOST_AUTO_TEST_CASE(returndatacopy_as_variable) +BOOST_AUTO_TEST_CASE(returndatasize_as_variable) { char const* text = R"( contract c { function f() public { uint returndatasize; assembly { returndatasize }}} diff --git a/test/libsolidity/StandardCompiler.cpp b/test/libsolidity/StandardCompiler.cpp index b285a2a0e..74bf01b2f 100644 --- a/test/libsolidity/StandardCompiler.cpp +++ b/test/libsolidity/StandardCompiler.cpp @@ -261,19 +261,24 @@ BOOST_AUTO_TEST_CASE(basic_compilation) BOOST_CHECK(contract["evm"]["bytecode"]["object"].isString()); BOOST_CHECK_EQUAL( dev::test::bytecodeSansMetadata(contract["evm"]["bytecode"]["object"].asString()), - "60806040523415600e57600080fd5b603580601b6000396000f3006080604052600080fd00" + "6080604052348015600f57600080fd5b50603580601d6000396000f3006080604052600080fd00" ); BOOST_CHECK(contract["evm"]["assembly"].isString()); BOOST_CHECK(contract["evm"]["assembly"].asString().find( - " /* \"fileA\":0:14 contract A { } */\n mstore(0x40, 0x80)\n jumpi(tag_1, iszero(callvalue))\n" - " 0x0\n dup1\n revert\ntag_1:\n dataSize(sub_0)\n dup1\n dataOffset(sub_0)\n 0x0\n codecopy\n 0x0\n" - " return\nstop\n\nsub_0: assembly {\n /* \"fileA\":0:14 contract A { } */\n" - " mstore(0x40, 0x80)\n 0x0\n dup1\n revert\n\n" - " auxdata: 0xa165627a7a7230582") == 0); + " /* \"fileA\":0:14 contract A { } */\n mstore(0x40, 0x80)\n " + "callvalue\n /* \"--CODEGEN--\":8:17 */\n dup1\n " + "/* \"--CODEGEN--\":5:7 */\n iszero\n tag_1\n jumpi\n " + "/* \"--CODEGEN--\":30:31 */\n 0x0\n /* \"--CODEGEN--\":27:28 */\n " + "dup1\n /* \"--CODEGEN--\":20:32 */\n revert\n /* \"--CODEGEN--\":5:7 */\n" + "tag_1:\n /* \"fileA\":0:14 contract A { } */\n pop\n dataSize(sub_0)\n dup1\n " + "dataOffset(sub_0)\n 0x0\n codecopy\n 0x0\n return\nstop\n\nsub_0: assembly {\n " + "/* \"fileA\":0:14 contract A { } */\n mstore(0x40, 0x80)\n 0x0\n " + "dup1\n revert\n\n auxdata: 0xa165627a7a72305820" + ) == 0); BOOST_CHECK(contract["evm"]["gasEstimates"].isObject()); BOOST_CHECK_EQUAL( dev::jsonCompactPrint(contract["evm"]["gasEstimates"]), - "{\"creation\":{\"codeDepositCost\":\"10600\",\"executionCost\":\"61\",\"totalCost\":\"10661\"}}" + "{\"creation\":{\"codeDepositCost\":\"10600\",\"executionCost\":\"66\",\"totalCost\":\"10666\"}}" ); BOOST_CHECK(contract["metadata"].isString()); BOOST_CHECK(dev::test::isValidMetadata(contract["metadata"].asString())); From fcb7a2721636a9a2f05659610fc05fa8513f745d Mon Sep 17 00:00:00 2001 From: chriseth Date: Fri, 2 Mar 2018 17:26:16 +0100 Subject: [PATCH 169/197] Only forward returndata if EVM version supports it. --- libsolidity/codegen/CompilerContext.cpp | 2 +- libsolidity/codegen/CompilerContext.h | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/libsolidity/codegen/CompilerContext.cpp b/libsolidity/codegen/CompilerContext.cpp index 2cd256f3d..a35eea738 100644 --- a/libsolidity/codegen/CompilerContext.cpp +++ b/libsolidity/codegen/CompilerContext.cpp @@ -264,7 +264,7 @@ CompilerContext& CompilerContext::appendRevert() CompilerContext& CompilerContext::appendConditionalRevert(bool _forwardReturnData) { - if (_forwardReturnData) + if (_forwardReturnData && m_evmVersion.supportsReturndata()) appendInlineAssembly(R"({ if condition { returndatacopy(0, 0, returndatasize()) diff --git a/libsolidity/codegen/CompilerContext.h b/libsolidity/codegen/CompilerContext.h index c6f2f3be5..098472f7b 100644 --- a/libsolidity/codegen/CompilerContext.h +++ b/libsolidity/codegen/CompilerContext.h @@ -158,6 +158,8 @@ public: CompilerContext& appendRevert(); /// Appends a conditional REVERT-call, either forwarding the RETURNDATA or providing the /// empty string. Consumes the condition. + /// If the current EVM version does not support RETURNDATA, uses REVERT but does not forward + /// the data. CompilerContext& appendConditionalRevert(bool _forwardReturnData = false); /// Appends a JUMP to a specific tag CompilerContext& appendJumpTo(eth::AssemblyItem const& _tag) { m_asm->appendJump(_tag); return *this; } From e133b1a0cd78acebb0db5448ec62e62ae0060fa2 Mon Sep 17 00:00:00 2001 From: chriseth Date: Tue, 6 Mar 2018 20:09:52 +0100 Subject: [PATCH 170/197] Adjust expectations in case of homestead VM. --- test/libsolidity/SolidityEndToEndTest.cpp | 27 ++++++++++++++--------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/test/libsolidity/SolidityEndToEndTest.cpp b/test/libsolidity/SolidityEndToEndTest.cpp index c7d8f9175..600757f10 100644 --- a/test/libsolidity/SolidityEndToEndTest.cpp +++ b/test/libsolidity/SolidityEndToEndTest.cpp @@ -10452,8 +10452,9 @@ BOOST_AUTO_TEST_CASE(revert_with_cause) } )"; compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("f()"), encodeArgs(0, 0x40, 0x80, 0, 0x40, 7, "test123")); - ABI_CHECK(callContractFunction("g()"), encodeArgs(0, 0x40, 0xa0, 0, 0x40, 44, "test1234567890123456789012345678901234567890")); + bool const haveReturndata = dev::test::Options::get().evmVersion().supportsReturndata(); + ABI_CHECK(callContractFunction("f()"), haveReturndata ? encodeArgs(0, 0x40, 0x80, 0, 0x40, 7, "test123") : bytes()); + ABI_CHECK(callContractFunction("g()"), haveReturndata ? encodeArgs(0, 0x40, 0xa0, 0, 0x40, 44, "test1234567890123456789012345678901234567890") : bytes()); } BOOST_AUTO_TEST_CASE(require_with_message) @@ -10505,11 +10506,12 @@ BOOST_AUTO_TEST_CASE(require_with_message) } )"; compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("f(uint256)", 8), encodeArgs(1, 0x40, 0)); - ABI_CHECK(callContractFunction("f(uint256)", 5), encodeArgs(0, 0x40, 0x80, 0, 0x40, 6, "failed")); - ABI_CHECK(callContractFunction("g()"), encodeArgs(1, 0x40, 0)); - ABI_CHECK(callContractFunction("g()"), encodeArgs(0, 0x40, 0x80, 0, 0x40, 18 , "only on second run")); - ABI_CHECK(callContractFunction("h()"), encodeArgs(0, 0x40, 0x80, 0, 0x40, 3, "abc")); + bool const haveReturndata = dev::test::Options::get().evmVersion().supportsReturndata(); + ABI_CHECK(callContractFunction("f(uint256)", 8), haveReturndata ? encodeArgs(1, 0x40, 0) : bytes()); + ABI_CHECK(callContractFunction("f(uint256)", 5), haveReturndata ? encodeArgs(0, 0x40, 0x80, 0, 0x40, 6, "failed") : bytes()); + ABI_CHECK(callContractFunction("g()"), haveReturndata ? encodeArgs(1, 0x40, 0) : bytes()); + ABI_CHECK(callContractFunction("g()"), haveReturndata ? encodeArgs(0, 0x40, 0x80, 0, 0x40, 18 , "only on second run") : bytes()); + ABI_CHECK(callContractFunction("h()"), haveReturndata ? encodeArgs(0, 0x40, 0x80, 0, 0x40, 3, "abc") : bytes()); } BOOST_AUTO_TEST_CASE(bubble_up_error_messages) @@ -10545,8 +10547,9 @@ BOOST_AUTO_TEST_CASE(bubble_up_error_messages) } )"; compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("f()"), encodeArgs(0, 0x40, 0x80, 0, 0x40, 7, "message")); - ABI_CHECK(callContractFunction("g()"), encodeArgs(0, 0x40, 0x80, 0, 0x40, 7, "message")); + bool const haveReturndata = dev::test::Options::get().evmVersion().supportsReturndata(); + ABI_CHECK(callContractFunction("f()"), haveReturndata ? encodeArgs(0, 0x40, 0x80, 0, 0x40, 7, "message") : bytes()); + ABI_CHECK(callContractFunction("g()"), haveReturndata ? encodeArgs(0, 0x40, 0x80, 0, 0x40, 7, "message") : bytes()); } BOOST_AUTO_TEST_CASE(bubble_up_error_messages_through_transfer) @@ -10579,7 +10582,8 @@ BOOST_AUTO_TEST_CASE(bubble_up_error_messages_through_transfer) } )"; compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("f()"), encodeArgs(0, 0x40, 0x80, 0, 0x40, 7, "message")); + bool const haveReturndata = dev::test::Options::get().evmVersion().supportsReturndata(); + ABI_CHECK(callContractFunction("f()"), haveReturndata ? encodeArgs(0, 0x40, 0x80, 0, 0x40, 7, "message") : bytes()); } BOOST_AUTO_TEST_CASE(bubble_up_error_messages_through_create) @@ -10614,7 +10618,8 @@ BOOST_AUTO_TEST_CASE(bubble_up_error_messages_through_create) } )"; compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("f()"), encodeArgs(0, 0x40, 0x80, 0, 0x40, 7, "message")); + bool const haveReturndata = dev::test::Options::get().evmVersion().supportsReturndata(); + ABI_CHECK(callContractFunction("f()"), haveReturndata ? encodeArgs(0, 0x40, 0x80, 0, 0x40, 7, "message") : bytes()); } BOOST_AUTO_TEST_CASE(negative_stack_height) From 338a875134f2e41e9a7e254cc3f7d87c7f4d462e Mon Sep 17 00:00:00 2001 From: chriseth Date: Wed, 4 Apr 2018 17:59:45 +0200 Subject: [PATCH 171/197] Update expectation. --- test/libsolidity/SolidityCompiler.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/libsolidity/SolidityCompiler.cpp b/test/libsolidity/SolidityCompiler.cpp index e87ab6030..90540f3ef 100644 --- a/test/libsolidity/SolidityCompiler.cpp +++ b/test/libsolidity/SolidityCompiler.cpp @@ -47,8 +47,8 @@ BOOST_AUTO_TEST_CASE(does_not_include_creation_time_only_internal_functions) BOOST_REQUIRE_MESSAGE(m_compiler.compile(), "Compiling contract failed"); bytes const& creationBytecode = m_compiler.object("C").bytecode; bytes const& runtimeBytecode = m_compiler.runtimeObject("C").bytecode; - BOOST_CHECK(creationBytecode.size() >= 120); - BOOST_CHECK(creationBytecode.size() <= 150); + BOOST_CHECK(creationBytecode.size() >= 130); + BOOST_CHECK(creationBytecode.size() <= 160); BOOST_CHECK(runtimeBytecode.size() >= 50); BOOST_CHECK(runtimeBytecode.size() <= 70); } From 4faa839813ce76fc87f99b002aad6cadd2b784e1 Mon Sep 17 00:00:00 2001 From: chriseth Date: Fri, 6 Apr 2018 15:14:55 +0200 Subject: [PATCH 172/197] Use error signature for revert data. --- libsolidity/codegen/CompilerUtils.cpp | 14 +++++++++++++ libsolidity/codegen/CompilerUtils.h | 7 +++++++ libsolidity/codegen/ExpressionCompiler.cpp | 23 +++------------------- test/libsolidity/SolidityEndToEndTest.cpp | 23 +++++++++++++--------- 4 files changed, 38 insertions(+), 29 deletions(-) diff --git a/libsolidity/codegen/CompilerUtils.cpp b/libsolidity/codegen/CompilerUtils.cpp index 34337d7d7..b4550153e 100644 --- a/libsolidity/codegen/CompilerUtils.cpp +++ b/libsolidity/codegen/CompilerUtils.cpp @@ -78,6 +78,20 @@ void CompilerUtils::toSizeAfterFreeMemoryPointer() m_context << Instruction::SWAP1; } +void CompilerUtils::revertWithStringData(Type const& _argumentType) +{ + solAssert(_argumentType.isImplicitlyConvertibleTo(*Type::fromElementaryTypeName("string memory")), ""); + fetchFreeMemoryPointer(); + m_context << (u256(FixedHash<4>::Arith(FixedHash<4>(dev::keccak256("Error(string)")))) << (256 - 32)); + m_context << Instruction::DUP2 << Instruction::MSTORE; + m_context << u256(4) << Instruction::ADD; + // Stack: + abiEncode({_argumentType.shared_from_this()}, {make_shared(DataLocation::Memory, true)}); + toSizeAfterFreeMemoryPointer(); + m_context << Instruction::REVERT; + m_context.adjustStackOffset(_argumentType.sizeOnStack()); +} + unsigned CompilerUtils::loadFromMemory( unsigned _offset, Type const& _type, diff --git a/libsolidity/codegen/CompilerUtils.h b/libsolidity/codegen/CompilerUtils.h index a32c5c6e1..476a7559d 100644 --- a/libsolidity/codegen/CompilerUtils.h +++ b/libsolidity/codegen/CompilerUtils.h @@ -54,6 +54,13 @@ public: /// Stack post: void toSizeAfterFreeMemoryPointer(); + /// Appends code that performs a revert, providing the given string data. + /// Will also append an error signature corresponding to Error(string). + /// @param _argumentType the type of the string argument, will be converted to memory string. + /// Stack pre: string data + /// Stack post: + void revertWithStringData(Type const& _argumentType); + /// Loads data from memory to the stack. /// @param _offset offset in memory (or calldata) /// @param _type data type to load diff --git a/libsolidity/codegen/ExpressionCompiler.cpp b/libsolidity/codegen/ExpressionCompiler.cpp index 3f521f2d6..b67e7b688 100644 --- a/libsolidity/codegen/ExpressionCompiler.cpp +++ b/libsolidity/codegen/ExpressionCompiler.cpp @@ -685,17 +685,11 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) { if (!arguments.empty()) { + // function-sel(Error(string)) + encoding solAssert(arguments.size() == 1, ""); solAssert(function.parameterTypes().size() == 1, ""); - m_context << u256(0); arguments.front()->accept(*this); - utils().fetchFreeMemoryPointer(); - utils().abiEncode( - {make_shared(256), arguments.front()->annotation().type}, - {make_shared(256), make_shared(DataLocation::Memory, true)} - ); - utils().toSizeAfterFreeMemoryPointer(); - m_context << Instruction::REVERT; + utils().revertWithStringData(*arguments.front()->annotation().type); } else m_context.appendRevert(); @@ -937,18 +931,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) // condition was not met, flag an error m_context.appendInvalid(); else if (arguments.size() > 1) - { - m_context << u256(0); - utils().moveIntoStack(arguments.at(1)->annotation().type->sizeOnStack(), 1); - utils().fetchFreeMemoryPointer(); - utils().abiEncode( - {make_shared(256), arguments.at(1)->annotation().type}, - {make_shared(256), make_shared(DataLocation::Memory, true)} - ); - utils().toSizeAfterFreeMemoryPointer(); - m_context << Instruction::REVERT; - m_context.adjustStackOffset(arguments.at(1)->annotation().type->sizeOnStack()); - } + utils().revertWithStringData(*arguments.at(1)->annotation().type); else m_context.appendRevert(); // the success branch diff --git a/test/libsolidity/SolidityEndToEndTest.cpp b/test/libsolidity/SolidityEndToEndTest.cpp index 600757f10..c71d6b594 100644 --- a/test/libsolidity/SolidityEndToEndTest.cpp +++ b/test/libsolidity/SolidityEndToEndTest.cpp @@ -10453,8 +10453,9 @@ BOOST_AUTO_TEST_CASE(revert_with_cause) )"; compileAndRun(sourceCode, 0, "C"); bool const haveReturndata = dev::test::Options::get().evmVersion().supportsReturndata(); - ABI_CHECK(callContractFunction("f()"), haveReturndata ? encodeArgs(0, 0x40, 0x80, 0, 0x40, 7, "test123") : bytes()); - ABI_CHECK(callContractFunction("g()"), haveReturndata ? encodeArgs(0, 0x40, 0xa0, 0, 0x40, 44, "test1234567890123456789012345678901234567890") : bytes()); + bytes const errorSignature = bytes{0x08, 0xc3, 0x79, 0xa0}; + ABI_CHECK(callContractFunction("f()"), haveReturndata ? encodeArgs(0, 0x40, 0x64) + errorSignature + encodeArgs(0x20, 7, "test123") + bytes(28, 0) : bytes()); + ABI_CHECK(callContractFunction("g()"), haveReturndata ? encodeArgs(0, 0x40, 0x84) + errorSignature + encodeArgs(0x20, 44, "test1234567890123456789012345678901234567890") + bytes(28, 0): bytes()); } BOOST_AUTO_TEST_CASE(require_with_message) @@ -10507,11 +10508,12 @@ BOOST_AUTO_TEST_CASE(require_with_message) )"; compileAndRun(sourceCode, 0, "C"); bool const haveReturndata = dev::test::Options::get().evmVersion().supportsReturndata(); + bytes const errorSignature = bytes{0x08, 0xc3, 0x79, 0xa0}; ABI_CHECK(callContractFunction("f(uint256)", 8), haveReturndata ? encodeArgs(1, 0x40, 0) : bytes()); - ABI_CHECK(callContractFunction("f(uint256)", 5), haveReturndata ? encodeArgs(0, 0x40, 0x80, 0, 0x40, 6, "failed") : bytes()); + ABI_CHECK(callContractFunction("f(uint256)", 5), haveReturndata ? encodeArgs(0, 0x40, 0x64) + errorSignature + encodeArgs(0x20, 6, "failed") + bytes(28, 0) : bytes()); ABI_CHECK(callContractFunction("g()"), haveReturndata ? encodeArgs(1, 0x40, 0) : bytes()); - ABI_CHECK(callContractFunction("g()"), haveReturndata ? encodeArgs(0, 0x40, 0x80, 0, 0x40, 18 , "only on second run") : bytes()); - ABI_CHECK(callContractFunction("h()"), haveReturndata ? encodeArgs(0, 0x40, 0x80, 0, 0x40, 3, "abc") : bytes()); + ABI_CHECK(callContractFunction("g()"), haveReturndata ? encodeArgs(0, 0x40, 0x64) + errorSignature + encodeArgs(0x20, 18, "only on second run") + bytes(28, 0) : bytes()); + ABI_CHECK(callContractFunction("h()"), haveReturndata ? encodeArgs(0, 0x40, 0x64) + errorSignature + encodeArgs(0x20, 3, "abc") + bytes(28, 0): bytes()); } BOOST_AUTO_TEST_CASE(bubble_up_error_messages) @@ -10548,8 +10550,9 @@ BOOST_AUTO_TEST_CASE(bubble_up_error_messages) )"; compileAndRun(sourceCode, 0, "C"); bool const haveReturndata = dev::test::Options::get().evmVersion().supportsReturndata(); - ABI_CHECK(callContractFunction("f()"), haveReturndata ? encodeArgs(0, 0x40, 0x80, 0, 0x40, 7, "message") : bytes()); - ABI_CHECK(callContractFunction("g()"), haveReturndata ? encodeArgs(0, 0x40, 0x80, 0, 0x40, 7, "message") : bytes()); + bytes const errorSignature = bytes{0x08, 0xc3, 0x79, 0xa0}; + ABI_CHECK(callContractFunction("f()"), haveReturndata ? encodeArgs(0, 0x40, 0x64) + errorSignature + encodeArgs(0x20, 7, "message") + bytes(28, 0) : bytes()); + ABI_CHECK(callContractFunction("g()"), haveReturndata ? encodeArgs(0, 0x40, 0x64) + errorSignature + encodeArgs(0x20, 7, "message") + bytes(28, 0) : bytes()); } BOOST_AUTO_TEST_CASE(bubble_up_error_messages_through_transfer) @@ -10583,7 +10586,8 @@ BOOST_AUTO_TEST_CASE(bubble_up_error_messages_through_transfer) )"; compileAndRun(sourceCode, 0, "C"); bool const haveReturndata = dev::test::Options::get().evmVersion().supportsReturndata(); - ABI_CHECK(callContractFunction("f()"), haveReturndata ? encodeArgs(0, 0x40, 0x80, 0, 0x40, 7, "message") : bytes()); + bytes const errorSignature = bytes{0x08, 0xc3, 0x79, 0xa0}; + ABI_CHECK(callContractFunction("f()"), haveReturndata ? encodeArgs(0, 0x40, 0x64) + errorSignature + encodeArgs(0x20, 7, "message") + bytes(28, 0) : bytes()); } BOOST_AUTO_TEST_CASE(bubble_up_error_messages_through_create) @@ -10619,7 +10623,8 @@ BOOST_AUTO_TEST_CASE(bubble_up_error_messages_through_create) )"; compileAndRun(sourceCode, 0, "C"); bool const haveReturndata = dev::test::Options::get().evmVersion().supportsReturndata(); - ABI_CHECK(callContractFunction("f()"), haveReturndata ? encodeArgs(0, 0x40, 0x80, 0, 0x40, 7, "message") : bytes()); + bytes const errorSignature = bytes{0x08, 0xc3, 0x79, 0xa0}; + ABI_CHECK(callContractFunction("f()"), haveReturndata ? encodeArgs(0, 0x40, 0x64) + errorSignature + encodeArgs(0x20, 7, "message") + bytes(28, 0) : bytes()); } BOOST_AUTO_TEST_CASE(negative_stack_height) From b25598126e57fca73058edd722eef7c681460557 Mon Sep 17 00:00:00 2001 From: chriseth Date: Fri, 6 Apr 2018 16:11:52 +0200 Subject: [PATCH 173/197] Update documentation and minor changes. --- docs/control-structures.rst | 18 +++++++++++------- test/libsolidity/Assembly.cpp | 1 - 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/docs/control-structures.rst b/docs/control-structures.rst index 7e3027a06..7024a6840 100644 --- a/docs/control-structures.rst +++ b/docs/control-structures.rst @@ -473,7 +473,7 @@ Catching exceptions is not yet possible. In the following example, you can see how ``require`` can be used to easily check conditions on inputs and how ``assert`` can be used for internal error checking. Note that you can optionally provide -a message string for require, but not for assert. +a message string for ``require``, but not for ``assert``. :: @@ -539,9 +539,13 @@ The following example shows how an error string can be used together with revert } } -The provided string will be abi-encoded together with a uint value that will always be zero. -This means that if a string ``x`` is provided, ``(0, x)`` will be encoded as if a function with arguments -``(uint256, string)`` was called. This zero is used as a version identifier. Future extensions -of this feature might provide actual exception payload of dynamic type and this number could be used -to encode the type or also as a simple numeric error code. Until this is specified, a zero will -be used in all cases. \ No newline at end of file +The provided string will be :ref:`abi-encoded ` as if it were a call to a function ``Error(string)``. +In the above example, ``revert("Not enough Ether provided.");`` will cause the following hexadecimal data be +set as error return data: + +.. code:: + + 0x08c379a0 // Function selector for Error(string) + 0x0000000000000000000000000000000000000000000000000000000000000020 // Data offset + 0x000000000000000000000000000000000000000000000000000000000000001a // String length + 0x4e6f7420656e6f7567682045746865722070726f76696465642e000000000000 // String data diff --git a/test/libsolidity/Assembly.cpp b/test/libsolidity/Assembly.cpp index 59993f66f..bdb7a1073 100644 --- a/test/libsolidity/Assembly.cpp +++ b/test/libsolidity/Assembly.cpp @@ -158,7 +158,6 @@ BOOST_AUTO_TEST_CASE(location_test) } )"; shared_ptr n = make_shared(""); - shared_ptr codegen = make_shared("--CODEGEN--:8-17"); AssemblyItems items = compileContract(sourceCode); vector locations = vector(24, SourceLocation(2, 75, make_shared(""))) + From 6862f2294357d18a4640e95e3f9235692e6c055e Mon Sep 17 00:00:00 2001 From: Daniel Kirchner Date: Thu, 12 Apr 2018 14:57:14 +0200 Subject: [PATCH 174/197] Fix internal compiler error when parsing ``var`` declaration without identifier. --- Changelog.md | 1 + libsolidity/parsing/Parser.cpp | 6 +++-- test/libsolidity/SolidityParser.cpp | 22 ------------------- .../syntaxTests/parsing/return_var.sol | 13 +++++++++++ .../parsing/var_in_function_arguments.sol | 13 +++++++++++ 5 files changed, 31 insertions(+), 24 deletions(-) create mode 100644 test/libsolidity/syntaxTests/parsing/return_var.sol create mode 100644 test/libsolidity/syntaxTests/parsing/var_in_function_arguments.sol diff --git a/Changelog.md b/Changelog.md index 6288e8488..b9cd80f58 100644 --- a/Changelog.md +++ b/Changelog.md @@ -31,6 +31,7 @@ Bugfixes: * Commandline interface: Support ``--evm-version constantinople`` properly. * DocString Parser: Fix error message for empty descriptions. * Gas Estimator: Correctly ignore costs of fallback function for other functions. + * Parser: Fix internal compiler error when parsing ``var`` declaration without identifier. * Parser: Fix parsing of getters for function type variables. * Standard JSON: Support ``constantinople`` as ``evmVersion`` properly. * Type Checker: Fix detection of recursive structs. diff --git a/libsolidity/parsing/Parser.cpp b/libsolidity/parsing/Parser.cpp index 2d8ca7d3f..618a08969 100644 --- a/libsolidity/parsing/Parser.cpp +++ b/libsolidity/parsing/Parser.cpp @@ -607,8 +607,10 @@ ASTPointer Parser::parseVariableDeclaration( if (_options.allowEmptyName && m_scanner->currentToken() != Token::Identifier) { identifier = make_shared(""); - solAssert(type != nullptr, ""); - nodeFactory.setEndPositionFromNode(type); + solAssert(!_options.allowVar, ""); // allowEmptyName && allowVar makes no sense + if (type) + nodeFactory.setEndPositionFromNode(type); + // if type is null this has already caused an error } else identifier = expectIdentifierToken(); diff --git a/test/libsolidity/SolidityParser.cpp b/test/libsolidity/SolidityParser.cpp index 93e6bcaa4..100b3662f 100644 --- a/test/libsolidity/SolidityParser.cpp +++ b/test/libsolidity/SolidityParser.cpp @@ -557,16 +557,6 @@ BOOST_AUTO_TEST_CASE(variable_definition_with_initialization) BOOST_CHECK(successParse(text)); } -BOOST_AUTO_TEST_CASE(variable_definition_in_function_parameter) -{ - char const* text = R"( - contract test { - function fun(var a) {} - } - )"; - CHECK_PARSE_ERROR(text, "Expected explicit type name"); -} - BOOST_AUTO_TEST_CASE(variable_definition_in_mapping) { char const* text = R"( @@ -579,18 +569,6 @@ BOOST_AUTO_TEST_CASE(variable_definition_in_mapping) CHECK_PARSE_ERROR(text, "Expected elementary type name for mapping key type"); } -BOOST_AUTO_TEST_CASE(variable_definition_in_function_return) -{ - char const* text = R"( - contract test { - function fun() returns(var d) { - return 1; - } - } - )"; - CHECK_PARSE_ERROR(text, "Expected explicit type name"); -} - BOOST_AUTO_TEST_CASE(operator_expression) { char const* text = R"( diff --git a/test/libsolidity/syntaxTests/parsing/return_var.sol b/test/libsolidity/syntaxTests/parsing/return_var.sol new file mode 100644 index 000000000..113032a75 --- /dev/null +++ b/test/libsolidity/syntaxTests/parsing/return_var.sol @@ -0,0 +1,13 @@ +contract C { + function f() returns(var) {} + function f() returns(var x) {} + function f() public pure returns (var storage) {} + function f() public pure returns (var storage x) {} +} +// ---- +// ParserError: (38-38): Expected explicit type name. +// ParserError: (71-71): Expected explicit type name. +// ParserError: (119-119): Expected explicit type name. +// ParserError: (123-123): Location specifier needs explicit type name. +// ParserError: (173-173): Expected explicit type name. +// ParserError: (177-177): Location specifier needs explicit type name. diff --git a/test/libsolidity/syntaxTests/parsing/var_in_function_arguments.sol b/test/libsolidity/syntaxTests/parsing/var_in_function_arguments.sol new file mode 100644 index 000000000..cb166c7d8 --- /dev/null +++ b/test/libsolidity/syntaxTests/parsing/var_in_function_arguments.sol @@ -0,0 +1,13 @@ +contract C { + function f(var) public pure {} + function f(var x) public pure {} + function f(var storage) public pure {} + function f(var storage x) public pure {} +} +// ---- +// ParserError: (28-28): Expected explicit type name. +// ParserError: (63-63): Expected explicit type name. +// ParserError: (100-100): Expected explicit type name. +// ParserError: (104-104): Location specifier needs explicit type name. +// ParserError: (143-143): Expected explicit type name. +// ParserError: (147-147): Location specifier needs explicit type name. From 75b88286667690ffb4a5e079665ed8b70bcaeb87 Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 5 Apr 2018 15:52:25 +0200 Subject: [PATCH 175/197] Allow struct encoding with new encoder. --- libsolidity/analysis/TypeChecker.cpp | 25 +++-- test/libsolidity/SolidityEndToEndTest.cpp | 106 ++++++++++++++++++ .../specialFunctions/abi_encode_structs.sol | 17 +++ .../abi_encode_structs_abiv2.sol | 18 +++ ...ypes_with_unspecified_encoding_structs.sol | 9 +- ...ith_unspecified_encoding_structs_abiv2.sol | 16 +++ 6 files changed, 179 insertions(+), 12 deletions(-) create mode 100644 test/libsolidity/syntaxTests/specialFunctions/abi_encode_structs.sol create mode 100644 test/libsolidity/syntaxTests/specialFunctions/abi_encode_structs_abiv2.sol create mode 100644 test/libsolidity/syntaxTests/specialFunctions/types_with_unspecified_encoding_structs_abiv2.sol diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index df08598c9..b95fee38a 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -1723,6 +1723,8 @@ bool TypeChecker::visit(FunctionCall const& _functionCall) } else if (isPositionalCall) { + bool const abiEncodeV2 = m_scope->sourceUnit().annotation().experimentalFeatures.count(ExperimentalFeature::ABIEncoderV2); + for (size_t i = 0; i < arguments.size(); ++i) { auto const& argType = type(*arguments[i]); @@ -1735,13 +1737,22 @@ bool TypeChecker::visit(FunctionCall const& _functionCall) m_errorReporter.typeError(arguments[i]->location(), "Invalid rational number (too large or division by zero)."); errored = true; } - if (!errored && !( - argType->mobileType() && - argType->mobileType()->interfaceType(false) && - argType->mobileType()->interfaceType(false)->encodingType() && - !(dynamic_cast(argType->mobileType()->interfaceType(false)->encodingType().get())) - )) - m_errorReporter.typeError(arguments[i]->location(), "This type cannot be encoded."); + 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(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( diff --git a/test/libsolidity/SolidityEndToEndTest.cpp b/test/libsolidity/SolidityEndToEndTest.cpp index 8b0e3f83f..49cb5d4d3 100644 --- a/test/libsolidity/SolidityEndToEndTest.cpp +++ b/test/libsolidity/SolidityEndToEndTest.cpp @@ -11234,6 +11234,53 @@ BOOST_AUTO_TEST_CASE(abi_encode_with_selector) ABI_CHECK(callContractFunction("f3()"), expectation); } +BOOST_AUTO_TEST_CASE(abi_encode_with_selectorv2) +{ + char const* sourceCode = R"( + pragma experimental ABIEncoderV2; + contract C { + function f0() public pure returns (bytes) { + return abi.encodeWithSelector(0x12345678); + } + function f1() public pure returns (bytes) { + return abi.encodeWithSelector(0x12345678, "abc"); + } + function f2() public pure returns (bytes) { + bytes4 x = 0x12345678; + return abi.encodeWithSelector(x, "abc"); + } + function f3() public pure returns (bytes) { + bytes4 x = 0x12345678; + return abi.encodeWithSelector(x, uint(-1)); + } + struct S { uint a; string b; uint16 c; } + function f4() public pure returns (bytes) { + bytes4 x = 0x12345678; + S memory s; + s.a = 0x1234567; + s.b = "Lorem ipsum dolor sit ethereum........"; + s.c = 0x1234; + return abi.encodeWithSelector(x, uint(-1), s, uint(3)); + } + } + )"; + compileAndRun(sourceCode, 0, "C"); + ABI_CHECK(callContractFunction("f0()"), encodeArgs(0x20, 4, "\x12\x34\x56\x78")); + bytes expectation; + expectation = encodeArgs(0x20, 4 + 0x60) + bytes{0x12, 0x34, 0x56, 0x78} + encodeArgs(0x20, 3, "abc") + bytes(0x20 - 4); + ABI_CHECK(callContractFunction("f1()"), expectation); + expectation = encodeArgs(0x20, 4 + 0x60) + bytes{0x12, 0x34, 0x56, 0x78} + encodeArgs(0x20, 3, "abc") + bytes(0x20 - 4); + ABI_CHECK(callContractFunction("f2()"), expectation); + expectation = encodeArgs(0x20, 4 + 0x20) + bytes{0x12, 0x34, 0x56, 0x78} + encodeArgs(u256(-1)) + bytes(0x20 - 4); + ABI_CHECK(callContractFunction("f3()"), expectation); + expectation = + encodeArgs(0x20, 4 + 0x120) + + bytes{0x12, 0x34, 0x56, 0x78} + + encodeArgs(u256(-1), 0x60, u256(3), 0x1234567, 0x60, 0x1234, 38, "Lorem ipsum dolor sit ethereum........") + + bytes(0x20 - 4); + ABI_CHECK(callContractFunction("f4()"), expectation); +} + BOOST_AUTO_TEST_CASE(abi_encode_with_signature) { char const* sourceCode = R"T( @@ -11277,6 +11324,65 @@ BOOST_AUTO_TEST_CASE(abi_encode_with_signature) ABI_CHECK(callContractFunction("f2()"), expectation); } +BOOST_AUTO_TEST_CASE(abi_encode_with_signaturev2) +{ + char const* sourceCode = R"T( + pragma experimental ABIEncoderV2; + contract C { + function f0() public pure returns (bytes) { + return abi.encodeWithSignature("f(uint256)"); + } + function f1() public pure returns (bytes) { + string memory x = "f(uint256)"; + return abi.encodeWithSignature(x, "abc"); + } + string xstor; + function f1s() public returns (bytes) { + xstor = "f(uint256)"; + return abi.encodeWithSignature(xstor, "abc"); + } + function f2() public pure returns (bytes r, uint[] ar) { + string memory x = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua."; + uint[] memory y = new uint[](4); + y[0] = uint(-1); + y[1] = uint(-2); + y[2] = uint(-3); + y[3] = uint(-4); + r = abi.encodeWithSignature(x, y); + // The hash uses temporary memory. This allocation re-uses the memory + // and should initialize it properly. + ar = new uint[](2); + } + struct S { uint a; string b; uint16 c; } + function f4() public pure returns (bytes) { + bytes4 x = 0x12345678; + S memory s; + s.a = 0x1234567; + s.b = "Lorem ipsum dolor sit ethereum........"; + s.c = 0x1234; + return abi.encodeWithSignature(s.b, uint(-1), s, uint(3)); + } + } + )T"; + compileAndRun(sourceCode, 0, "C"); + ABI_CHECK(callContractFunction("f0()"), encodeArgs(0x20, 4, "\xb3\xde\x64\x8b")); + bytes expectation; + expectation = encodeArgs(0x20, 4 + 0x60) + bytes{0xb3, 0xde, 0x64, 0x8b} + encodeArgs(0x20, 3, "abc") + bytes(0x20 - 4); + ABI_CHECK(callContractFunction("f1()"), expectation); + ABI_CHECK(callContractFunction("f1s()"), expectation); + expectation = + encodeArgs(0x40, 0x140, 4 + 0xc0) + + (bytes{0xe9, 0xc9, 0x21, 0xcd} + encodeArgs(0x20, 4, u256(-1), u256(-2), u256(-3), u256(-4)) + bytes(0x20 - 4)) + + encodeArgs(2, 0, 0); + ABI_CHECK(callContractFunction("f2()"), expectation); + expectation = + encodeArgs(0x20, 4 + 0x120) + + bytes{0x7c, 0x79, 0x30, 0x02} + + encodeArgs(u256(-1), 0x60, u256(3), 0x1234567, 0x60, 0x1234, 38, "Lorem ipsum dolor sit ethereum........") + + bytes(0x20 - 4); + ABI_CHECK(callContractFunction("f4()"), expectation); +} + BOOST_AUTO_TEST_CASE(abi_encode_call) { char const* sourceCode = R"T( diff --git a/test/libsolidity/syntaxTests/specialFunctions/abi_encode_structs.sol b/test/libsolidity/syntaxTests/specialFunctions/abi_encode_structs.sol new file mode 100644 index 000000000..d9eebee42 --- /dev/null +++ b/test/libsolidity/syntaxTests/specialFunctions/abi_encode_structs.sol @@ -0,0 +1,17 @@ +contract C { + struct S { uint x; } + S s; + struct T { uint y; } + T t; + function f() public view { + abi.encode(s, t); + } + function g() public view { + abi.encodePacked(s, t); + } +} +// ---- +// TypeError: (131-132): This type cannot be encoded. +// TypeError: (134-135): This type cannot be encoded. +// TypeError: (200-201): This type cannot be encoded. +// TypeError: (203-204): This type cannot be encoded. diff --git a/test/libsolidity/syntaxTests/specialFunctions/abi_encode_structs_abiv2.sol b/test/libsolidity/syntaxTests/specialFunctions/abi_encode_structs_abiv2.sol new file mode 100644 index 000000000..d6cf60e47 --- /dev/null +++ b/test/libsolidity/syntaxTests/specialFunctions/abi_encode_structs_abiv2.sol @@ -0,0 +1,18 @@ +pragma experimental ABIEncoderV2; + +contract C { + struct S { uint x; } + S s; + struct T { uint y; } + T t; + function f() public view { + abi.encode(s, t); + } + function g() public view { + abi.encodePacked(s, t); + } +} +// ---- +// Warning: (0-33): Experimental features are turned on. Do not use experimental features on live deployments. +// TypeError: (235-236): This type cannot be encoded. +// TypeError: (238-239): 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 cc354819f..fa9102607 100644 --- a/test/libsolidity/syntaxTests/specialFunctions/types_with_unspecified_encoding_structs.sol +++ b/test/libsolidity/syntaxTests/specialFunctions/types_with_unspecified_encoding_structs.sol @@ -1,14 +1,13 @@ contract C { struct S { uint x; } S s; - struct T { } + struct T { uint y; } T t; - function f() public pure { + function f() public view { bytes32 a = sha256(s, t); a; } } // ---- -// Warning: (51-63): Defining empty structs is deprecated. -// TypeError: (131-132): This type cannot be encoded. -// TypeError: (134-135): This type cannot be encoded. +// TypeError: (139-140): This type cannot be encoded. +// TypeError: (142-143): 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 new file mode 100644 index 000000000..1187ce4a7 --- /dev/null +++ b/test/libsolidity/syntaxTests/specialFunctions/types_with_unspecified_encoding_structs_abiv2.sol @@ -0,0 +1,16 @@ +pragma experimental ABIEncoderV2; + +contract C { + struct S { uint x; } + S s; + struct T { uint y; } + T t; + function f() public view { + bytes32 a = sha256(s, t); + a; + } +} +// ---- +// Warning: (0-33): Experimental features are turned on. Do not use experimental features on live deployments. +// TypeError: (174-175): This type cannot be encoded. +// TypeError: (177-178): This type cannot be encoded. From db40bd46afe6c33af2f3b7e0d9a268e088592b2c Mon Sep 17 00:00:00 2001 From: Daniel Kirchner Date: Thu, 12 Apr 2018 15:10:11 +0200 Subject: [PATCH 176/197] Add additional test for ``var`` for storage variables. --- .../syntaxTests/parsing/return_var.sol | 20 +++++++++++++++---- .../parsing/var_in_function_arguments.sol | 18 ++++++++++++++--- .../syntaxTests/parsing/var_storage_var.sol | 5 +++++ 3 files changed, 36 insertions(+), 7 deletions(-) create mode 100644 test/libsolidity/syntaxTests/parsing/var_storage_var.sol diff --git a/test/libsolidity/syntaxTests/parsing/return_var.sol b/test/libsolidity/syntaxTests/parsing/return_var.sol index 113032a75..47ac9ef05 100644 --- a/test/libsolidity/syntaxTests/parsing/return_var.sol +++ b/test/libsolidity/syntaxTests/parsing/return_var.sol @@ -1,13 +1,25 @@ contract C { function f() returns(var) {} function f() returns(var x) {} + function f() returns(var x, uint y) {} + function f() returns(uint x, var y) {} + function f() returns(var x, var y) {} function f() public pure returns (var storage) {} function f() public pure returns (var storage x) {} + function f() public pure returns (var storage x, var storage y) {} } // ---- // ParserError: (38-38): Expected explicit type name. // ParserError: (71-71): Expected explicit type name. -// ParserError: (119-119): Expected explicit type name. -// ParserError: (123-123): Location specifier needs explicit type name. -// ParserError: (173-173): Expected explicit type name. -// ParserError: (177-177): Location specifier needs explicit type name. +// ParserError: (106-106): Expected explicit type name. +// ParserError: (157-157): Expected explicit type name. +// ParserError: (192-192): Expected explicit type name. +// ParserError: (199-199): Expected explicit type name. +// ParserError: (247-247): Expected explicit type name. +// ParserError: (251-251): Location specifier needs explicit type name. +// ParserError: (301-301): Expected explicit type name. +// ParserError: (305-305): Location specifier needs explicit type name. +// ParserError: (357-357): Expected explicit type name. +// ParserError: (361-361): Location specifier needs explicit type name. +// ParserError: (372-372): Expected explicit type name. +// ParserError: (376-376): Location specifier needs explicit type name. diff --git a/test/libsolidity/syntaxTests/parsing/var_in_function_arguments.sol b/test/libsolidity/syntaxTests/parsing/var_in_function_arguments.sol index cb166c7d8..e041247d0 100644 --- a/test/libsolidity/syntaxTests/parsing/var_in_function_arguments.sol +++ b/test/libsolidity/syntaxTests/parsing/var_in_function_arguments.sol @@ -1,13 +1,25 @@ contract C { function f(var) public pure {} function f(var x) public pure {} + function f(var x, var y) public pure {} + function f(uint x, var y) public pure {} + function f(var x, uint y) public pure {} function f(var storage) public pure {} function f(var storage x) public pure {} + function f(var storage x, var storage y) public pure {} } // ---- // ParserError: (28-28): Expected explicit type name. // ParserError: (63-63): Expected explicit type name. // ParserError: (100-100): Expected explicit type name. -// ParserError: (104-104): Location specifier needs explicit type name. -// ParserError: (143-143): Expected explicit type name. -// ParserError: (147-147): Location specifier needs explicit type name. +// ParserError: (107-107): Expected explicit type name. +// ParserError: (152-152): Expected explicit type name. +// ParserError: (189-189): Expected explicit type name. +// ParserError: (234-234): Expected explicit type name. +// ParserError: (238-238): Location specifier needs explicit type name. +// ParserError: (277-277): Expected explicit type name. +// ParserError: (281-281): Location specifier needs explicit type name. +// ParserError: (322-322): Expected explicit type name. +// ParserError: (326-326): Location specifier needs explicit type name. +// ParserError: (337-337): Expected explicit type name. +// ParserError: (341-341): Location specifier needs explicit type name. diff --git a/test/libsolidity/syntaxTests/parsing/var_storage_var.sol b/test/libsolidity/syntaxTests/parsing/var_storage_var.sol new file mode 100644 index 000000000..431d4ca55 --- /dev/null +++ b/test/libsolidity/syntaxTests/parsing/var_storage_var.sol @@ -0,0 +1,5 @@ +contract C { + var a; +} +// ---- +// ParserError: (17-17): Function, variable, struct or modifier declaration expected. From 966367305ad511900bedfd9af08114a0b1307399 Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 12 Apr 2018 20:13:16 +0200 Subject: [PATCH 177/197] Remove dead code and clarify throw. --- docs/control-structures.rst | 2 +- test/libsolidity/Assembly.cpp | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/docs/control-structures.rst b/docs/control-structures.rst index 7024a6840..879e26f7a 100644 --- a/docs/control-structures.rst +++ b/docs/control-structures.rst @@ -457,7 +457,7 @@ 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 ``throw`` keyword can also be used as an alternative to ``revert()``, but is deprecated. +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. diff --git a/test/libsolidity/Assembly.cpp b/test/libsolidity/Assembly.cpp index bdb7a1073..77ca363a8 100644 --- a/test/libsolidity/Assembly.cpp +++ b/test/libsolidity/Assembly.cpp @@ -157,7 +157,6 @@ BOOST_AUTO_TEST_CASE(location_test) } } )"; - shared_ptr n = make_shared(""); AssemblyItems items = compileContract(sourceCode); vector locations = vector(24, SourceLocation(2, 75, make_shared(""))) + From 8935c0dd2f47a20ed7cc2d1b6e11fa7cdd978b79 Mon Sep 17 00:00:00 2001 From: Erik Kundt Date: Wed, 21 Mar 2018 16:40:19 +0100 Subject: [PATCH 178/197] Uses short string representation of TypePointer --- libsolidity/ast/ASTJsonConverter.cpp | 16 +++++++-------- libsolidity/ast/ASTJsonConverter.h | 2 +- test/libsolidity/ASTJSON.cpp | 30 ++++++++++++++++++++++++++++ 3 files changed, 39 insertions(+), 9 deletions(-) diff --git a/libsolidity/ast/ASTJsonConverter.cpp b/libsolidity/ast/ASTJsonConverter.cpp index 95ba30893..b8e00b604 100644 --- a/libsolidity/ast/ASTJsonConverter.cpp +++ b/libsolidity/ast/ASTJsonConverter.cpp @@ -134,10 +134,10 @@ string ASTJsonConverter::namePathToString(std::vector const& _namePat return boost::algorithm::join(_namePath, "."); } -Json::Value ASTJsonConverter::typePointerToJson(TypePointer _tp) +Json::Value ASTJsonConverter::typePointerToJson(TypePointer _tp, bool _short) { Json::Value typeDescriptions(Json::objectValue); - typeDescriptions["typeString"] = _tp ? Json::Value(_tp->toString()) : Json::nullValue; + typeDescriptions["typeString"] = _tp ? Json::Value(_tp->toString(_short)) : Json::nullValue; typeDescriptions["typeIdentifier"] = _tp ? Json::Value(_tp->identifier()) : Json::nullValue; return typeDescriptions; @@ -354,7 +354,7 @@ bool ASTJsonConverter::visit(VariableDeclaration const& _node) make_pair("visibility", Declaration::visibilityToString(_node.visibility())), make_pair("value", _node.value() ? toJson(*_node.value()) : Json::nullValue), make_pair("scope", idOrNull(_node.scope())), - make_pair("typeDescriptions", typePointerToJson(_node.annotation().type)) + make_pair("typeDescriptions", typePointerToJson(_node.annotation().type, true)) }; if (m_inEvent) attributes.push_back(make_pair("indexed", _node.isIndexed())); @@ -399,7 +399,7 @@ bool ASTJsonConverter::visit(ElementaryTypeName const& _node) { setJsonNode(_node, "ElementaryTypeName", { make_pair("name", _node.typeName().toString()), - make_pair("typeDescriptions", typePointerToJson(_node.annotation().type)) + make_pair("typeDescriptions", typePointerToJson(_node.annotation().type, true)) }); return false; } @@ -410,7 +410,7 @@ bool ASTJsonConverter::visit(UserDefinedTypeName const& _node) make_pair("name", namePathToString(_node.namePath())), make_pair("referencedDeclaration", idOrNull(_node.annotation().referencedDeclaration)), make_pair("contractScope", idOrNull(_node.annotation().contractScope)), - make_pair("typeDescriptions", typePointerToJson(_node.annotation().type)) + make_pair("typeDescriptions", typePointerToJson(_node.annotation().type, true)) }); return false; } @@ -425,7 +425,7 @@ bool ASTJsonConverter::visit(FunctionTypeName const& _node) 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)) + make_pair("typeDescriptions", typePointerToJson(_node.annotation().type, true)) }); return false; } @@ -435,7 +435,7 @@ bool ASTJsonConverter::visit(Mapping const& _node) setJsonNode(_node, "Mapping", { make_pair("keyType", toJson(_node.keyType())), make_pair("valueType", toJson(_node.valueType())), - make_pair("typeDescriptions", typePointerToJson(_node.annotation().type)) + make_pair("typeDescriptions", typePointerToJson(_node.annotation().type, true)) }); return false; } @@ -445,7 +445,7 @@ bool ASTJsonConverter::visit(ArrayTypeName const& _node) setJsonNode(_node, "ArrayTypeName", { make_pair("baseType", toJson(_node.baseType())), make_pair("length", toJsonOrNull(_node.length())), - make_pair("typeDescriptions", typePointerToJson(_node.annotation().type)) + make_pair("typeDescriptions", typePointerToJson(_node.annotation().type, true)) }); return false; } diff --git a/libsolidity/ast/ASTJsonConverter.h b/libsolidity/ast/ASTJsonConverter.h index 88b93699a..29712f3bb 100644 --- a/libsolidity/ast/ASTJsonConverter.h +++ b/libsolidity/ast/ASTJsonConverter.h @@ -152,7 +152,7 @@ private: } return tmp; } - static Json::Value typePointerToJson(TypePointer _tp); + static Json::Value typePointerToJson(TypePointer _tp, bool _short = false); static Json::Value typePointerToJson(std::shared_ptr> _tps); void appendExpressionAttributes( std::vector> &_attributes, diff --git a/test/libsolidity/ASTJSON.cpp b/test/libsolidity/ASTJSON.cpp index b44dd331a..b585d13ff 100644 --- a/test/libsolidity/ASTJSON.cpp +++ b/test/libsolidity/ASTJSON.cpp @@ -181,6 +181,36 @@ BOOST_AUTO_TEST_CASE(array_type_name) 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 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 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(placeholder_statement) { CompilerStack c; From 824008340ae3ba6c1ad89d05942db3839d1fa4a5 Mon Sep 17 00:00:00 2001 From: Erik Kundt Date: Mon, 9 Apr 2018 13:35:03 +0200 Subject: [PATCH 179/197] Adds type expectations to legacy tests. --- test/libsolidity/ASTJSON.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/test/libsolidity/ASTJSON.cpp b/test/libsolidity/ASTJSON.cpp index b585d13ff..e3467ba29 100644 --- a/test/libsolidity/ASTJSON.cpp +++ b/test/libsolidity/ASTJSON.cpp @@ -66,6 +66,7 @@ BOOST_AUTO_TEST_CASE(source_location) 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) @@ -176,6 +177,9 @@ BOOST_AUTO_TEST_CASE(array_type_name) map 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"); @@ -234,6 +238,9 @@ BOOST_AUTO_TEST_CASE(non_utf8) map 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"); From 34da3e634f564d4aa994801ff54a61c80b0f463b Mon Sep 17 00:00:00 2001 From: Erik Kundt Date: Mon, 9 Apr 2018 14:14:59 +0200 Subject: [PATCH 180/197] Separates tests for legacy and compact output. --- test/libsolidity/ASTJSON.cpp | 239 +-------------------- test/libsolidity/ASTJSONLegacy.cpp | 324 +++++++++++++++++++++++++++++ 2 files changed, 332 insertions(+), 231 deletions(-) create mode 100644 test/libsolidity/ASTJSONLegacy.cpp diff --git a/test/libsolidity/ASTJSON.cpp b/test/libsolidity/ASTJSON.cpp index e3467ba29..d22db18dc 100644 --- a/test/libsolidity/ASTJSON.cpp +++ b/test/libsolidity/ASTJSON.cpp @@ -41,150 +41,6 @@ namespace test BOOST_AUTO_TEST_SUITE(SolidityASTJSON) -BOOST_AUTO_TEST_CASE(smoke_test) -{ - CompilerStack c; - c.addSource("a", "contract C {}"); - c.setEVMVersion(dev::test::Options::get().evmVersion()); - c.parseAndAnalyze(); - map 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 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 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 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 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 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 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 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 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; @@ -215,72 +71,6 @@ BOOST_AUTO_TEST_CASE(short_type_name_ref) BOOST_CHECK_EQUAL(varDecl["typeName"]["typeDescriptions"]["typeString"], "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 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 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 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; @@ -303,30 +93,17 @@ BOOST_AUTO_TEST_CASE(documentation) 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."); //same tests for non-legacy mode - astJsonA = ASTJsonConverter(false, sourceIndices).toJson(c.ast("a")); - documentationA = astJsonA["nodes"][0]["documentation"]; + 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"); - astJsonB = ASTJsonConverter(false, sourceIndices).toJson(c.ast("b")); - documentationB = astJsonB["nodes"][0]["documentation"]; + 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."); - astJsonC = ASTJsonConverter(false, sourceIndices).toJson(c.ast("c")); - documentationC0 = astJsonC["nodes"][0]["nodes"][0]["documentation"]; - documentationC1 = astJsonC["nodes"][0]["nodes"][1]["documentation"]; - documentationC2 = astJsonC["nodes"][0]["nodes"][2]["documentation"]; + 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."); diff --git a/test/libsolidity/ASTJSONLegacy.cpp b/test/libsolidity/ASTJSONLegacy.cpp new file mode 100644 index 000000000..ebe1b0077 --- /dev/null +++ b/test/libsolidity/ASTJSONLegacy.cpp @@ -0,0 +1,324 @@ +/* + 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 . +*/ +/** + * @author Christian + * @date 2016 + * Tests for the json ast output. + */ + +#include + +#include +#include +#include + +#include + +#include + +using namespace std; + +namespace dev +{ +namespace solidity +{ +namespace test +{ + +BOOST_AUTO_TEST_SUITE(SolidityASTJSONLegacy) + +BOOST_AUTO_TEST_CASE(smoke_test) +{ + CompilerStack c; + c.addSource("a", "contract C {}"); + c.setEVMVersion(dev::test::Options::get().evmVersion()); + c.parseAndAnalyze(); + map 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 From 6c656a93911d92bc8de7191087e9854eac4d69a4 Mon Sep 17 00:00:00 2001 From: Erik Kundt Date: Mon, 9 Apr 2018 17:30:39 +0200 Subject: [PATCH 181/197] Adds unit test that covers long typeDescription and renames suite. --- test/libsolidity/ASTJSON.cpp | 28 +++++++++++++++++++ .../{ASTJSONLegacy.cpp => ASTLegacyJSON.cpp} | 2 +- 2 files changed, 29 insertions(+), 1 deletion(-) rename test/libsolidity/{ASTJSONLegacy.cpp => ASTLegacyJSON.cpp} (99%) diff --git a/test/libsolidity/ASTJSON.cpp b/test/libsolidity/ASTJSON.cpp index d22db18dc..5d5b14e8b 100644 --- a/test/libsolidity/ASTJSON.cpp +++ b/test/libsolidity/ASTJSON.cpp @@ -71,6 +71,34 @@ BOOST_AUTO_TEST_CASE(short_type_name_ref) 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 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 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; diff --git a/test/libsolidity/ASTJSONLegacy.cpp b/test/libsolidity/ASTLegacyJSON.cpp similarity index 99% rename from test/libsolidity/ASTJSONLegacy.cpp rename to test/libsolidity/ASTLegacyJSON.cpp index ebe1b0077..13148682c 100644 --- a/test/libsolidity/ASTJSONLegacy.cpp +++ b/test/libsolidity/ASTLegacyJSON.cpp @@ -39,7 +39,7 @@ namespace solidity namespace test { -BOOST_AUTO_TEST_SUITE(SolidityASTJSONLegacy) +BOOST_AUTO_TEST_SUITE(SolidityASTLegacyJSON) BOOST_AUTO_TEST_CASE(smoke_test) { From 4bd31aaecad0decd6a3e1d7031761cb42163bbbb Mon Sep 17 00:00:00 2001 From: Daniel Kirchner Date: Thu, 12 Apr 2018 11:08:40 +0200 Subject: [PATCH 182/197] Add ChangeLog entry. --- Changelog.md | 1 + 1 file changed, 1 insertion(+) diff --git a/Changelog.md b/Changelog.md index f56a746e2..80f02a2d0 100644 --- a/Changelog.md +++ b/Changelog.md @@ -32,6 +32,7 @@ Bugfixes: * Commandline interface: Support ``--evm-version constantinople`` properly. * DocString Parser: Fix error message for empty descriptions. * Gas Estimator: Correctly ignore costs of fallback function for other functions. + * JSON AST: Remove storage qualifier for type name strings. * Parser: Fix internal compiler error when parsing ``var`` declaration without identifier. * Parser: Fix parsing of getters for function type variables. * Standard JSON: Support ``constantinople`` as ``evmVersion`` properly. From be37e3a912f6d5a2a57544f60362be65b7be8284 Mon Sep 17 00:00:00 2001 From: Daniel Kirchner Date: Thu, 12 Apr 2018 18:14:48 +0200 Subject: [PATCH 183/197] Stricter check for member access to "this" in constructor. --- libsolidity/analysis/StaticAnalyzer.cpp | 30 ++++++++++++++++--- .../syntaxTests/constructor_this.sol | 12 ++++++++ .../parsing/constructor_allowed_this.sol | 28 +++++++++++++++++ .../syntaxTests/parsing/constructor_super.sol | 10 +++++++ 4 files changed, 76 insertions(+), 4 deletions(-) create mode 100644 test/libsolidity/syntaxTests/constructor_this.sol create mode 100644 test/libsolidity/syntaxTests/parsing/constructor_allowed_this.sol create mode 100644 test/libsolidity/syntaxTests/parsing/constructor_super.sol diff --git a/libsolidity/analysis/StaticAnalyzer.cpp b/libsolidity/analysis/StaticAnalyzer.cpp index 51aa0b286..00a581d04 100644 --- a/libsolidity/analysis/StaticAnalyzer.cpp +++ b/libsolidity/analysis/StaticAnalyzer.cpp @@ -206,10 +206,32 @@ bool StaticAnalyzer::visit(MemberAccess const& _memberAccess) ); } - if (m_constructor && m_currentContract) - if (ContractType const* type = dynamic_cast(_memberAccess.expression().annotation().type.get())) - if (type->contractDefinition() == *m_currentContract) - m_errorReporter.warning(_memberAccess.location(), "\"this\" used in constructor."); + if (m_constructor) + { + auto const* expr = &_memberAccess.expression(); + while(expr) + { + if (auto id = dynamic_cast(expr)) + { + if (id->name() == "this") + m_errorReporter.warning( + id->location(), + "\"this\" used in constructor. " + "Note that external functions of a contract " + "cannot be called while it is being constructed."); + break; + } + else if (auto tuple = dynamic_cast(expr)) + { + if (tuple->components().size() == 1) + expr = tuple->components().front().get(); + else + break; + } + else + break; + } + } return true; } diff --git a/test/libsolidity/syntaxTests/constructor_this.sol b/test/libsolidity/syntaxTests/constructor_this.sol new file mode 100644 index 000000000..9d22a1618 --- /dev/null +++ b/test/libsolidity/syntaxTests/constructor_this.sol @@ -0,0 +1,12 @@ +contract C { + function f() public pure {} + constructor() public { + C c = this; + c.f(); // this does not warn now, but should warn in the future + this.f(); + (this).f(); + } +} +// ---- +// Warning: (172-176): "this" used in constructor. Note that external functions of a contract cannot be called while it is being constructed. +// Warning: (191-195): "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/parsing/constructor_allowed_this.sol b/test/libsolidity/syntaxTests/parsing/constructor_allowed_this.sol new file mode 100644 index 000000000..9f714aeab --- /dev/null +++ b/test/libsolidity/syntaxTests/parsing/constructor_allowed_this.sol @@ -0,0 +1,28 @@ +contract A { + function a() public pure { + } +} +contract B { + constructor(address) public { + } + function b(address) public returns (A) { + return new A(); + } +} +contract C { + B m_b; + C m_c; + constructor(C other_c) public { + m_c = other_c; + m_b = new B(this); + m_b.b(this).a(); + g(this).f(); + other_c.f(); + m_c.f(); + } + function f() public pure { + } + function g(C) public view returns (C) { + return m_c; + } +} diff --git a/test/libsolidity/syntaxTests/parsing/constructor_super.sol b/test/libsolidity/syntaxTests/parsing/constructor_super.sol new file mode 100644 index 000000000..fa1be1878 --- /dev/null +++ b/test/libsolidity/syntaxTests/parsing/constructor_super.sol @@ -0,0 +1,10 @@ +contract A { + function x() pure internal {} +} + +contract B is A { + constructor() public { + // used to trigger warning about using ``this`` in constructor + super.x(); + } +} From 05c5ab19fb67849d806d8de1b9c3a2982d37f721 Mon Sep 17 00:00:00 2001 From: Daniel Kirchner Date: Fri, 13 Apr 2018 17:28:08 +0200 Subject: [PATCH 184/197] Improve documentation and warning about accessing contract members inherited from address. --- Changelog.md | 1 + docs/types.rst | 4 ++-- libsolidity/analysis/TypeChecker.cpp | 3 ++- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/Changelog.md b/Changelog.md index 80f02a2d0..201450c76 100644 --- a/Changelog.md +++ b/Changelog.md @@ -17,6 +17,7 @@ Features: * Syntax Checker: Issue warning for empty structs (or error as experimental 0.5.0 feature). * Syntax Checker: Warn about modifiers on functions without implementation (this will turn into an error with version 0.5.0). * Syntax Tests: Add source locations to syntax test expectations. + * Type Checker: Improve documentation and warnings for accessing contract members inherited from ``address``. * General: Introduce new constructor syntax using the ``constructor`` keyword as experimental 0.5.0 feature. * 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. diff --git a/docs/types.rst b/docs/types.rst index 07421bdfb..cf8f91bef 100644 --- a/docs/types.rst +++ b/docs/types.rst @@ -179,8 +179,8 @@ All three functions ``call``, ``delegatecall`` and ``callcode`` are very low-lev The ``.gas()`` option is available on all three methods, while the ``.value()`` option is not supported for ``delegatecall``. .. note:: - All contracts inherit the members of address, so it is possible to query the balance of the - current contract using ``this.balance``. + All contracts can be converted to ``address`` type, so it is possible to query the balance of the + current contract using ``address(this).balance``. .. note:: The use of ``callcode`` is discouraged and will be removed in the future. diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index f4df4c94d..47a551dc7 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -1988,7 +1988,8 @@ bool TypeChecker::visit(MemberAccess const& _memberAccess) 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." + " Convert the contract to \"address\" type to access the member," + " for example use \"address(contract)." + memberName + "\" instead." ); } From 559fa58ddf3d6b32242286e392b2695d56594423 Mon Sep 17 00:00:00 2001 From: chriseth Date: Fri, 13 Apr 2018 18:36:33 +0200 Subject: [PATCH 185/197] Document ABI encoding functions. --- Changelog.md | 1 + docs/miscellaneous.rst | 5 +++++ docs/units-and-global-variables.rst | 25 +++++++++++++++++++++++-- 3 files changed, 29 insertions(+), 2 deletions(-) diff --git a/Changelog.md b/Changelog.md index 80f02a2d0..81c8b974b 100644 --- a/Changelog.md +++ b/Changelog.md @@ -8,6 +8,7 @@ Features: * General: Allow providing reason string for ``revert()`` and ``require()``. * General: Limit the number of errors output in a single run to 256. * General: Support accessing dynamic return data in post-byzantium EVMs. + * General: Add encoding routines ``abi.encodePacked``, ``abi.encode``, ``abi.encodeWithSelector`` and ``abi.encodeWithSignature``. * Interfaces: Allow overriding external functions in interfaces with public in an implementing contract. * Optimizer: Optimize ``SHL`` and ``SHR`` only involving constants (Constantinople only). * Optimizer: Remove useless ``SWAP1`` instruction preceding a commutative instruction (such as ``ADD``, ``MUL``, etc). diff --git a/docs/miscellaneous.rst b/docs/miscellaneous.rst index 8270727f9..c7c325285 100644 --- a/docs/miscellaneous.rst +++ b/docs/miscellaneous.rst @@ -317,6 +317,11 @@ The following is the order of precedence for operators, listed in order of evalu Global Variables ================ +- ``abi.encode(...) returns (bytes)``: :ref:`ABI `-encodes the given arguments +- ``abi.encodePacked(...) returns (bytes)``: Performes :ref:`packed encoding ` of the given arguments +- ``abi.encodeWithSelector(bytes4 selector, ...) returns (bytes)``: :ref:`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 - ``block.difficulty`` (``uint``): current block difficulty diff --git a/docs/units-and-global-variables.rst b/docs/units-and-global-variables.rst index 9d5821d58..51f7b9f37 100644 --- a/docs/units-and-global-variables.rst +++ b/docs/units-and-global-variables.rst @@ -44,9 +44,10 @@ Special Variables and Functions =============================== There are special variables and functions which always exist in the global -namespace and are mainly used to provide information about the blockchain. +namespace and are mainly used to provide information about the blockchain +or are general-use utility functions. -.. index:: block, coinbase, difficulty, number, block;number, timestamp, block;timestamp, msg, data, gas, sender, value, now, gas price, origin +.. index:: abi, block, coinbase, difficulty, encode, number, block;number, timestamp, block;timestamp, msg, data, gas, sender, value, now, gas price, origin Block and Transaction Properties @@ -90,6 +91,26 @@ Block and Transaction Properties You can only access the hashes of the most recent 256 blocks, all other values will be zero. +.. index:: abi, encoding, packed + +ABI Encoding 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), ...)``` + +.. 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. + +See the documentation about the :ref:`ABI ` and the +:ref:`tightly packed encoding ` for details about the encoding. + .. index:: assert, revert, require Error Handling From 8a7224683b8fbf7c822922dca35ee2eda8e41d79 Mon Sep 17 00:00:00 2001 From: Lefteris Karapetsas Date: Sun, 15 Apr 2018 23:12:28 +0200 Subject: [PATCH 186/197] Docs: Update solidity version for revert with reason --- docs/common-patterns.rst | 4 ++-- docs/contracts.rst | 4 ++-- docs/control-structures.rst | 4 ++-- docs/solidity-by-example.rst | 8 ++++---- docs/structure-of-a-contract.rst | 2 +- docs/types.rst | 2 +- 6 files changed, 12 insertions(+), 12 deletions(-) diff --git a/docs/common-patterns.rst b/docs/common-patterns.rst index 233bdc4e9..739e136f5 100644 --- a/docs/common-patterns.rst +++ b/docs/common-patterns.rst @@ -130,7 +130,7 @@ restrictions highly readable. :: - pragma solidity ^0.4.11; + pragma solidity ^0.4.22; contract AccessRestriction { // These will be assigned at the construction @@ -282,7 +282,7 @@ function finishes. :: - pragma solidity ^0.4.11; + pragma solidity ^0.4.22; contract StateMachine { enum Stages { diff --git a/docs/contracts.rst b/docs/contracts.rst index 0c697dd6b..dfd2e8a6f 100644 --- a/docs/contracts.rst +++ b/docs/contracts.rst @@ -301,7 +301,7 @@ inheritable properties of contracts and may be overridden by derived contracts. :: - pragma solidity ^0.4.11; + pragma solidity ^0.4.22; contract owned { function owned() public { owner = msg.sender; } @@ -1215,7 +1215,7 @@ more advanced example to implement a set). :: - pragma solidity ^0.4.16; + pragma solidity ^0.4.22; library Set { // We define a new struct datatype that will be used to diff --git a/docs/control-structures.rst b/docs/control-structures.rst index 879e26f7a..f3c351dd1 100644 --- a/docs/control-structures.rst +++ b/docs/control-structures.rst @@ -477,7 +477,7 @@ a message string for ``require``, but not for ``assert``. :: - pragma solidity ^0.4.0; + pragma solidity ^0.4.22; contract Sharer { function sendHalf(address addr) public payable returns (uint balance) { @@ -524,7 +524,7 @@ The following example shows how an error string can be used together with revert :: - pragma solidity ^0.4.0; + pragma solidity ^0.4.22; contract VendingMachine { function buy(uint amount) payable { diff --git a/docs/solidity-by-example.rst b/docs/solidity-by-example.rst index 3cbfcd666..546767e4c 100644 --- a/docs/solidity-by-example.rst +++ b/docs/solidity-by-example.rst @@ -36,7 +36,7 @@ of votes. :: - pragma solidity ^0.4.16; + pragma solidity ^0.4.22; /// @title Voting with delegation. contract Ballot { @@ -225,7 +225,7 @@ activate themselves. :: - pragma solidity ^0.4.21; + pragma solidity ^0.4.22; contract SimpleAuction { // Parameters of the auction. Times are either @@ -388,7 +388,7 @@ high or low invalid bids. :: - pragma solidity ^0.4.21; + pragma solidity ^0.4.22; contract BlindAuction { struct Bid { @@ -541,7 +541,7 @@ Safe Remote Purchase :: - pragma solidity ^0.4.21; + pragma solidity ^0.4.22; contract Purchase { uint public value; diff --git a/docs/structure-of-a-contract.rst b/docs/structure-of-a-contract.rst index 9e5eacbbd..d57f17031 100644 --- a/docs/structure-of-a-contract.rst +++ b/docs/structure-of-a-contract.rst @@ -62,7 +62,7 @@ Function modifiers can be used to amend the semantics of functions in a declarat :: - pragma solidity ^0.4.11; + pragma solidity ^0.4.22; contract Purchase { address public seller; diff --git a/docs/types.rst b/docs/types.rst index 07421bdfb..be8afe482 100644 --- a/docs/types.rst +++ b/docs/types.rst @@ -470,7 +470,7 @@ Example that shows how to use internal function types:: Another example that uses external function types:: - pragma solidity ^0.4.21; + pragma solidity ^0.4.22; contract Oracle { struct Request { From 33fbf88707e69362bd5b6336860827a7b4d74440 Mon Sep 17 00:00:00 2001 From: Erik Kundt Date: Mon, 26 Mar 2018 19:48:20 +0200 Subject: [PATCH 187/197] Limits rational numbers to 4096 bits. --- Changelog.md | 1 + libsolidity/ast/Types.cpp | 180 ++++++++++++++++-- test/libsolidity/SolidityEndToEndTest.cpp | 23 +++ .../rational_number_array_index_limit.sol | 5 + .../types/rational_number_bitshift_limit.sol | 13 ++ .../types/rational_number_div_limit.sol | 9 + .../types/rational_number_exp_limit.sol | 50 +++++ .../types/rational_number_literal_limit_1.sol | 9 + .../types/rational_number_literal_limit_2.sol | 9 + .../types/rational_number_literal_limit_3.sol | 9 + .../types/rational_number_mul_limit.sol | 9 + 11 files changed, 297 insertions(+), 20 deletions(-) create mode 100644 test/libsolidity/syntaxTests/types/rational_number_array_index_limit.sol create mode 100644 test/libsolidity/syntaxTests/types/rational_number_bitshift_limit.sol create mode 100644 test/libsolidity/syntaxTests/types/rational_number_div_limit.sol create mode 100644 test/libsolidity/syntaxTests/types/rational_number_exp_limit.sol create mode 100644 test/libsolidity/syntaxTests/types/rational_number_literal_limit_1.sol create mode 100644 test/libsolidity/syntaxTests/types/rational_number_literal_limit_2.sol create mode 100644 test/libsolidity/syntaxTests/types/rational_number_literal_limit_3.sol create mode 100644 test/libsolidity/syntaxTests/types/rational_number_mul_limit.sol diff --git a/Changelog.md b/Changelog.md index 80f02a2d0..bc8391bca 100644 --- a/Changelog.md +++ b/Changelog.md @@ -41,6 +41,7 @@ Bugfixes: * Type System: Improve error message when attempting to shift by a fractional amount. * Type System: Make external library functions accessible. * Type System: Prevent encoding of weird types. + * Type System: Restrict rational numbers to 4096 bits. * Static Analyzer: Fix non-deterministic order of unused variable warnings. * Static Analyzer: Invalid arithmetic with constant expressions causes errors. diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp index 68b127770..51739cb01 100644 --- a/libsolidity/ast/Types.cpp +++ b/libsolidity/ast/Types.cpp @@ -44,6 +44,85 @@ using namespace std; using namespace dev; using namespace dev::solidity; +namespace +{ + +unsigned int mostSignificantBit(bigint const& _number) +{ +#if BOOST_VERSION < 105500 + solAssert(_number > 0, ""); + bigint number = _number; + unsigned int result = 0; + while (number != 0) + { + number >>= 1; + ++result; + } + return --result; +#else + return boost::multiprecision::msb(_number); +#endif +} + +/// Check whether (_base ** _exp) fits into 4096 bits. +bool fitsPrecisionExp(bigint const& _base, bigint const& _exp) +{ + if (_base == 0) + return true; + + solAssert(_base > 0, ""); + + size_t const bitsMax = 4096; + + unsigned mostSignificantBaseBit = mostSignificantBit(_base); + if (mostSignificantBaseBit == 0) // _base == 1 + return true; + if (mostSignificantBaseBit > bitsMax) // _base >= 2 ^ 4096 + return false; + + bigint bitsNeeded = _exp * (mostSignificantBaseBit + 1); + + return bitsNeeded <= bitsMax; +} + +/// Checks whether _mantissa * (X ** _exp) fits into 4096 bits, +/// where X is given indirectly via _log2OfBase = log2(X). +bool fitsPrecisionBaseX( + bigint const& _mantissa, + double _log2OfBase, + uint32_t _exp +) +{ + if (_mantissa == 0) + return true; + + solAssert(_mantissa > 0, ""); + + size_t const bitsMax = 4096; + + unsigned mostSignificantMantissaBit = mostSignificantBit(_mantissa); + if (mostSignificantMantissaBit > bitsMax) // _mantissa >= 2 ^ 4096 + return false; + + bigint bitsNeeded = mostSignificantMantissaBit + bigint(floor(double(_exp) * _log2OfBase)) + 1; + return bitsNeeded <= bitsMax; +} + +/// Checks whether _mantissa * (10 ** _expBase10) fits into 4096 bits. +bool fitsPrecisionBase10(bigint const& _mantissa, uint32_t _expBase10) +{ + double const log2Of10AwayFromZero = 3.3219280948873624; + return fitsPrecisionBaseX(_mantissa, log2Of10AwayFromZero, _expBase10); +} + +/// Checks whether _mantissa * (2 ** _expBase10) fits into 4096 bits. +bool fitsPrecisionBase2(bigint const& _mantissa, uint32_t _expBase2) +{ + return fitsPrecisionBaseX(_mantissa, 1.0, _expBase2); +} + +} + void StorageOffsets::computeOffsets(TypePointers const& _types) { bigint slotOffset = 0; @@ -689,31 +768,39 @@ tuple RationalNumberType::isValidLiteral(Literal const& _literal } else if (expPoint != _literal.value().end()) { - // parse the exponent + // Parse base and exponent. Checks numeric limit. bigint exp = bigint(string(expPoint + 1, _literal.value().end())); if (exp > numeric_limits::max() || exp < numeric_limits::min()) return make_tuple(false, rational(0)); - // parse the base + uint32_t expAbs = bigint(abs(exp)).convert_to(); + + tuple base = parseRational(string(_literal.value().begin(), expPoint)); + if (!get<0>(base)) return make_tuple(false, rational(0)); value = get<1>(base); if (exp < 0) { - exp *= -1; + if (!fitsPrecisionBase10(abs(value.denominator()), expAbs)) + return make_tuple(false, rational(0)); value /= boost::multiprecision::pow( bigint(10), - exp.convert_to() + expAbs ); } - else + else if (exp > 0) + { + if (!fitsPrecisionBase10(abs(value.numerator()), expAbs)) + return make_tuple(false, rational(0)); value *= boost::multiprecision::pow( bigint(10), - exp.convert_to() + expAbs ); + } } else { @@ -912,16 +999,49 @@ TypePointer RationalNumberType::binaryOperatorResult(Token::Value _operator, Typ using boost::multiprecision::pow; if (other.isFractional()) return TypePointer(); - else if (abs(other.m_value) > numeric_limits::max()) - return TypePointer(); // This will need too much memory to represent. - uint32_t exponent = abs(other.m_value).numerator().convert_to(); - bigint numerator = pow(m_value.numerator(), exponent); - bigint denominator = pow(m_value.denominator(), exponent); - if (other.m_value >= 0) - value = rational(numerator, denominator); + solAssert(other.m_value.denominator() == 1, ""); + bigint const& exp = other.m_value.numerator(); + + // x ** 0 = 1 + // for 0, 1 and -1 the size of the exponent doesn't have to be restricted + if (exp == 0) + value = 1; + else if (m_value.numerator() == 0 || m_value == 1) + value = m_value; + else if (m_value == -1) + { + bigint isOdd = abs(exp) & bigint(1); + value = 1 - 2 * isOdd.convert_to(); + } else - // invert - value = rational(denominator, numerator); + { + if (abs(exp) > numeric_limits::max()) + return TypePointer(); // This will need too much memory to represent. + + uint32_t absExp = bigint(abs(exp)).convert_to(); + + // Limit size to 4096 bits + if (!fitsPrecisionExp(abs(m_value.numerator()), absExp) || !fitsPrecisionExp(abs(m_value.denominator()), absExp)) + return TypePointer(); + + static auto const optimizedPow = [](bigint const& _base, uint32_t _exponent) -> bigint { + if (_base == 1) + return 1; + else if (_base == -1) + return 1 - 2 * int(_exponent & 1); + else + return pow(_base, _exponent); + }; + + bigint numerator = optimizedPow(m_value.numerator(), absExp); + bigint denominator = optimizedPow(m_value.denominator(), absExp); + + if (exp >= 0) + value = rational(numerator, denominator); + else + // invert + value = rational(denominator, numerator); + } break; } case Token::SHL: @@ -933,28 +1053,48 @@ TypePointer RationalNumberType::binaryOperatorResult(Token::Value _operator, Typ return TypePointer(); else if (other.m_value > numeric_limits::max()) return TypePointer(); - uint32_t exponent = other.m_value.numerator().convert_to(); - value = m_value.numerator() * pow(bigint(2), exponent); + if (m_value.numerator() == 0) + value = 0; + else + { + uint32_t exponent = other.m_value.numerator().convert_to(); + if (!fitsPrecisionBase2(abs(m_value.numerator()), exponent)) + return TypePointer(); + value = m_value.numerator() * pow(bigint(2), exponent); + } break; } // NOTE: we're using >> (SAR) to denote right shifting. The type of the LValue // determines the resulting type and the type of shift (SAR or SHR). case Token::SAR: { - using boost::multiprecision::pow; + namespace mp = boost::multiprecision; if (fractional) return TypePointer(); else if (other.m_value < 0) return TypePointer(); else if (other.m_value > numeric_limits::max()) return TypePointer(); - uint32_t exponent = other.m_value.numerator().convert_to(); - value = rational(m_value.numerator() / pow(bigint(2), exponent), 1); + if (m_value.numerator() == 0) + value = 0; + else + { + uint32_t exponent = other.m_value.numerator().convert_to(); + if (exponent > mostSignificantBit(mp::abs(m_value.numerator()))) + value = 0; + else + value = rational(m_value.numerator() / mp::pow(bigint(2), exponent), 1); + } break; } default: return TypePointer(); } + + // verify that numerator and denominator fit into 4096 bit after every operation + if (value.numerator() != 0 && max(mostSignificantBit(abs(value.numerator())), mostSignificantBit(abs(value.denominator()))) > 4096) + return TypePointer(); + return make_shared(value); } } diff --git a/test/libsolidity/SolidityEndToEndTest.cpp b/test/libsolidity/SolidityEndToEndTest.cpp index cbeca2151..8440449cb 100644 --- a/test/libsolidity/SolidityEndToEndTest.cpp +++ b/test/libsolidity/SolidityEndToEndTest.cpp @@ -102,6 +102,29 @@ BOOST_AUTO_TEST_CASE(exp_operator_const_signed) ABI_CHECK(callContractFunction("f()", bytes()), toBigEndian(u256(-8))); } +BOOST_AUTO_TEST_CASE(exp_zero) +{ + char const* sourceCode = R"( + contract test { + function f(uint a) returns(uint d) { return a ** 0; } + } + )"; + compileAndRun(sourceCode); + testContractAgainstCppOnRange("f(uint256)", [](u256 const&) -> u256 { return u256(1); }, 0, 16); +} + +BOOST_AUTO_TEST_CASE(exp_zero_literal) +{ + char const* sourceCode = R"( + contract test { + function f() returns(uint d) { return 0 ** 0; } + } + )"; + compileAndRun(sourceCode); + ABI_CHECK(callContractFunction("f()", bytes()), toBigEndian(u256(1))); +} + + BOOST_AUTO_TEST_CASE(conditional_expression_true_literal) { char const* sourceCode = R"( diff --git a/test/libsolidity/syntaxTests/types/rational_number_array_index_limit.sol b/test/libsolidity/syntaxTests/types/rational_number_array_index_limit.sol new file mode 100644 index 000000000..45ede998a --- /dev/null +++ b/test/libsolidity/syntaxTests/types/rational_number_array_index_limit.sol @@ -0,0 +1,5 @@ +contract c { + uint[2**253] data; +} +// ---- +// Warning: (17-34): 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/types/rational_number_bitshift_limit.sol b/test/libsolidity/syntaxTests/types/rational_number_bitshift_limit.sol new file mode 100644 index 000000000..94981aa0c --- /dev/null +++ b/test/libsolidity/syntaxTests/types/rational_number_bitshift_limit.sol @@ -0,0 +1,13 @@ +contract c { + function f() public pure { + int a; + a = 1 << 4095; // shift is fine, but result too large + a = 1 << 4096; // too large + a = (1E1233) << 2; // too large + } +} +// ---- +// TypeError: (71-80): Type int_const 5221...(1225 digits omitted)...5168 is not implicitly convertible to expected type int256. +// TypeError: (133-142): Operator << not compatible with types int_const 1 and int_const 4096 +// TypeError: (169-182): Operator << not compatible with types int_const 1000...(1226 digits omitted)...0000 and int_const 2 +// TypeError: (169-182): Type int_const 1000...(1226 digits omitted)...0000 is not implicitly convertible to expected type int256. diff --git a/test/libsolidity/syntaxTests/types/rational_number_div_limit.sol b/test/libsolidity/syntaxTests/types/rational_number_div_limit.sol new file mode 100644 index 000000000..1b0b5f945 --- /dev/null +++ b/test/libsolidity/syntaxTests/types/rational_number_div_limit.sol @@ -0,0 +1,9 @@ +contract c { + function f() public pure { + int a; + a = 1/(2<<4094)/(2<<4094); + } +} +// ---- +// TypeError: (71-92): Operator / not compatible with types rational_const 1 / 5221...(1225 digits omitted)...5168 and int_const 5221...(1225 digits omitted)...5168 +// TypeError: (71-92): Type rational_const 1 / 5221...(1225 digits omitted)...5168 is not implicitly convertible to expected type int256. Try converting to type ufixed8x80 or use an explicit conversion. diff --git a/test/libsolidity/syntaxTests/types/rational_number_exp_limit.sol b/test/libsolidity/syntaxTests/types/rational_number_exp_limit.sol new file mode 100644 index 000000000..6785f580a --- /dev/null +++ b/test/libsolidity/syntaxTests/types/rational_number_exp_limit.sol @@ -0,0 +1,50 @@ +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_literal_limit_1.sol b/test/libsolidity/syntaxTests/types/rational_number_literal_limit_1.sol new file mode 100644 index 000000000..233857a32 --- /dev/null +++ b/test/libsolidity/syntaxTests/types/rational_number_literal_limit_1.sol @@ -0,0 +1,9 @@ +contract c { + function bignum() public { + uint256 a; + a = 1e1233 / 1e1233; // 1e1233 is still fine + a = 1e1234; // 1e1234 is too big + } +} +// ---- +// TypeError: (128-134): Invalid literal value. diff --git a/test/libsolidity/syntaxTests/types/rational_number_literal_limit_2.sol b/test/libsolidity/syntaxTests/types/rational_number_literal_limit_2.sol new file mode 100644 index 000000000..166739248 --- /dev/null +++ b/test/libsolidity/syntaxTests/types/rational_number_literal_limit_2.sol @@ -0,0 +1,9 @@ +contract c { + function bignum() public { + uint a; + a = 134562324532464234452335168163516E1200 / 134562324532464234452335168163516E1200; // still fine + a = 1345623245324642344523351681635168E1200; // too large + } +} +// ---- +// TypeError: (179-218): Invalid literal value. diff --git a/test/libsolidity/syntaxTests/types/rational_number_literal_limit_3.sol b/test/libsolidity/syntaxTests/types/rational_number_literal_limit_3.sol new file mode 100644 index 000000000..5a6961718 --- /dev/null +++ b/test/libsolidity/syntaxTests/types/rational_number_literal_limit_3.sol @@ -0,0 +1,9 @@ +contract c { + function bignum() public { + uint a; + a = 134562324532464.234452335168163517E1200 / 134562324532464.234452335168163517E1200; // still fine + a = 134562324532464.2344523351681635177E1200; // too large + } +} +// ---- +// TypeError: (181-221): Invalid literal value. diff --git a/test/libsolidity/syntaxTests/types/rational_number_mul_limit.sol b/test/libsolidity/syntaxTests/types/rational_number_mul_limit.sol new file mode 100644 index 000000000..bbed94b59 --- /dev/null +++ b/test/libsolidity/syntaxTests/types/rational_number_mul_limit.sol @@ -0,0 +1,9 @@ +contract c { + function f() public pure { + int a; + a = (1<<4095)*(1<<4095); + } +} +// ---- +// TypeError: (71-90): Operator * not compatible with types int_const 5221...(1225 digits omitted)...5168 and int_const 5221...(1225 digits omitted)...5168 +// TypeError: (71-90): Type int_const 5221...(1225 digits omitted)...5168 is not implicitly convertible to expected type int256. From cdb0bbb5ecbe3fe9b40559c7b84e0674d0023bbf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Mon, 16 Apr 2018 12:02:38 +0200 Subject: [PATCH 188/197] CMake: Do no search for ctest --- cmake/EthDependencies.cmake | 7 ------- 1 file changed, 7 deletions(-) diff --git a/cmake/EthDependencies.cmake b/cmake/EthDependencies.cmake index 233ac22a2..cc2f87114 100644 --- a/cmake/EthDependencies.cmake +++ b/cmake/EthDependencies.cmake @@ -34,13 +34,6 @@ endif() set(ETH_CMAKE_DIR ${CMAKE_CURRENT_LIST_DIR}) set(ETH_SCRIPTS_DIR ${ETH_CMAKE_DIR}/scripts) -find_program(CTEST_COMMAND ctest) - -#message(STATUS "CMAKE_PREFIX_PATH ${CMAKE_PREFIX_PATH}") -#message(STATUS "CMake Helper Path: ${ETH_CMAKE_DIR}") -#message(STATUS "CMake Script Path: ${ETH_SCRIPTS_DIR}") -#message(STATUS "ctest path: ${CTEST_COMMAND}") - ## use multithreaded boost libraries, with -mt suffix set(Boost_USE_MULTITHREADED ON) option(Boost_USE_STATIC_LIBS "Link Boost statically" ON) From 1ca8aebf8e46ce2379dca81ec21087228287558e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Mon, 16 Apr 2018 12:16:04 +0200 Subject: [PATCH 189/197] CMake: Clean up devcore dependencies --- libdevcore/CMakeLists.txt | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/libdevcore/CMakeLists.txt b/libdevcore/CMakeLists.txt index d107f7010..fa7e3f489 100644 --- a/libdevcore/CMakeLists.txt +++ b/libdevcore/CMakeLists.txt @@ -2,9 +2,7 @@ file(GLOB sources "*.cpp") file(GLOB headers "*.h") add_library(devcore ${sources} ${headers}) -target_link_libraries(devcore PRIVATE ${JSONCPP_LIBRARY} ${Boost_FILESYSTEM_LIBRARIES} ${Boost_REGEX_LIBRARIES} ${Boost_SYSTEM_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT}) -target_include_directories(devcore SYSTEM PUBLIC ${Boost_INCLUDE_DIRS}) +target_link_libraries(devcore PRIVATE jsoncpp ${Boost_FILESYSTEM_LIBRARIES} ${Boost_REGEX_LIBRARIES} ${Boost_SYSTEM_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT}) target_include_directories(devcore PUBLIC "${CMAKE_SOURCE_DIR}") -target_include_directories(devcore PUBLIC "${JSONCPP_INCLUDE_DIR}") -add_dependencies(devcore jsoncpp) +target_include_directories(devcore SYSTEM PUBLIC ${Boost_INCLUDE_DIRS}) add_dependencies(devcore solidity_BuildInfo.h) From 4c1d39b7a2bc9e58436da0bf85edf5cd74d5a882 Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 12 Apr 2018 00:39:20 +0200 Subject: [PATCH 190/197] Properly force-clean for shortening bytesXX conversions. --- Changelog.md | 1 + libsolidity/codegen/CompilerUtils.cpp | 19 +++++++++---------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/Changelog.md b/Changelog.md index 91048cd58..0d436b6b6 100644 --- a/Changelog.md +++ b/Changelog.md @@ -30,6 +30,7 @@ Bugfixes: * Code Generator: Bugfix in modifier lookup in libraries. * Code Generator: Implement packed encoding of external function types. * Code Generator: Treat empty base constructor argument list as not provided. + * Code Generator: Properly force-clean bytesXX types for shortening conversions. * Commandline interface: Fix error messages for imported files that do not exist. * Commandline interface: Support ``--evm-version constantinople`` properly. * DocString Parser: Fix error message for empty descriptions. diff --git a/libsolidity/codegen/CompilerUtils.cpp b/libsolidity/codegen/CompilerUtils.cpp index b4550153e..4af7d9051 100644 --- a/libsolidity/codegen/CompilerUtils.cpp +++ b/libsolidity/codegen/CompilerUtils.cpp @@ -684,19 +684,17 @@ void CompilerUtils::convertType( // clear for conversion to longer bytes solAssert(targetTypeCategory == Type::Category::FixedBytes, "Invalid type conversion requested."); FixedBytesType const& targetType = dynamic_cast(_targetType); - if (targetType.numBytes() > typeOnStack.numBytes() || _cleanupNeeded) + if (typeOnStack.numBytes() == 0 || targetType.numBytes() == 0) + m_context << Instruction::POP << u256(0); + else if (targetType.numBytes() > typeOnStack.numBytes() || _cleanupNeeded) { - if (typeOnStack.numBytes() == 0) - m_context << Instruction::POP << u256(0); - else - { - m_context << ((u256(1) << (256 - typeOnStack.numBytes() * 8)) - 1); - m_context << Instruction::NOT << Instruction::AND; - } + int bytes = min(typeOnStack.numBytes(), targetType.numBytes()); + m_context << ((u256(1) << (256 - bytes * 8)) - 1); + m_context << Instruction::NOT << Instruction::AND; } } - } break; + } case Type::Category::Enum: solAssert(_targetType == _typeOnStack || targetTypeCategory == Type::Category::Integer, ""); if (enumOverflowCheckPending) @@ -798,8 +796,9 @@ void CompilerUtils::convertType( bytesConstRef data(value); if (targetTypeCategory == Type::Category::FixedBytes) { + int const numBytes = dynamic_cast(_targetType).numBytes(); solAssert(data.size() <= 32, ""); - m_context << h256::Arith(h256(data, h256::AlignLeft)); + m_context << (h256::Arith(h256(data, h256::AlignLeft)) & (~(u256(-1) >> (8 * numBytes)))); } else if (targetTypeCategory == Type::Category::Array) { From 0201492bbfb18ecc73f34d10e76dc0ee8395de73 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Fri, 13 Apr 2018 02:14:18 +0100 Subject: [PATCH 191/197] Remove redundant cleanup for abi.encode. --- libsolidity/codegen/ExpressionCompiler.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/libsolidity/codegen/ExpressionCompiler.cpp b/libsolidity/codegen/ExpressionCompiler.cpp index ed5af42ec..3cf46a9d0 100644 --- a/libsolidity/codegen/ExpressionCompiler.cpp +++ b/libsolidity/codegen/ExpressionCompiler.cpp @@ -1023,7 +1023,6 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) solAssert(function.kind() == FunctionType::Kind::ABIEncodeWithSelector, ""); } - // Cleanup actually does not clean on shrinking the type. utils().convertType(*dataOnStack, FixedBytesType(4), true); // stack: @@ -1034,7 +1033,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) let data_start := add(mem_ptr, 0x20) let data := mload(data_start) let mask := )" + mask + R"( - mstore(data_start, or(and(data, mask), and(selector, not(mask)))) + mstore(data_start, or(and(data, mask), selector)) })", {"mem_ptr", "selector"}); m_context << Instruction::POP; } From bf57500e250c0ebaed4b608626245dde4b423ba1 Mon Sep 17 00:00:00 2001 From: chriseth Date: Fri, 13 Apr 2018 17:34:35 +0200 Subject: [PATCH 192/197] Tests for bytes cleanup. --- test/libsolidity/SolidityEndToEndTest.cpp | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/test/libsolidity/SolidityEndToEndTest.cpp b/test/libsolidity/SolidityEndToEndTest.cpp index cbeca2151..e1727ee05 100644 --- a/test/libsolidity/SolidityEndToEndTest.cpp +++ b/test/libsolidity/SolidityEndToEndTest.cpp @@ -8807,6 +8807,24 @@ BOOST_AUTO_TEST_CASE(cleanup_bytes_types) ABI_CHECK(callContractFunction("f(bytes2,uint16)", string("abc"), u256(0x040102)), encodeArgs(0)); } +BOOST_AUTO_TEST_CASE(cleanup_bytes_types_shortening) +{ + char const* sourceCode = R"( + contract C { + function f() pure returns (bytes32 r) { + bytes4 x = 0xffffffff; + bytes2 y = bytes2(x); + assembly { r := y } + // At this point, r and y both store four bytes, but + // y is properly cleaned before the equality check + require(y == bytes2(0xffff)); + } + } + )"; + compileAndRun(sourceCode, 0, "C"); + ABI_CHECK(callContractFunction("f()"), encodeArgs("\xff\xff\xff\xff")); +} + BOOST_AUTO_TEST_CASE(skip_dynamic_types) { // The EVM cannot provide access to dynamically-sized return values, so we have to skip them. @@ -11322,6 +11340,10 @@ BOOST_AUTO_TEST_CASE(abi_encode) y[0] = "e"; require(y[0] == "e"); } + function f4() returns (bytes) { + bytes4 x = "abcd"; + return abi.encode(bytes2(x)); + } } )"; compileAndRun(sourceCode, 0, "C"); @@ -11329,6 +11351,7 @@ BOOST_AUTO_TEST_CASE(abi_encode) ABI_CHECK(callContractFunction("f1()"), encodeArgs(0x20, 0x40, 1, 2)); ABI_CHECK(callContractFunction("f2()"), encodeArgs(0x20, 0xa0, 1, 0x60, 2, 3, "abc")); ABI_CHECK(callContractFunction("f3()"), encodeArgs(0x20, 0xa0, 1, 0x60, 2, 3, "abc")); + ABI_CHECK(callContractFunction("f4()"), encodeArgs(0x20, 0x20, "ab")); } BOOST_AUTO_TEST_CASE(abi_encode_v2) From a9c16b8c3976dbd2c386586cdf143150a4266ac0 Mon Sep 17 00:00:00 2001 From: chriseth Date: Mon, 16 Apr 2018 12:46:48 +0200 Subject: [PATCH 193/197] Add documentation. --- docs/assembly.rst | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/docs/assembly.rst b/docs/assembly.rst index 705cd1b84..978e71e33 100644 --- a/docs/assembly.rst +++ b/docs/assembly.rst @@ -405,6 +405,16 @@ changes during the call, and thus references to local variables will be wrong. } } +.. note:: + 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 + encoding of the type. Especially, do not assume them to be zero. + To be safe, always clear the data properly before you use it + in a context where this is important: + ``uint32 x = f(); assembly { x := and(x, 0xffffffff) /* now use x */ }`` + To clean signed types, you can use the ``signextend`` opcode. + Labels ------ From 936832b3f92af6c714cf53aaa1982c6ec9549497 Mon Sep 17 00:00:00 2001 From: chriseth Date: Mon, 16 Apr 2018 15:28:43 +0200 Subject: [PATCH 194/197] Preparation for 0.4.22 release. --- Changelog.md | 15 +++++++++------ docs/bugs_by_version.json | 4 ++++ 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/Changelog.md b/Changelog.md index 431a7db77..8a3156850 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,14 +1,18 @@ -### 0.4.22 (unreleased) +### 0.4.22 (2018-04-16) Features: * Code Generator: Initialize arrays without using ``msize()``. * Code Generator: More specialized and thus optimized implementation for ``x.push(...)`` * Commandline interface: Error when missing or inaccessible file detected. Suppress it with the ``--ignore-missing`` flag. * Constant Evaluator: Fix evaluation of single element tuples. + * General: Add encoding routines ``abi.encodePacked``, ``abi.encode``, ``abi.encodeWithSelector`` and ``abi.encodeWithSignature``. + * General: Add global function ``gasleft()`` and deprecate ``msg.gas``. + * General: Add global function ``blockhash(uint)`` and deprecate ``block.hash(uint)``. * General: Allow providing reason string for ``revert()`` and ``require()``. * General: Limit the number of errors output in a single run to 256. * General: Support accessing dynamic return data in post-byzantium EVMs. - * General: Add encoding routines ``abi.encodePacked``, ``abi.encode``, ``abi.encodeWithSelector`` and ``abi.encodeWithSignature``. + * 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. * Optimizer: Optimize ``SHL`` and ``SHR`` only involving constants (Constantinople only). * Optimizer: Remove useless ``SWAP1`` instruction preceding a commutative instruction (such as ``ADD``, ``MUL``, etc). @@ -20,8 +24,7 @@ Features: * Syntax Tests: Add source locations to syntax test expectations. * Type Checker: Improve documentation and warnings for accessing contract members inherited from ``address``. * General: Introduce new constructor syntax using the ``constructor`` keyword as experimental 0.5.0 feature. - * 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. + Bugfixes: * Code Generator: Allow ``block.blockhash`` without being called. @@ -39,14 +42,14 @@ Bugfixes: * Parser: Fix internal compiler error when parsing ``var`` declaration without identifier. * Parser: Fix parsing of getters for function type variables. * Standard JSON: Support ``constantinople`` as ``evmVersion`` properly. + * Static Analyzer: Fix non-deterministic order of unused variable warnings. + * Static Analyzer: Invalid arithmetic with constant expressions causes errors. * Type Checker: Fix detection of recursive structs. * Type Checker: Fix asymmetry bug when comparing with literal numbers. * Type System: Improve error message when attempting to shift by a fractional amount. * Type System: Make external library functions accessible. * Type System: Prevent encoding of weird types. * Type System: Restrict rational numbers to 4096 bits. - * Static Analyzer: Fix non-deterministic order of unused variable warnings. - * Static Analyzer: Invalid arithmetic with constant expressions causes errors. ### 0.4.21 (2018-03-07) diff --git a/docs/bugs_by_version.json b/docs/bugs_by_version.json index 4c976a323..32f305c85 100644 --- a/docs/bugs_by_version.json +++ b/docs/bugs_by_version.json @@ -422,6 +422,10 @@ "bugs": [], "released": "2018-03-07" }, + "0.4.22": { + "bugs": [], + "released": "2018-04-16" + }, "0.4.3": { "bugs": [ "ZeroFunctionSelector", From c65159c78b5b7a4553af5f769cee943f145a9027 Mon Sep 17 00:00:00 2001 From: chriseth Date: Mon, 16 Apr 2018 15:28:43 +0200 Subject: [PATCH 195/197] Preparation for 0.4.22 release. --- Changelog.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Changelog.md b/Changelog.md index 8a3156850..0015c9499 100644 --- a/Changelog.md +++ b/Changelog.md @@ -9,6 +9,7 @@ Features: * General: Add global function ``gasleft()`` and deprecate ``msg.gas``. * General: Add global function ``blockhash(uint)`` and deprecate ``block.hash(uint)``. * General: Allow providing reason string for ``revert()`` and ``require()``. + * 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. * Inheritance: Error when using empty parentheses for base class constructors that require arguments as experimental 0.5.0 feature. @@ -23,7 +24,6 @@ Features: * Syntax Checker: Warn about modifiers on functions without implementation (this will turn into an error with version 0.5.0). * Syntax Tests: Add source locations to syntax test expectations. * Type Checker: Improve documentation and warnings for accessing contract members inherited from ``address``. - * General: Introduce new constructor syntax using the ``constructor`` keyword as experimental 0.5.0 feature. Bugfixes: From 759928524fb97b921fd3e961cd1b7552abd142b0 Mon Sep 17 00:00:00 2001 From: chriseth Date: Mon, 16 Apr 2018 22:12:30 +0200 Subject: [PATCH 196/197] Show progress on travis. --- scripts/tests.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/tests.sh b/scripts/tests.sh index 542c932a3..425a4ff4a 100755 --- a/scripts/tests.sh +++ b/scripts/tests.sh @@ -94,7 +94,7 @@ download_eth ETH_PID=$(run_eth /tmp/test) progress="--show-progress" -if [ "$CI" ] +if [ "$CIRCLECI" ] then progress="" fi From 6091cd54b42c6e6aa1d5f4720e91d75f619ad077 Mon Sep 17 00:00:00 2001 From: chriseth Date: Mon, 16 Apr 2018 22:30:30 +0200 Subject: [PATCH 197/197] Disable tests on travis. --- .travis.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 8aeda86aa..539e6088e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -159,7 +159,8 @@ cache: install: - test $SOLC_INSTALL_DEPS_TRAVIS != On || (scripts/install_deps.sh) - test "$TRAVIS_OS_NAME" != "linux" || (scripts/install_cmake.sh) - - if [ "$TRAVIS_BRANCH" != release -a -z "$TRAVIS_TAG" ]; then SOLC_TESTS=Off; fi +# - if [ "$TRAVIS_BRANCH" != release -a -z "$TRAVIS_TAG" ]; then SOLC_TESTS=Off; fi + - SOLC_TESTS=Off - if [ "$TRAVIS_BRANCH" = release -o -n "$TRAVIS_TAG" ]; then echo -n > prerelease.txt; else date -u +"nightly.%Y.%-m.%-d" > prerelease.txt; fi - echo -n "$TRAVIS_COMMIT" > commit_hash.txt