diff options
-rw-r--r-- | CMakeLists.txt | 2 | ||||
-rw-r--r-- | docs/index.rst | 10 | ||||
-rw-r--r-- | docs/installing-solidity.rst | 76 | ||||
-rw-r--r-- | docs/miscellaneous.rst | 40 | ||||
-rw-r--r-- | docs/types.rst | 14 | ||||
-rw-r--r-- | docs/units-and-global-variables.rst | 56 | ||||
-rw-r--r-- | libsolidity/analysis/TypeChecker.cpp | 5 | ||||
-rw-r--r-- | libsolidity/ast/Types.cpp | 23 | ||||
-rw-r--r-- | libsolidity/ast/Types.h | 23 | ||||
-rw-r--r-- | libsolidity/codegen/ContractCompiler.cpp | 3 | ||||
-rw-r--r-- | test/CMakeLists.txt | 2 | ||||
-rw-r--r-- | test/libsolidity/SolidityEndToEndTest.cpp | 45 | ||||
-rw-r--r-- | test/libsolidity/SolidityNameAndTypeResolution.cpp | 17 |
13 files changed, 225 insertions, 91 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index 7d0e3f66..35a44d1c 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.3.3") +set(PROJECT_VERSION "0.3.4") project(solidity VERSION ${PROJECT_VERSION}) # Let's find our dependencies diff --git a/docs/index.rst b/docs/index.rst index 178f34d9..61081e1c 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -45,19 +45,19 @@ Available Solidity Integrations Solidity syntax highlighting for SublimeText editor. * `Atom Solidity package <https://github.com/gmtcreators/atom-solidity/>`_ - Plugin for the atom editor that features syntax highlighting, compilation and a runtime environment (requires backend node). + Plugin for the Atom editor that features syntax highlighting, compilation and a runtime environment (requires backend node). * `Atom Solidity Linter <https://atom.io/packages/linter-solidity>`_ - Plugin for the atom editor that provides solidity linting + Plugin for the Atom editor that provides Solidity linting. * `Visual Studio Code extension <http://juan.blanco.ws/solidity-contracts-in-visual-studio-code/>`_ Solidity plugin for Microsoft Visual Studio Code that includes syntax highlighting and the Solidity compiler. * `Emacs Solidity <https://github.com/ethereum/emacs-solidity/>`_ - Plugin for the emacs editor providing syntax highlighting and compilation error reporting. + Plugin for the Emacs editor providing syntax highlighting and compilation error reporting. -* `VIM Solidity <https://github.com/tomlion/vim-solidity/>`_ - Plugin for the VIM editor providing syntax highlighting. +* `Vim Solidity <https://github.com/tomlion/vim-solidity/>`_ + Plugin for the Vim editor providing syntax highlighting. Language Documentation ---------------------- diff --git a/docs/installing-solidity.rst b/docs/installing-solidity.rst index 400aab5b..a5f9b0f2 100644 --- a/docs/installing-solidity.rst +++ b/docs/installing-solidity.rst @@ -68,67 +68,75 @@ Set up Homebrew: brew install llvm --HEAD --with-clang brew install qt5 --with-d-bus # add --verbose if long waits with a stale screen drive you crazy as well -Ubuntu ------- +Ubuntu Trusty (14.04) +--------------------- -Below are the build instructions for the latest versions of Ubuntu. The best -supported platform as of December 2014 is Ubuntu 14.04, 64 bit, with at least 2 -GB RAM. All our tests are done with this version. Community contributions for -other versions are welcome! - -Install dependencies: - -Before you can build the source, you need several tools and dependencies for the application to get started. - -First, update your repositories. Not all packages are provided in the main -Ubuntu repository, those you'll get from the Ethereum PPA and the LLVM archive. +Below are the instructions to install the minimal dependencies required +to compile Solidity on Ubuntu 14.04 (Trusty Tahr). .. note:: - Ubuntu 14.04 users, you'll need the latest version of cmake. For this, use: - `sudo apt-add-repository ppa:george-edison55/cmake-3.x` - -Now add all the rest: + These dependencies are not enough to compile the GUIs (Alethzero and Mix). .. code-block:: bash - sudo apt-get -y update - sudo apt-get -y install language-pack-en-base - sudo dpkg-reconfigure locales - sudo apt-get -y install software-properties-common + sudo apt-get -y install build-essential git cmake libgmp-dev libboost-all-dev \ + libjsoncpp-dev libleveldb-dev libcurl4-openssl-dev libminiupnpc-dev \ + libmicrohttpd-dev + sudo add-apt-repository -y ppa:ethereum/ethereum sudo add-apt-repository -y ppa:ethereum/ethereum-dev sudo apt-get -y update - sudo apt-get -y upgrade + sudo apt-get -y upgrade # this will update cmake to version 3.x + sudo apt-get -y install libcryptopp-dev libjson-rpc-cpp-dev -For Ubuntu 15.04 (Vivid Vervet) or older, use the following command to add the develop packages: +Ubuntu Xenial (16.04) +--------------------- -.. code-block:: bash +Below are the instructions to install the minimal dependencies required +to compile Solidity on Ubuntu 16.04 (Xenial Xerus). - sudo apt-get -y install build-essential git cmake libboost-all-dev libgmp-dev libleveldb-dev libminiupnpc-dev libreadline-dev libncurses5-dev libcurl4-openssl-dev libcryptopp-dev libjson-rpc-cpp-dev libmicrohttpd-dev libjsoncpp-dev libedit-dev libz-dev +One of the dependencies (Crypto++ Library, with version >= 5.6.2) can be +installed either by adding the Ethereum PPA (Option 1) or by backporting +``libcrypto++`` from Ubuntu Development to Ubuntu Xenial (Option 2). -For Ubuntu 15.10 (Wily Werewolf) or newer, use the following command instead: +.. note:: -.. code-block:: bash + These dependencies are not enough to compile the GUIs (Alethzero and Mix). - sudo apt-get -y install build-essential git cmake libboost-all-dev libgmp-dev libleveldb-dev libminiupnpc-dev libreadline-dev libncurses5-dev libcurl4-openssl-dev libcryptopp-dev libjsonrpccpp-dev libmicrohttpd-dev libjsoncpp-dev libedit-dev libz-dev +.. code-block:: bash -The reason for the change is that ``libjsonrpccpp-dev`` is available in the universe repository for newer versions of Ubuntu. + sudo apt-get -y install build-essential git cmake libgmp-dev libboost-all-dev \ + libjsoncpp-dev libleveldb-dev libcurl4-openssl-dev libminiupnpc-dev \ + libjsonrpccpp-dev libmicrohttpd-dev + + # (Option 1) For those willing to add the Ethereum PPA: + sudo add-apt-repository -y ppa:ethereum/ethereum + sudo add-apt-repository -y ppa:ethereum/ethereum-dev + sudo apt-get -y update + sudo apt-get -y upgrade + sudo apt-get -y install libcryptopp-dev + + ## (Option 2) For those willing to backport libcrypto++: + #sudo apt-get -y install ubuntu-dev-tools + #sudo pbuilder create + #mkdir ubuntu + #cd ubuntu + #backportpackage --workdir=. --build --dont-sign libcrypto++ + #sudo dpkg -i buildresult/libcrypto++6_*.deb buildresult/libcrypto++-dev_*.deb + #cd .. Building -------- -Run this if you plan on installing Solidity only, ignore errors at the end as -they relate only to Alethzero and Mix +Run this if you plan on installing Solidity only: .. code-block:: bash git clone --recursive https://github.com/ethereum/webthree-umbrella.git cd webthree-umbrella ./webthree-helpers/scripts/ethupdate.sh --no-push --simple-pull --project solidity # update Solidity repo - ./webthree-helpers/scripts/ethbuild.sh --no-git --project solidity --all --cores 4 -DEVMJIT=0 # build Solidity and others - #enabling DEVMJIT on OS X will not build - #feel free to enable it on Linux + ./webthree-helpers/scripts/ethbuild.sh --no-git --project solidity --cores 4 -DEVMJIT=0 -DETHASHCL=0 # build Solidity only If you opted to install Alethzero and Mix: diff --git a/docs/miscellaneous.rst b/docs/miscellaneous.rst index 6b327c56..4e61b283 100644 --- a/docs/miscellaneous.rst +++ b/docs/miscellaneous.rst @@ -148,6 +148,26 @@ Pitfalls Unfortunately, there are some subtleties the compiler does not yet warn you about. - In ``for (var i = 0; i < arrayName.length; i++) { ... }``, the type of ``i`` will be ``uint8``, because this is the smallest type that is required to hold the value ``0``. If the array has more than 255 elements, the loop will not terminate. +- If a contract receives Ether (without a function being called), the fallback function is executed. The contract can only rely + on the "gas stipend" (2300 gas) being available to it at that time. This stipend is not enough to access storage in any way. + To be sure that your contract can receive Ether in that way, check the gas requirements of the fallback function. +- If you want to send ether using ``address.send``, there are certain details to be aware of: + + 1. If the recipient is a contract, it causes its fallback function to be executed which can in turn call back into the sending contract + 2. Sending Ether can fail due to the call depth going above 1024. Since the caller is in total control of the call + depth, they can force the transfer to fail, so make sure to always check the return value of ``send``. Better yet, + write your contract using a pattern where the recipient can withdraw Ether instead. + 3. Sending Ether can also fail because the recipient goes out of gas (either explicitly by using ``throw`` or + because the operation is just too expensive). If the return value of ``send`` is checked, this might provide a + means for the recipient to block progress in the sending contract. Again, the best practise here is to use + a "withdraw" pattern instead of a "send" pattern. + +- Loops that do not have a fixed number of iterations, e.g. loops that depends on storage values, have to be used carefully: + Due to the block gas limit, transactions can only consume a certain amount of gas. Either explicitly or just due to + normal operation, the number of iterations in a loop can grow beyond the block gas limit, which can cause the complete + contract to be stalled at a certain point. This does not apply at full extent to ``constant`` functions that are only executed + to read data from the blockchain. Still, such functions may be called by other contracts as part of on-chain operations + and stall those. Please be explicit about such cases in the documentation of your contracts. ********** Cheatsheet @@ -158,11 +178,11 @@ Cheatsheet Global Variables ================ +- ``block.blockhash(uint blockNumber) returns (bytes32)``: hash of the given block - only works for 256 most recent blocks - ``block.coinbase`` (``address``): current block miner's address - ``block.difficulty`` (``uint``): current block difficulty - ``block.gaslimit`` (``uint``): current block gaslimit - ``block.number`` (``uint``): current block number -- ``block.blockhash`` (``function(uint) returns (bytes32)``): hash of the given block - only works for 256 most recent blocks - ``block.timestamp`` (``uint``): current block timestamp - ``msg.data`` (``bytes``): complete calldata - ``msg.gas`` (``uint``): remaining gas @@ -171,17 +191,17 @@ Global Variables - ``now`` (``uint``): current block timestamp (alias for ``block.timestamp``) - ``tx.gasprice`` (``uint``): gas price of the transaction - ``tx.origin`` (``address``): sender of the transaction (full call chain) -- ``sha3(...) returns (bytes32)``: compute the Ethereum-SHA3 hash of the (tightly packed) arguments -- ``sha256(...) returns (bytes32)``: compute the SHA256 hash of the (tightly packed) arguments -- ``ripemd160(...) returns (bytes20)``: compute RIPEMD of 256 the (tightly packed) arguments -- ``ecrecover(bytes32, uint8, bytes32, bytes32) returns (address)``: recover address associated with the public key from elliptic curve signature -- ``addmod(uint x, uint y, uint k) returns (uint)``: compute ``(x + y) % k`` where the addition is performed with arbitrary precision and does not wrap around at ``2**256``. -- ``mulmod(uint x, uint y, uint k) returns (uint)``: compute ``(x * y) % k`` where the multiplication is performed with arbitrary precision and does not wrap around at ``2**256``. +- ``sha3(...) returns (bytes32)``: compute the Ethereum-SHA-3 (KECCAK-256) hash of the (tightly packed) arguments +- ``sha256(...) returns (bytes32)``: compute the SHA-256 hash of the (tightly packed) arguments +- ``ripemd160(...) returns (bytes20)``: compute the RIPEMD-160 hash of the (tightly packed) arguments +- ``ecrecover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) returns (address)``: recover address associated with the public key from elliptic curve signature +- ``addmod(uint x, uint y, uint k) returns (uint)``: compute ``(x + y) % k`` where the addition is performed with arbitrary precision and does not wrap around at ``2**256`` +- ``mulmod(uint x, uint y, uint k) returns (uint)``: compute ``(x * y) % k`` where the multiplication is performed with arbitrary precision and does not wrap around at ``2**256`` - ``this`` (current contract's type): the current contract, explicitly convertible to ``address`` - ``super``: the contract one level higher in the inheritance hierarchy -- ``selfdestruct(address)``: destroy the current contract, sending its funds to the given address -- ``<address>.balance``: balance of the address in Wei -- ``<address>.send(uint256) returns (bool)``: send given amount of Wei to address, returns ``false`` on failure. +- ``selfdestruct(address recipient)``: destroy the current contract, sending its funds to the given address +- ``<address>.balance`` (``uint256``): balance of the address in Wei +- ``<address>.send(uint256 amount) returns (bool)``: send given amount of Wei to address, returns ``false`` on failure .. index:: visibility, public, private, external, internal diff --git a/docs/types.rst b/docs/types.rst index 5909713e..9de83843 100644 --- a/docs/types.rst +++ b/docs/types.rst @@ -72,6 +72,8 @@ Members of Addresses * ``balance`` and ``send`` +For a quick reference, see :ref:`address_related`. + It is possible to query the balance of an address using the property ``balance`` and to send Ether (in units of wei) to an address using the ``send`` function: @@ -84,6 +86,12 @@ and to send Ether (in units of wei) to an address using the ``send`` function: .. note:: If ``x`` is a contract address, its code (more specifically: its fallback function, if present) will be executed together with the ``send`` call (this is a limitation of the EVM and cannot be prevented). If that execution runs out of gas or fails in any way, the Ether transfer will be reverted. In this case, ``send`` returns ``false``. +.. warning:: + There are some dangers in using ``send``: The transfer fails if the call stack depth is at 1024 + (this can always be forced by the caller) and it also fails if the recipient runs out of gas. So in order + to make safe Ether transfers, always check the return value of ``send`` or even better: + Use a pattern where the recipient withdraws the money. + * ``call``, ``callcode`` and ``delegatecall`` Furthermore, to interface with contracts that do not adhere to the ABI, @@ -95,7 +103,7 @@ the function ``call`` is provided which takes an arbitrary number of arguments o nameReg.call("register", "MyName"); nameReg.call(bytes4(sha3("fun(uint256)")), a); -``call`` returns a boolean indicating whether the invoked function terminated (``true``) or caused an EVM exception (`false:code:`). It is not possible to access the actual data returned (for this we would need to know the encoding and size in advance). +``call`` returns a boolean indicating whether the invoked function terminated (``true``) or caused an EVM exception (``false``). It is not possible to access the actual data returned (for this we would need to know the encoding and size in advance). In a similar way, the function ``delegatecall`` can be used: The difference is that only the code of the given address is used, all other aspects (storage, balance, ...) are taken from the current contract. The purpose of ``delegatecall`` is to use library code which is stored in another contract. The user has to ensure that the layout of storage in both contracts is suitable for delegatecall to be used. Prior to homestead, only a limited variant called ``callcode`` was available that did not provide access to the original ``msg.sender`` and ``msg.value`` values. @@ -654,10 +662,10 @@ For convenience, it is not always necessary to explicitly specify the type of a variable, the compiler automatically infers it from the type of the first expression that is assigned to the variable:: - uint20 x = 0x123; + uint24 x = 0x123; var y = x; -Here, the type of ``y`` will be ``uint20``. Using ``var`` is not possible for function +Here, the type of ``y`` will be ``uint24``. Using ``var`` is not possible for function parameters or return parameters. .. warning:: diff --git a/docs/units-and-global-variables.rst b/docs/units-and-global-variables.rst index 8f910c80..167c694d 100644 --- a/docs/units-and-global-variables.rst +++ b/docs/units-and-global-variables.rst @@ -48,22 +48,22 @@ namespace and are mainly used to provide information about the blockchain. Block and Transaction Properties ------------------------------------- - - - ``block.coinbase`` (``address``): current block miner's address - - ``block.difficulty`` (``uint``): current block difficulty - - ``block.gaslimit`` (``uint``): current block gaslimit - - ``block.number`` (``uint``): current block number - - ``block.blockhash`` (``function(uint) returns (bytes32)``): hash of the given block - only for 256 most recent blocks - - ``block.timestamp`` (``uint``): current block timestamp - - ``msg.data`` (``bytes``): complete calldata - - ``msg.gas`` (``uint``): remaining gas - - ``msg.sender`` (``address``): sender of the message (current call) - - ``msg.sig`` (``bytes4``): first four bytes of the calldata (i.e. function identifier) - - ``msg.value`` (``uint``): number of wei sent with the message - - ``now`` (``uint``): current block timestamp (alias for ``block.timestamp``) - - ``tx.gasprice`` (``uint``): gas price of the transaction - - ``tx.origin`` (``address``): sender of the transaction (full call chain) +-------------------------------- + +- ``block.blockhash(uint blockNumber) returns (bytes32)``: hash of the given block - only works for 256 most recent blocks +- ``block.coinbase`` (``address``): current block miner's address +- ``block.difficulty`` (``uint``): current block difficulty +- ``block.gaslimit`` (``uint``): current block gaslimit +- ``block.number`` (``uint``): current block number +- ``block.timestamp`` (``uint``): current block timestamp +- ``msg.data`` (``bytes``): complete calldata +- ``msg.gas`` (``uint``): remaining gas +- ``msg.sender`` (``address``): sender of the message (current call) +- ``msg.sig`` (``bytes4``): first four bytes of the calldata (i.e. function identifier) +- ``msg.value`` (``uint``): number of wei sent with the message +- ``now`` (``uint``): current block timestamp (alias for ``block.timestamp``) +- ``tx.gasprice`` (``uint``): gas price of the transaction +- ``tx.origin`` (``address``): sender of the transaction (full call chain) .. note:: The values of all members of ``msg``, including ``msg.sender`` and @@ -89,12 +89,12 @@ Mathematical and Cryptographic Functions ``mulmod(uint x, uint y, uint k) returns (uint)``: compute ``(x * y) % k`` where the multiplication is performed with arbitrary precision and does not wrap around at ``2**256``. ``sha3(...) returns (bytes32)``: - compute the Ethereum-SHA-3 hash of the (tightly packed) arguments + compute the Ethereum-SHA-3 (KECCAK-256) hash of the (tightly packed) arguments ``sha256(...) returns (bytes32)``: compute the SHA-256 hash of the (tightly packed) arguments ``ripemd160(...) returns (bytes20)``: compute RIPEMD-160 hash of the (tightly packed) arguments -``ecrecover(bytes32 data, uint8 v, bytes32 r, bytes32 s) returns (address)``: +``ecrecover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) returns (address)``: recover the address associated with the public key from elliptic curve signature In the above, "tightly packed" means that the arguments are concatenated without padding. @@ -111,6 +111,24 @@ same as ``sha3(uint16(0x12))``. It might be that you run into Out-of-Gas for ``sha256``, ``ripemd160`` or ``ecrecover`` on a *private blockchain*. The reason for this is that those are implemented as so-called precompiled contracts and these contracts only really exist after they received the first message (although their contract code is hardcoded). Messages to non-existing contracts are more expensive and thus the execution runs into an Out-of-Gas error. A workaround for this problem is to first send e.g. 1 Wei to each of the contracts before you use them in your actual contracts. This is not an issue on the official or test net. +.. _address_related: + +Address Related +--------------- + +``<address>.balance`` (``uint256``): + balance of the :ref:`address` in Wei +``<address>.send(uint256 amount) returns (bool)``: + send given amount of Wei to :ref:`address`, returns ``false`` on failure + +For more information, see the section on :ref:`address`. + +.. warning:: + There are some dangers in using ``send``: The transfer fails if the call stack depth is at 1024 + (this can always be forced by the caller) and it also fails if the recipient runs out of gas. So in order + to make safe Ether transfers, always check the return value of ``send`` or even better: + Use a pattern where the recipient withdraws the money. + .. index:: this, selfdestruct Contract Related @@ -119,7 +137,7 @@ Contract Related ``this`` (current contract's type): the current contract, explicitly convertible to :ref:`address` -``selfdestruct(address)``: +``selfdestruct(address recipient)``: destroy the current contract, sending its funds to the given :ref:`address` Furthermore, all functions of the current contract are callable directly including the current function. diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index 5ae0443a..ce55de00 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -32,7 +32,7 @@ using namespace dev; using namespace dev::solidity; -bool TypeChecker::checkTypeRequirements(const ContractDefinition& _contract) +bool TypeChecker::checkTypeRequirements(ContractDefinition const& _contract) { try { @@ -174,6 +174,9 @@ void TypeChecker::checkContractAbstractFunctions(ContractDefinition const& _cont for (ContractDefinition const* contract: boost::adaptors::reverse(_contract.annotation().linearizedBaseContracts)) for (FunctionDefinition const* function: contract->definedFunctions()) { + // Take constructors out of overload hierarchy + if (function->isConstructor()) + continue; auto& overloads = functions[function->name()]; FunctionTypePointer funType = make_shared<FunctionType>(*function); auto it = find_if(overloads.begin(), overloads.end(), [&](FunTypeAndFlag const& _funAndFlag) diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp index e964d683..5630743b 100644 --- a/libsolidity/ast/Types.cpp +++ b/libsolidity/ast/Types.cpp @@ -2037,29 +2037,20 @@ FunctionTypePointer FunctionType::asMemberFunction(bool _inLibrary, bool _bound) location = Location::DelegateCall; } - TypePointers returnParameterTypes; - vector<string> returnParameterNames; - if (location == Location::Internal) + TypePointers returnParameterTypes = m_returnParameterTypes; + if (location != Location::Internal) { - returnParameterNames = m_returnParameterNames; - returnParameterTypes = m_returnParameterTypes; - } - else - { - // Removes dynamic types. - for (size_t i = 0; i < m_returnParameterTypes.size(); ++i) - if (!m_returnParameterTypes[i]->isDynamicallySized()) - { - returnParameterTypes.push_back(m_returnParameterTypes[i]); - returnParameterNames.push_back(m_returnParameterNames[i]); - } + // Alter dynamic types to be non-accessible. + for (auto& param: returnParameterTypes) + if (param->isDynamicallySized()) + param = make_shared<InaccessibleDynamicType>(); } return make_shared<FunctionType>( parameterTypes, returnParameterTypes, m_parameterNames, - returnParameterNames, + m_returnParameterNames, location, m_arbitraryParameters, m_declaration, diff --git a/libsolidity/ast/Types.h b/libsolidity/ast/Types.h index 967e968c..1ee762e5 100644 --- a/libsolidity/ast/Types.h +++ b/libsolidity/ast/Types.h @@ -137,7 +137,8 @@ public: { Integer, RationalNumber, StringLiteral, Bool, FixedPoint, Array, FixedBytes, Contract, Struct, Function, Enum, Tuple, - Mapping, TypeType, Modifier, Magic, Module + Mapping, TypeType, Modifier, Magic, Module, + InaccessibleDynamic }; /// @{ @@ -1081,5 +1082,25 @@ private: Kind m_kind; }; +/** + * Special type that is used for dynamic types in returns from external function calls + * (The EVM currently cannot access dynamically-sized return values). + */ +class InaccessibleDynamicType: public Type +{ +public: + virtual Category category() const override { return Category::InaccessibleDynamic; } + + virtual bool isImplicitlyConvertibleTo(Type const&) const override { return false; } + virtual bool isExplicitlyConvertibleTo(Type const&) const override { return false; } + virtual unsigned calldataEncodedSize(bool _padded) const override { (void)_padded; return 32; } + virtual bool canBeStored() const override { return false; } + virtual bool canLiveOutsideStorage() const override { return false; } + virtual bool isValueType() const override { return true; } + virtual unsigned sizeOnStack() const override { return 1; } + virtual std::string toString(bool) const override { return "inaccessible dynamic type"; } + virtual TypePointer decodingType() const override { return std::make_shared<IntegerType>(256); } +}; + } } diff --git a/libsolidity/codegen/ContractCompiler.cpp b/libsolidity/codegen/ContractCompiler.cpp index 3623046e..bcfd33f2 100644 --- a/libsolidity/codegen/ContractCompiler.cpp +++ b/libsolidity/codegen/ContractCompiler.cpp @@ -776,7 +776,10 @@ void ContractCompiler::appendModifierOrFunctionCode() { solAssert(m_currentFunction, ""); if (m_modifierDepth >= m_currentFunction->modifiers().size()) + { + solAssert(m_currentFunction->isImplemented(), ""); m_currentFunction->body().accept(*this); + } else { ASTPointer<ModifierInvocation> const& modifierInvocation = m_currentFunction->modifiers()[m_modifierDepth]; diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 9d958b16..ed53ce59 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -40,7 +40,7 @@ foreach(file ${SRC_LIST}) endforeach(test_raw) endforeach(file) -file(GLOB HEADERS "*.h") +file(GLOB HEADERS "*.h" "*/*.h") set(EXECUTABLE soltest) eth_simple_add_executable(${EXECUTABLE} ${SRC_LIST} ${HEADERS}) diff --git a/test/libsolidity/SolidityEndToEndTest.cpp b/test/libsolidity/SolidityEndToEndTest.cpp index 30ae5792..07bf6759 100644 --- a/test/libsolidity/SolidityEndToEndTest.cpp +++ b/test/libsolidity/SolidityEndToEndTest.cpp @@ -6793,6 +6793,51 @@ BOOST_AUTO_TEST_CASE(cleanup_bytes_types) BOOST_CHECK(callContractFunction("f(bytes2,uint16)", string("abc"), u256(0x040102)) == encodeArgs(0)); } +BOOST_AUTO_TEST_CASE(skip_dynamic_types) +{ + // The EVM cannot provide access to dynamically-sized return values, so we have to skip them. + char const* sourceCode = R"( + contract C { + function f() returns (uint, uint[], uint) { + return (7, new uint[](2), 8); + } + function g() returns (uint, uint) { + // Previous implementation "moved" b to the second place and did not skip. + var (a, _, b) = this.f(); + return (a, b); + } + } + )"; + compileAndRun(sourceCode, 0, "C"); + BOOST_CHECK(callContractFunction("g()") == encodeArgs(u256(7), u256(8))); +} + +BOOST_AUTO_TEST_CASE(skip_dynamic_types_for_structs) +{ + // For accessors, the dynamic types are already removed in the external signature itself. + char const* sourceCode = R"( + contract C { + struct S { + uint x; + string a; // this is present in the accessor + uint[] b; // this is not present + uint y; + } + S public s; + function g() returns (uint, uint) { + s.x = 2; + s.a = "abc"; + s.b = [7, 8, 9]; + s.y = 6; + var (x, a, y) = this.s(); + return (x, y); + } + } + )"; + compileAndRun(sourceCode, 0, "C"); + BOOST_CHECK(callContractFunction("g()") == encodeArgs(u256(2), u256(6))); +} + BOOST_AUTO_TEST_SUITE_END() } diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index 52e0bf58..697b3fa9 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -544,6 +544,21 @@ BOOST_AUTO_TEST_CASE(redeclare_implemented_abstract_function_as_abstract) BOOST_CHECK(expectError(text) == Error::Type::TypeError); } +BOOST_AUTO_TEST_CASE(implement_abstract_via_constructor) +{ + ASTPointer<SourceUnit> sourceUnit; + char const* text = R"( + contract base { function foo(); } + contract foo is base { function foo() {} } + )"; + ETH_TEST_REQUIRE_NO_THROW(sourceUnit = parseAndAnalyse(text), "Parsing and name resolving failed"); + std::vector<ASTPointer<ASTNode>> nodes = sourceUnit->nodes(); + BOOST_CHECK_EQUAL(nodes.size(), 2); + ContractDefinition* derived = dynamic_cast<ContractDefinition*>(nodes[1].get()); + BOOST_CHECK(derived); + BOOST_CHECK(!derived->annotation().isFullyImplemented); +} + BOOST_AUTO_TEST_CASE(function_canonical_signature) { ASTPointer<SourceUnit> sourceUnit; @@ -2154,6 +2169,8 @@ BOOST_AUTO_TEST_CASE(dynamic_return_types_not_possible) function f(uint) returns (string); function g() { var (x,) = this.f(2); + // we can assign to x but it is not usable. + bytes(x).length; } } )"; |