diff options
author | Daniel Kirchner <daniel@ekpyron.org> | 2018-09-05 23:59:55 +0800 |
---|---|---|
committer | Daniel Kirchner <daniel@ekpyron.org> | 2018-09-13 21:15:49 +0800 |
commit | 12aaca16458861e9b622818d49a82c1a7026594e (patch) | |
tree | 7b51c4893c6646134618b6c20574317ec014f225 | |
parent | 9214c7c34f5e4501a50cb29de964bbf04131f9a3 (diff) | |
download | dexon-solidity-12aaca16458861e9b622818d49a82c1a7026594e.tar.gz dexon-solidity-12aaca16458861e9b622818d49a82c1a7026594e.tar.zst dexon-solidity-12aaca16458861e9b622818d49a82c1a7026594e.zip |
Add payable and non-payable state mutability to AddressType.
47 files changed, 468 insertions, 82 deletions
diff --git a/Changelog.md b/Changelog.md index 846ae1f5..18062c9c 100644 --- a/Changelog.md +++ b/Changelog.md @@ -7,6 +7,7 @@ How to update your code: * Make your fallback functions ``external``. * Explicitly state the data location for all variables of struct, array or mapping types (including function parameters), e.g. change ``uint[] x = m_x`` to ``uint[] storage x = m_x``. Note that ``external`` functions require parameters with a data location of ``calldata``. * Explicitly convert values of contract type to addresses before using an ``address`` member. Example: if ``c`` is a contract, change ``c.transfer(...)`` to ``address(c).transfer(...)``. + * Declare variables and especially function arguments as ``address payable``, if you want to call ``transfer`` on them. Breaking Changes: * ABI Encoder: Properly pad data from calldata (``msg.data`` and external function parameters). Use ``abi.encodePacked`` for unpadded encoding. @@ -62,6 +63,7 @@ Breaking Changes: * Type Checker: Disallow "loose assembly" syntax entirely. This means that jump labels, jumps and non-functional instructions cannot be used anymore. * Type System: Disallow explicit and implicit conversions from decimal literals to ``bytesXX`` types. * Type System: Disallow explicit and implicit conversions from hex literals to ``bytesXX`` types of different size. + * Type System: Distinguish between payable and non-payable address types. * View Pure Checker: Disallow ``msg.value`` in (or introducing it via a modifier to) a non-payable function. * Remove obsolete ``std`` directory from the Solidity repository. This means accessing ``https://github.com/ethereum/solidity/blob/develop/std/*.sol`` (or ``https://github.com/ethereum/solidity/std/*.sol`` in Remix) will not be possible. * References Resolver: Turn missing storage locations into an error. This was already the case in the experimental 0.5.0 mode. diff --git a/docs/common-patterns.rst b/docs/common-patterns.rst index 6b061bf7..d26e4377 100644 --- a/docs/common-patterns.rst +++ b/docs/common-patterns.rst @@ -68,7 +68,7 @@ This is as opposed to the more intuitive sending pattern: pragma solidity >0.4.24; contract SendContract { - address public richest; + address payable public richest; uint public mostSent; constructor() public payable { diff --git a/docs/contracts.rst b/docs/contracts.rst index 8fd1c89e..b9179b27 100644 --- a/docs/contracts.rst +++ b/docs/contracts.rst @@ -334,7 +334,7 @@ inheritable properties of contracts and may be overridden by derived contracts. contract owned { constructor() public { owner = msg.sender; } - address owner; + address payable owner; // This contract only defines a modifier but does not use // it: it will be used in derived contracts. @@ -650,9 +650,14 @@ Like any function, the fallback function can execute complex operations as long require(success); // results in test.x becoming == 1. + // address(test) will not allow to call ``send`` directly, since ``test`` has no payable + // fallback function. It has to be converted to the ``address payable`` type via an + // intermediate conversion to ``uint160`` to even allow calling ``send`` on it. + address payable testPayable = address(uint160(address(test))); + // If someone sends ether to that contract, // the transfer will fail, i.e. this returns false here. - return address(test).send(2 ether); + return testPayable.send(2 ether); } } @@ -891,7 +896,7 @@ Details are given in the following example. contract owned { constructor() public { owner = msg.sender; } - address owner; + address payable owner; } // Use `is` to derive from another contract. Derived @@ -963,7 +968,7 @@ seen in the following example:: contract owned { constructor() public { owner = msg.sender; } - address owner; + address payable owner; } contract mortal is owned { @@ -992,7 +997,7 @@ derived override, but this function will bypass contract owned { constructor() public { owner = msg.sender; } - address owner; + address payable owner; } contract mortal is owned { diff --git a/docs/control-structures.rst b/docs/control-structures.rst index 5810aaa7..745a0599 100644 --- a/docs/control-structures.rst +++ b/docs/control-structures.rst @@ -421,7 +421,7 @@ a message string for ``require``, but not for ``assert``. pragma solidity >0.4.24; contract Sharer { - function sendHalf(address addr) public payable returns (uint balance) { + function sendHalf(address payable addr) public payable returns (uint balance) { require(msg.value % 2 == 0, "Even value required."); uint balanceBeforeTransfer = address(this).balance; addr.transfer(msg.value / 2); diff --git a/docs/miscellaneous.rst b/docs/miscellaneous.rst index 87041be6..d96ff166 100644 --- a/docs/miscellaneous.rst +++ b/docs/miscellaneous.rst @@ -329,7 +329,7 @@ Global Variables starting from the second and prepends the given four-byte selector - ``abi.encodeWithSignature(string signature, ...) returns (bytes)``: Equivalent to ``abi.encodeWithSelector(bytes4(keccak256(bytes(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.coinbase`` (``address payable``): current block miner's address - ``block.difficulty`` (``uint``): current block difficulty - ``block.gaslimit`` (``uint``): current block gaslimit - ``block.number`` (``uint``): current block number @@ -337,11 +337,11 @@ Global Variables - ``gasleft() returns (uint256)``: remaining gas - ``msg.data`` (``bytes``): complete calldata - ``msg.gas`` (``uint``): remaining gas - deprecated in version 0.4.21 and to be replaced by ``gasleft()`` -- ``msg.sender`` (``address``): sender of the message (current call) +- ``msg.sender`` (``address payable``): sender of the message (current call) - ``msg.value`` (``uint``): number of wei sent with the message - ``now`` (``uint``): current block timestamp (alias for ``block.timestamp``) - ``tx.gasprice`` (``uint``): gas price of the transaction -- ``tx.origin`` (``address``): sender of the transaction (full call chain) +- ``tx.origin`` (``address payable``): sender of the transaction (full call chain) - ``assert(bool condition)``: abort execution and revert state changes if condition is ``false`` (use for internal error) - ``require(bool condition)``: abort execution and revert state changes if condition is ``false`` (use for malformed input or error in external component) - ``require(bool condition, string message)``: abort execution and revert state changes if condition is ``false`` (use for malformed input or error in external component). Also provide error message. @@ -360,8 +360,8 @@ Global Variables - ``selfdestruct(address recipient)``: destroy the current contract, sending its funds to the given address - ``suicide(address recipient)``: a deprecated alias to ``selfdestruct`` - ``<address>.balance`` (``uint256``): balance of the :ref:`address` in Wei -- ``<address>.send(uint256 amount) returns (bool)``: send given amount of Wei to :ref:`address`, returns ``false`` on failure -- ``<address>.transfer(uint256 amount)``: send given amount of Wei to :ref:`address`, throws on failure +- ``<address payable>.send(uint256 amount) returns (bool)``: send given amount of Wei to :ref:`address`, returns ``false`` on failure +- ``<address payable>.transfer(uint256 amount)``: send given amount of Wei to :ref:`address`, throws on failure .. note:: Do not rely on ``block.timestamp``, ``now`` and ``blockhash`` as a source of randomness, diff --git a/docs/security-considerations.rst b/docs/security-considerations.rst index 3bcd9566..8df12b7c 100644 --- a/docs/security-considerations.rst +++ b/docs/security-considerations.rst @@ -192,7 +192,7 @@ Never use tx.origin for authorization. Let's say you have a wallet contract like owner = msg.sender; } - function transferTo(address dest, uint amount) public { + function transferTo(address payable dest, uint amount) public { require(tx.origin == owner); dest.transfer(amount); } @@ -205,11 +205,11 @@ Now someone tricks you into sending ether to the address of this attack wallet: pragma solidity >0.4.24; interface TxUserWallet { - function transferTo(address dest, uint amount) external; + function transferTo(address payable dest, uint amount) external; } contract TxAttackWallet { - address owner; + address payable owner; constructor() public { owner = msg.sender; diff --git a/docs/solidity-by-example.rst b/docs/solidity-by-example.rst index 72b3581b..0b183ca5 100644 --- a/docs/solidity-by-example.rst +++ b/docs/solidity-by-example.rst @@ -231,7 +231,7 @@ activate themselves. // Parameters of the auction. Times are either // absolute unix timestamps (seconds since 1970-01-01) // or time periods in seconds. - address public beneficiary; + address payable public beneficiary; uint public auctionEndTime; // Current state of the auction. @@ -258,7 +258,7 @@ activate themselves. /// beneficiary address `_beneficiary`. constructor( uint _biddingTime, - address _beneficiary + address payable _beneficiary ) public { beneficiary = _beneficiary; auctionEndTime = now + _biddingTime; @@ -396,7 +396,7 @@ high or low invalid bids. uint deposit; } - address public beneficiary; + address payable public beneficiary; uint public biddingEnd; uint public revealEnd; bool public ended; @@ -421,7 +421,7 @@ high or low invalid bids. constructor( uint _biddingTime, uint _revealTime, - address _beneficiary + address payable _beneficiary ) public { beneficiary = _beneficiary; biddingEnd = now + _biddingTime; @@ -545,8 +545,8 @@ Safe Remote Purchase contract Purchase { uint public value; - address public seller; - address public buyer; + address payable public seller; + address payable public buyer; enum State { Created, Locked, Inactive } State public state; @@ -990,11 +990,11 @@ The full contract pragma solidity ^0.4.24; contract SimplePaymentChannel { - address public sender; // The account sending payments. - address public recipient; // The account receiving the payments. + address payable public sender; // The account sending payments. + address payable public recipient; // The account receiving the payments. uint256 public expiration; // Timeout in case the recipient never closes. - constructor (address _recipient, uint256 duration) + constructor (address payable _recipient, uint256 duration) public payable { diff --git a/docs/types.rst b/docs/types.rst index 03fd36d9..eaec8ad5 100644 --- a/docs/types.rst +++ b/docs/types.rst @@ -99,6 +99,15 @@ Address ------- ``address``: Holds a 20 byte value (size of an Ethereum address). Address types also have members and serve as a base for all contracts. +``address payable``: Same as ``address``, but with the additional members ``transfer`` and ``send``. + +Implicit conversions from ``address payable`` to ``address`` are allowed, whereas conversions from ``address`` to ``address payable`` are +not possible (the only way to perform such a conversion is by using an intermediate conversion to ``uint160``). +Conversions of the form ``address payable(x)`` are not allowed. Instead the result of a conversion of the form ``address(x)`` +has the type ``address payable``, if ``x`` is of integer or fixed bytes type, a literal or a contract with a payable fallback function. +If ``x`` is a contract without payable fallback function ``address(x)`` will be of type ``address``. The type of address literals +is ``address payable``. +In external function signatures ``address`` is used for both the ``address`` and the ``address payable`` type. Operators: @@ -113,7 +122,8 @@ Operators: or you can use ``address(uint160(uint256(b)))``, which results in ``0x777788889999AaAAbBbbCcccddDdeeeEfFFfCcCc``. .. note:: - Starting with version 0.5.0 contracts do not derive from the address type, but can still be explicitly converted to address. + Starting with version 0.5.0 contracts do not derive from the address type, but can still be explicitly converted to + ``address`` or to ``address payable``, if they have a payable fallback function. .. _members-of-addresses: @@ -125,11 +135,11 @@ Members of Addresses 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 ``transfer`` function: +and to send Ether (in units of wei) to a payable address using the ``transfer`` function: :: - address x = 0x123; + address payable x = address(0x123); address myAddress = this; if (x.balance < 10 && myAddress.balance >= 10) x.transfer(10); @@ -211,11 +221,14 @@ Contract Types Every :ref:`contract<contracts>` defines its own type. You can implicitly convert contracts to contracts they inherit from, -and explicitly convert them to and from the ``address`` type. +and explicitly convert them to and from the ``address`` type, if they have no +payable fallback functions, or to and from the ``address payable`` type, if they do +have payable fallback functions. .. note:: Starting with version 0.5.0 contracts do not derive from the address type, - but can still be explicitly converted to address. + but can still be explicitly converted to ``address``, resp. to ``address payable``, + if they have a payable fallback function. If you declare a local variable of contract type (`MyContract c`), you can call functions on that contract. Take care to assign it from somewhere that is the @@ -860,7 +873,7 @@ shown in the following example: } struct Campaign { - address beneficiary; + address payable beneficiary; uint fundingGoal; uint numFunders; uint amount; @@ -870,7 +883,7 @@ shown in the following example: uint numCampaigns; mapping (uint => Campaign) campaigns; - function newCampaign(address beneficiary, uint goal) public returns (uint campaignID) { + function newCampaign(address payable beneficiary, uint goal) public returns (uint campaignID) { campaignID = numCampaigns++; // campaignID is return variable // Creates new struct and saves in storage. We leave out the mapping type. campaigns[campaignID] = Campaign(beneficiary, goal, 0, 0); diff --git a/docs/units-and-global-variables.rst b/docs/units-and-global-variables.rst index 55911dc6..2e1b90a0 100644 --- a/docs/units-and-global-variables.rst +++ b/docs/units-and-global-variables.rst @@ -57,7 +57,7 @@ Block and Transaction Properties -------------------------------- - ``block.blockhash(uint blockNumber) returns (bytes32)``: hash of the given block - only works for 256 most recent, excluding current, blocks - deprecated in version 0.4.22 and replaced by ``blockhash(uint blockNumber)``. -- ``block.coinbase`` (``address``): current block miner's address +- ``block.coinbase`` (``address payable``): current block miner's address - ``block.difficulty`` (``uint``): current block difficulty - ``block.gaslimit`` (``uint``): current block gaslimit - ``block.number`` (``uint``): current block number @@ -65,12 +65,12 @@ Block and Transaction Properties - ``gasleft() returns (uint256)``: remaining gas - ``msg.data`` (``bytes``): complete calldata - ``msg.gas`` (``uint``): remaining gas - deprecated in version 0.4.21 and to be replaced by ``gasleft()`` -- ``msg.sender`` (``address``): sender of the message (current call) +- ``msg.sender`` (``address payable``): sender of the message (current call) - ``msg.sig`` (``bytes4``): first four bytes of the calldata (i.e. function identifier) - ``msg.value`` (``uint``): number of wei sent with the message - ``now`` (``uint``): current block timestamp (alias for ``block.timestamp``) - ``tx.gasprice`` (``uint``): gas price of the transaction -- ``tx.origin`` (``address``): sender of the transaction (full call chain) +- ``tx.origin`` (``address payable``): sender of the transaction (full call chain) .. note:: The values of all members of ``msg``, including ``msg.sender`` and @@ -161,9 +161,9 @@ Address Related ``<address>.balance`` (``uint256``): balance of the :ref:`address` in Wei -``<address>.transfer(uint256 amount)``: +``<address payable>.transfer(uint256 amount)``: send given amount of Wei to :ref:`address`, throws on failure, forwards 2300 gas stipend, not adjustable -``<address>.send(uint256 amount) returns (bool)``: +``<address payable>.send(uint256 amount) returns (bool)``: send given amount of Wei to :ref:`address`, returns ``false`` on failure, forwards 2300 gas stipend, not adjustable ``<address>.call(bytes memory) returns (bool)``: issue low-level ``CALL`` with the given payload, returns ``false`` on failure, forwards all available gas, adjustable @@ -204,10 +204,10 @@ Contract Related ``this`` (current contract's type): the current contract, explicitly convertible to :ref:`address` -``selfdestruct(address recipient)``: +``selfdestruct(address payable recipient)``: destroy the current contract, sending its funds to the given :ref:`address` -``suicide(address recipient)``: +``suicide(address payable recipient)``: deprecated alias to ``selfdestruct`` Furthermore, all functions of the current contract are callable directly including the current function. diff --git a/libsolidity/analysis/GlobalContext.cpp b/libsolidity/analysis/GlobalContext.cpp index 3ac8fd47..cba2655c 100644 --- a/libsolidity/analysis/GlobalContext.cpp +++ b/libsolidity/analysis/GlobalContext.cpp @@ -56,10 +56,10 @@ m_magicVariables(vector<shared_ptr<MagicVariableDeclaration const>>{ make_shared<MagicVariableDeclaration>("revert", make_shared<FunctionType>(strings(), strings(), FunctionType::Kind::Revert, false, StateMutability::Pure)), make_shared<MagicVariableDeclaration>("revert", make_shared<FunctionType>(strings{"string memory"}, strings(), FunctionType::Kind::Revert, false, StateMutability::Pure)), make_shared<MagicVariableDeclaration>("ripemd160", make_shared<FunctionType>(strings{"bytes memory"}, strings{"bytes20"}, FunctionType::Kind::RIPEMD160, false, StateMutability::Pure)), - make_shared<MagicVariableDeclaration>("selfdestruct", make_shared<FunctionType>(strings{"address"}, strings{}, FunctionType::Kind::Selfdestruct)), + make_shared<MagicVariableDeclaration>("selfdestruct", make_shared<FunctionType>(strings{"address payable"}, strings{}, FunctionType::Kind::Selfdestruct)), make_shared<MagicVariableDeclaration>("sha256", make_shared<FunctionType>(strings{"bytes memory"}, strings{"bytes32"}, FunctionType::Kind::SHA256, false, StateMutability::Pure)), make_shared<MagicVariableDeclaration>("sha3", make_shared<FunctionType>(strings{"bytes memory"}, strings{"bytes32"}, FunctionType::Kind::KECCAK256, false, StateMutability::Pure)), - make_shared<MagicVariableDeclaration>("suicide", make_shared<FunctionType>(strings{"address"}, strings{}, FunctionType::Kind::Selfdestruct)), + make_shared<MagicVariableDeclaration>("suicide", make_shared<FunctionType>(strings{"address payable"}, strings{}, FunctionType::Kind::Selfdestruct)), make_shared<MagicVariableDeclaration>("tx", make_shared<MagicType>(MagicType::Kind::Transaction)) }) { diff --git a/libsolidity/analysis/ReferencesResolver.cpp b/libsolidity/analysis/ReferencesResolver.cpp index 4b678c3b..8a576e2e 100644 --- a/libsolidity/analysis/ReferencesResolver.cpp +++ b/libsolidity/analysis/ReferencesResolver.cpp @@ -119,11 +119,19 @@ bool ReferencesResolver::visit(ElementaryTypeName const& _typeName) { // for non-address types this was already caught by the parser solAssert(_typeName.annotation().type->category() == Type::Category::Address, ""); - if (!( - *_typeName.stateMutability() == StateMutability::Payable || - *_typeName.stateMutability() == StateMutability::NonPayable - )) - m_errorReporter.typeError(_typeName.location(), "Address types can only be payable or non-payable."); + switch(*_typeName.stateMutability()) + { + case StateMutability::Payable: + case StateMutability::NonPayable: + _typeName.annotation().type = make_shared<AddressType>(*_typeName.stateMutability()); + break; + default: + m_errorReporter.typeError( + _typeName.location(), + "Address types can only be payable or non-payable." + ); + break; + } } } return true; diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index 143ac109..e023dc38 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -571,6 +571,9 @@ TypePointers TypeChecker::typeCheckABIDecodeAndRetrieveReturnType(FunctionCall c // data locations. Furthermore, storage can be a little dangerous and // calldata is not really implemented anyway. actualType = ReferenceType::copyForLocationIfReference(DataLocation::Memory, actualType); + // We force address payable for address types. + if (actualType->category() == Type::Category::Address) + actualType = make_shared<AddressType>(StateMutability::Payable); solAssert( !actualType->dataStoredIn(DataLocation::CallData) && !actualType->dataStoredIn(DataLocation::Storage), @@ -1732,14 +1735,39 @@ bool TypeChecker::visit(FunctionCall const& _functionCall) dataLoc = argRefType->location(); resultType = ReferenceType::copyForLocationIfReference(dataLoc, resultType); if (!argType->isExplicitlyConvertibleTo(*resultType)) - m_errorReporter.typeError( - _functionCall.location(), - "Explicit type conversion not allowed from \"" + - argType->toString() + - "\" to \"" + - resultType->toString() + - "\"." - ); + { + if (resultType->category() == Type::Category::Contract && argType->category() == Type::Category::Address) + { + solAssert(dynamic_cast<ContractType const*>(resultType.get())->isPayable(), ""); + solAssert(dynamic_cast<AddressType const*>(argType.get())->stateMutability() < StateMutability::Payable, ""); + SecondarySourceLocation ssl; + if (auto const* identifier = dynamic_cast<Identifier const*>(arguments.front().get())) + if (auto const* variableDeclaration = dynamic_cast<VariableDeclaration const*>(identifier->annotation().referencedDeclaration)) + ssl.append("Did you mean to declare this variable as \"address payable\"?", variableDeclaration->location()); + m_errorReporter.typeError( + _functionCall.location(), ssl, + "Explicit type conversion not allowed from non-payable \"address\" to \"" + + resultType->toString() + + "\", which has a payable fallback function." + ); + } + else + m_errorReporter.typeError( + _functionCall.location(), + "Explicit type conversion not allowed from \"" + + argType->toString() + + "\" to \"" + + resultType->toString() + + "\"." + ); + } + if (resultType->category() == Type::Category::Address) + { + bool payable = true; + if (auto const* contractType = dynamic_cast<ContractType const*>(argType.get())) + payable = contractType->isPayable(); + resultType = make_shared<AddressType>(payable ? StateMutability::Payable : StateMutability::NonPayable); + } } _functionCall.annotation().type = resultType; _functionCall.annotation().isPure = isPure; @@ -2103,7 +2131,7 @@ bool TypeChecker::visit(MemberAccess const& _memberAccess) "after argument-dependent lookup in " + exprType->toString() + (memberName == "value" ? " - did you forget the \"payable\" modifier?" : "."); if (exprType->category() == Type::Category::Contract) - for (auto const& addressMember: AddressType().nativeMembers(nullptr)) + for (auto const& addressMember: AddressType(StateMutability::Payable).nativeMembers(nullptr)) if (addressMember.name == memberName) { Identifier const* var = dynamic_cast<Identifier const*>(&_memberAccess.expression()); @@ -2354,7 +2382,7 @@ void TypeChecker::endVisit(Literal const& _literal) if (_literal.looksLikeAddress()) { // Assign type here if it even looks like an address. This prevents double errors for invalid addresses - _literal.annotation().type = make_shared<AddressType>(); + _literal.annotation().type = make_shared<AddressType>(StateMutability::Payable); string msg; if (_literal.value().length() != 42) // "0x" + 40 hex digits diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp index a302203b..25702f19 100644 --- a/libsolidity/ast/Types.cpp +++ b/libsolidity/ast/Types.cpp @@ -299,7 +299,7 @@ TypePointer Type::fromElementaryTypeName(ElementaryTypeNameToken const& _type) case Token::Byte: return make_shared<FixedBytesType>(1); case Token::Address: - return make_shared<AddressType>(); + return make_shared<AddressType>(StateMutability::NonPayable); case Token::Bool: return make_shared<BoolType>(); case Token::Bytes: @@ -340,6 +340,17 @@ TypePointer Type::fromElementaryTypeName(string const& _name) } return ref->copyForLocation(location, true); } + else if (t->category() == Type::Category::Address) + { + if (nameParts.size() == 2) + { + if (nameParts[1] == "payable") + return make_shared<AddressType>(StateMutability::Payable); + else + solAssert(false, "Invalid state mutability for address type: " + nameParts[1]); + } + return make_shared<AddressType>(StateMutability::NonPayable); + } else { solAssert(nameParts.size() == 1, "Storage location suffix only allowed for reference types"); @@ -439,21 +450,48 @@ MemberList::MemberMap Type::boundFunctions(Type const& _type, ContractDefinition return members; } +AddressType::AddressType(StateMutability _stateMutability): + m_stateMutability(_stateMutability) +{ + solAssert(m_stateMutability == StateMutability::Payable || m_stateMutability == StateMutability::NonPayable, ""); +} + string AddressType::richIdentifier() const { - return "t_address"; + if (m_stateMutability == StateMutability::Payable) + return "t_address_payable"; + else + return "t_address"; +} + +bool AddressType::isImplicitlyConvertibleTo(Type const& _other) const +{ + if (_other.category() != category()) + return false; + AddressType const& other = dynamic_cast<AddressType const&>(_other); + + return other.m_stateMutability <= m_stateMutability; } bool AddressType::isExplicitlyConvertibleTo(Type const& _convertTo) const { + if (auto const* contractType = dynamic_cast<ContractType const*>(&_convertTo)) + return (m_stateMutability >= StateMutability::Payable) || !contractType->isPayable(); return isImplicitlyConvertibleTo(_convertTo) || - _convertTo.category() == Category::Contract || _convertTo.category() == Category::Integer || (_convertTo.category() == Category::FixedBytes && 160 == dynamic_cast<FixedBytesType const&>(_convertTo).numBytes() * 8); } string AddressType::toString(bool) const { + if (m_stateMutability == StateMutability::Payable) + return "address payable"; + else + return "address"; +} + +string AddressType::canonicalName() const +{ return "address"; } @@ -479,17 +517,29 @@ TypePointer AddressType::binaryOperatorResult(Token::Value _operator, TypePointe return Type::commonType(shared_from_this(), _other); } +bool AddressType::operator==(Type const& _other) const +{ + if (_other.category() != category()) + return false; + AddressType const& other = dynamic_cast<AddressType const&>(_other); + return other.m_stateMutability == m_stateMutability; +} + MemberList::MemberMap AddressType::nativeMembers(ContractDefinition const*) const { - return { + MemberList::MemberMap members = { {"balance", make_shared<IntegerType>(256)}, {"call", make_shared<FunctionType>(strings{"bytes memory"}, strings{"bool", "bytes memory"}, FunctionType::Kind::BareCall, false, StateMutability::Payable)}, {"callcode", make_shared<FunctionType>(strings{"bytes memory"}, strings{"bool", "bytes memory"}, FunctionType::Kind::BareCallCode, false, StateMutability::Payable)}, {"delegatecall", make_shared<FunctionType>(strings{"bytes memory"}, strings{"bool", "bytes memory"}, FunctionType::Kind::BareDelegateCall, false)}, - {"send", make_shared<FunctionType>(strings{"uint"}, strings{"bool"}, FunctionType::Kind::Send)}, - {"staticcall", make_shared<FunctionType>(strings{"bytes memory"}, strings{"bool", "bytes memory"}, FunctionType::Kind::BareStaticCall, false, StateMutability::View)}, - {"transfer", make_shared<FunctionType>(strings{"uint"}, strings(), FunctionType::Kind::Transfer)} + {"staticcall", make_shared<FunctionType>(strings{"bytes memory"}, strings{"bool", "bytes memory"}, FunctionType::Kind::BareStaticCall, false, StateMutability::View)} }; + if (m_stateMutability == StateMutability::Payable) + { + members.emplace_back(MemberList::Member{"send", make_shared<FunctionType>(strings{"uint"}, strings{"bool"}, FunctionType::Kind::Send)}); + members.emplace_back(MemberList::Member{"transfer", make_shared<FunctionType>(strings{"uint"}, strings(), FunctionType::Kind::Transfer)}); + } + return members; } namespace @@ -1476,7 +1526,9 @@ bool ContractType::isImplicitlyConvertibleTo(Type const& _convertTo) const bool ContractType::isExplicitlyConvertibleTo(Type const& _convertTo) const { - return isImplicitlyConvertibleTo(_convertTo) || _convertTo.category() == Category::Address; + if (auto const* addressType = dynamic_cast<AddressType const*>(&_convertTo)) + return isPayable() || (addressType->stateMutability() < StateMutability::Payable); + return isImplicitlyConvertibleTo(_convertTo); } bool ContractType::isPayable() const @@ -3275,7 +3327,7 @@ MemberList::MemberMap MagicType::nativeMembers(ContractDefinition const*) const { case Kind::Block: return MemberList::MemberMap({ - {"coinbase", make_shared<AddressType>()}, + {"coinbase", make_shared<AddressType>(StateMutability::Payable)}, {"timestamp", make_shared<IntegerType>(256)}, {"blockhash", make_shared<FunctionType>(strings{"uint"}, strings{"bytes32"}, FunctionType::Kind::BlockHash, false, StateMutability::View)}, {"difficulty", make_shared<IntegerType>(256)}, @@ -3284,7 +3336,7 @@ MemberList::MemberMap MagicType::nativeMembers(ContractDefinition const*) const }); case Kind::Message: return MemberList::MemberMap({ - {"sender", make_shared<AddressType>()}, + {"sender", make_shared<AddressType>(StateMutability::Payable)}, {"gas", make_shared<IntegerType>(256)}, {"value", make_shared<IntegerType>(256)}, {"data", make_shared<ArrayType>(DataLocation::CallData)}, @@ -3292,7 +3344,7 @@ MemberList::MemberMap MagicType::nativeMembers(ContractDefinition const*) const }); case Kind::Transaction: return MemberList::MemberMap({ - {"origin", make_shared<AddressType>()}, + {"origin", make_shared<AddressType>(StateMutability::Payable)}, {"gasprice", make_shared<IntegerType>(256)} }); case Kind::ABI: diff --git a/libsolidity/ast/Types.h b/libsolidity/ast/Types.h index 7ee66838..a2d18b0a 100644 --- a/libsolidity/ast/Types.h +++ b/libsolidity/ast/Types.h @@ -321,15 +321,16 @@ class AddressType: public Type public: virtual Category category() const override { return Category::Address; } - explicit AddressType() - { - } + explicit AddressType(StateMutability _stateMutability); virtual std::string richIdentifier() const override; + virtual bool isImplicitlyConvertibleTo(Type const& _other) const override; virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const override; virtual TypePointer unaryOperatorResult(Token::Value _operator) const override; virtual TypePointer binaryOperatorResult(Token::Value _operator, TypePointer const& _other) const override; + virtual bool operator==(Type const& _other) const override; + virtual unsigned calldataEncodedSize(bool _padded = true) const override { return _padded ? 32 : 160 / 8; } virtual unsigned storageBytes() const override { return 160 / 8; } virtual bool isValueType() const override { return true; } @@ -337,11 +338,17 @@ public: virtual MemberList::MemberMap nativeMembers(ContractDefinition const*) const override; virtual std::string toString(bool _short) const override; + virtual std::string canonicalName() const override; virtual u256 literalValue(Literal const* _literal) const override; virtual TypePointer encodingType() const override { return shared_from_this(); } virtual TypePointer interfaceType(bool) const override { return shared_from_this(); } + + StateMutability stateMutability(void) const { return m_stateMutability; } + +private: + StateMutability m_stateMutability; }; /** @@ -755,7 +762,7 @@ public: { if (isSuper()) return TypePointer{}; - return std::make_shared<AddressType>(); + return std::make_shared<AddressType>(isPayable() ? StateMutability::Payable : StateMutability::NonPayable); } virtual TypePointer interfaceType(bool _inLibrary) const override { diff --git a/libsolidity/codegen/ABIFunctions.cpp b/libsolidity/codegen/ABIFunctions.cpp index 5e5fe84a..6c27533c 100644 --- a/libsolidity/codegen/ABIFunctions.cpp +++ b/libsolidity/codegen/ABIFunctions.cpp @@ -242,8 +242,14 @@ string ABIFunctions::cleanupFunction(Type const& _type, bool _revertOnFailure) break; } case Type::Category::Contract: - templ("body", "cleaned := " + cleanupFunction(AddressType()) + "(value)"); + { + AddressType addressType(dynamic_cast<ContractType const&>(_type).isPayable() ? + StateMutability::Payable : + StateMutability::NonPayable + ); + templ("body", "cleaned := " + cleanupFunction(addressType) + "(value)"); break; + } case Type::Category::Enum: { size_t members = dynamic_cast<EnumType const&>(_type).numberOfMembers(); diff --git a/libsolidity/codegen/ExpressionCompiler.cpp b/libsolidity/codegen/ExpressionCompiler.cpp index 45e58bd0..bd863e05 100644 --- a/libsolidity/codegen/ExpressionCompiler.cpp +++ b/libsolidity/codegen/ExpressionCompiler.cpp @@ -1259,7 +1259,7 @@ bool ExpressionCompiler::visit(MemberAccess const& _memberAccess) identifier = FunctionType(*function).externalIdentifier(); else solAssert(false, "Contract member is neither variable nor function."); - utils().convertType(type, AddressType(), true); + utils().convertType(type, AddressType(type.isPayable() ? StateMutability::Payable : StateMutability::NonPayable), true); m_context << identifier; } else @@ -1277,15 +1277,24 @@ bool ExpressionCompiler::visit(MemberAccess const& _memberAccess) { utils().convertType( *_memberAccess.expression().annotation().type, - AddressType(), + AddressType(StateMutability::NonPayable), true ); m_context << Instruction::BALANCE; } - else if ((set<string>{"send", "transfer", "call", "callcode", "delegatecall", "staticcall"}).count(member)) + else if ((set<string>{"send", "transfer"}).count(member)) + { + solAssert(dynamic_cast<AddressType const&>(*_memberAccess.expression().annotation().type).stateMutability() == StateMutability::Payable, ""); + utils().convertType( + *_memberAccess.expression().annotation().type, + AddressType(StateMutability::Payable), + true + ); + } + else if ((set<string>{"call", "callcode", "delegatecall", "staticcall"}).count(member)) utils().convertType( *_memberAccess.expression().annotation().type, - AddressType(), + AddressType(StateMutability::NonPayable), true ); else diff --git a/test/libsolidity/syntaxTests/parsing/address_in_struct.sol b/test/libsolidity/syntaxTests/parsing/address_in_struct.sol new file mode 100644 index 00000000..68049b50 --- /dev/null +++ b/test/libsolidity/syntaxTests/parsing/address_in_struct.sol @@ -0,0 +1,6 @@ +contract C { + struct S { + address payable a; + address b; + } +} diff --git a/test/libsolidity/syntaxTests/parsing/address_nonpayable.sol b/test/libsolidity/syntaxTests/parsing/address_nonpayable.sol new file mode 100644 index 00000000..fea67138 --- /dev/null +++ b/test/libsolidity/syntaxTests/parsing/address_nonpayable.sol @@ -0,0 +1,7 @@ +contract C { + address a; + function f(address b) public pure returns (address c) { + address d = b; + return d; + } +} diff --git a/test/libsolidity/syntaxTests/parsing/address_payable.sol b/test/libsolidity/syntaxTests/parsing/address_payable.sol new file mode 100644 index 00000000..c29ae1b7 --- /dev/null +++ b/test/libsolidity/syntaxTests/parsing/address_payable.sol @@ -0,0 +1,7 @@ +contract C { + address payable a; + function f(address payable b) public pure returns (address payable c) { + address payable d = b; + return d; + } +} diff --git a/test/libsolidity/syntaxTests/types/address/address_abi_decode.sol b/test/libsolidity/syntaxTests/types/address/address_abi_decode.sol new file mode 100644 index 00000000..7be61ad2 --- /dev/null +++ b/test/libsolidity/syntaxTests/types/address/address_abi_decode.sol @@ -0,0 +1,6 @@ +contract C { + function f(bytes memory b) public pure returns (address payable) { + (address payable c) = abi.decode(b, (address)); + return c; + } +}
\ No newline at end of file diff --git a/test/libsolidity/syntaxTests/types/address/address_constant.sol b/test/libsolidity/syntaxTests/types/address/address_constant.sol new file mode 100644 index 00000000..0b1af991 --- /dev/null +++ b/test/libsolidity/syntaxTests/types/address/address_constant.sol @@ -0,0 +1,7 @@ +contract C { + address constant a = address(0); + address payable constant b = address(0); + function f() public pure returns (address, address) { + return (a,b); + } +} diff --git a/test/libsolidity/syntaxTests/types/address/address_constant_assignment.sol b/test/libsolidity/syntaxTests/types/address/address_constant_assignment.sol new file mode 100644 index 00000000..da17ae33 --- /dev/null +++ b/test/libsolidity/syntaxTests/types/address/address_constant_assignment.sol @@ -0,0 +1,11 @@ +contract C { + address constant a = address(0); + address payable constant b = address(0); + function f() public { + a = address(0); + b = address(0); + } +} +// ---- +// TypeError: (129-130): Cannot assign to a constant variable. +// TypeError: (153-154): Cannot assign to a constant variable. diff --git a/test/libsolidity/syntaxTests/types/address/address_in_struct_fail.sol b/test/libsolidity/syntaxTests/types/address/address_in_struct_fail.sol new file mode 100644 index 00000000..9a5b2abb --- /dev/null +++ b/test/libsolidity/syntaxTests/types/address/address_in_struct_fail.sol @@ -0,0 +1,11 @@ +contract A { + struct S { + address payable a; + } + S s; + function f() public { + s.a = address(this); + } +} +// ---- +// TypeError: (110-123): Type address is not implicitly convertible to expected type address payable. diff --git a/test/libsolidity/syntaxTests/types/address/address_in_struct_fine.sol b/test/libsolidity/syntaxTests/types/address/address_in_struct_fine.sol new file mode 100644 index 00000000..5519f0ef --- /dev/null +++ b/test/libsolidity/syntaxTests/types/address/address_in_struct_fine.sol @@ -0,0 +1,20 @@ +contract A { + struct S { + address a; + } + S s; + function f() public { + s.a = address(this); + } +} +contract B { + struct S { + address payable a; + } + S s; + function f() public { + s.a = address(this); + } + function() external payable { + } +}
\ No newline at end of file diff --git a/test/libsolidity/syntaxTests/types/address/address_nonpayable_selfdestruct.sol b/test/libsolidity/syntaxTests/types/address/address_nonpayable_selfdestruct.sol new file mode 100644 index 00000000..cc680ff3 --- /dev/null +++ b/test/libsolidity/syntaxTests/types/address/address_nonpayable_selfdestruct.sol @@ -0,0 +1,7 @@ +contract C { + function f(address a) public { + selfdestruct(a); + } +} +// ---- +// TypeError: (69-70): Invalid type for argument in function call. Invalid implicit conversion from address to address payable requested. diff --git a/test/libsolidity/syntaxTests/types/address/address_payable_external_overload.sol b/test/libsolidity/syntaxTests/types/address/address_payable_external_overload.sol new file mode 100644 index 00000000..875532c4 --- /dev/null +++ b/test/libsolidity/syntaxTests/types/address/address_payable_external_overload.sol @@ -0,0 +1,7 @@ +contract C { + function f(address) external pure {} + function f(address payable) external pure {} + +} +// ---- +// TypeError: (58-102): Function overload clash during conversion to external types for arguments. diff --git a/test/libsolidity/syntaxTests/types/address/address_payable_internal_overload_nonpayable.sol b/test/libsolidity/syntaxTests/types/address/address_payable_internal_overload_nonpayable.sol new file mode 100644 index 00000000..65600544 --- /dev/null +++ b/test/libsolidity/syntaxTests/types/address/address_payable_internal_overload_nonpayable.sol @@ -0,0 +1,10 @@ +contract C { + function f(address payable) internal pure {} + function f(address) internal pure returns (uint) {} + function g() internal pure { + address a = address(0); + uint b = f(a); // TODO: should this be valid? + b; + } +} +// ---- diff --git a/test/libsolidity/syntaxTests/types/address/address_payable_internal_overload_payable.sol b/test/libsolidity/syntaxTests/types/address/address_payable_internal_overload_payable.sol new file mode 100644 index 00000000..84142a31 --- /dev/null +++ b/test/libsolidity/syntaxTests/types/address/address_payable_internal_overload_payable.sol @@ -0,0 +1,10 @@ +contract C { + function f(address payable) internal pure {} + function f(address) internal pure {} + function g() internal pure { + address payable a = address(0); + f(a); + } +} +// ---- +// TypeError: (184-185): No unique declaration found after argument-dependent lookup. diff --git a/test/libsolidity/syntaxTests/types/address/address_payable_memory_array_conversion.sol b/test/libsolidity/syntaxTests/types/address/address_payable_memory_array_conversion.sol new file mode 100644 index 00000000..ec58170b --- /dev/null +++ b/test/libsolidity/syntaxTests/types/address/address_payable_memory_array_conversion.sol @@ -0,0 +1,11 @@ +contract C { + function f() public pure { + address payable[] memory a = new address payable[](4); + address[] memory b = new address[](4); + a = b; + b = a; + } +} +// ---- +// TypeError: (166-167): Type address[] memory is not implicitly convertible to expected type address payable[] memory. +// TypeError: (181-182): Type address payable[] memory is not implicitly convertible to expected type address[] memory. diff --git a/test/libsolidity/syntaxTests/types/address/address_payable_public_overload.sol b/test/libsolidity/syntaxTests/types/address/address_payable_public_overload.sol new file mode 100644 index 00000000..839abc26 --- /dev/null +++ b/test/libsolidity/syntaxTests/types/address/address_payable_public_overload.sol @@ -0,0 +1,7 @@ +contract C { + function f(address) public pure {} + function f(address payable) public pure {} + +} +// ---- +// TypeError: (56-98): Function overload clash during conversion to external types for arguments. diff --git a/test/libsolidity/syntaxTests/types/address/address_payable_selfdestruct.sol b/test/libsolidity/syntaxTests/types/address/address_payable_selfdestruct.sol new file mode 100644 index 00000000..bdf43be7 --- /dev/null +++ b/test/libsolidity/syntaxTests/types/address/address_payable_selfdestruct.sol @@ -0,0 +1,5 @@ +contract C { + function f(address payable a) public { + selfdestruct(a); + } +} diff --git a/test/libsolidity/syntaxTests/types/address/address_payable_storage_array_conversion.sol b/test/libsolidity/syntaxTests/types/address/address_payable_storage_array_conversion.sol new file mode 100644 index 00000000..40f85ccc --- /dev/null +++ b/test/libsolidity/syntaxTests/types/address/address_payable_storage_array_conversion.sol @@ -0,0 +1,11 @@ +contract C { + address payable[] a; + address[] b; + function f() public view { + address payable[] storage c = a; + address[] storage d = b; + d = c; // TODO: this could be allowed in the future + } +} +// ---- +// TypeError: (172-173): Type address payable[] storage pointer is not implicitly convertible to expected type address[] storage pointer. diff --git a/test/libsolidity/syntaxTests/types/address/address_payable_storage_array_conversion_fail.sol b/test/libsolidity/syntaxTests/types/address/address_payable_storage_array_conversion_fail.sol new file mode 100644 index 00000000..3c3eb859 --- /dev/null +++ b/test/libsolidity/syntaxTests/types/address/address_payable_storage_array_conversion_fail.sol @@ -0,0 +1,11 @@ +contract C { + address payable[] a; + address[] b; + function f() public view { + address payable[] storage c = a; + address[] storage d = b; + c = d; + } +} +// ---- +// TypeError: (172-173): Type address[] storage pointer is not implicitly convertible to expected type address payable[] storage pointer. diff --git a/test/libsolidity/syntaxTests/types/address/address_to_contract_implicitly.sol b/test/libsolidity/syntaxTests/types/address/address_to_contract_implicitly.sol new file mode 100644 index 00000000..c9e5ddf6 --- /dev/null +++ b/test/libsolidity/syntaxTests/types/address/address_to_contract_implicitly.sol @@ -0,0 +1,7 @@ +contract C { + function f() public view { + C c = address(2); + } +} +// ---- +// TypeError: (46-62): Type address payable is not implicitly convertible to expected type contract C. diff --git a/test/libsolidity/syntaxTests/types/address/address_to_contract_payable_fallback.sol b/test/libsolidity/syntaxTests/types/address/address_to_contract_payable_fallback.sol new file mode 100644 index 00000000..6917444b --- /dev/null +++ b/test/libsolidity/syntaxTests/types/address/address_to_contract_payable_fallback.sol @@ -0,0 +1,8 @@ +contract C { + function f() public pure returns (C c) { + c = C(address(2)); + } + function() external payable { + } +} +// ---- diff --git a/test/libsolidity/syntaxTests/types/address/address_to_payable_address.sol b/test/libsolidity/syntaxTests/types/address/address_to_payable_address.sol new file mode 100644 index 00000000..1aab9b51 --- /dev/null +++ b/test/libsolidity/syntaxTests/types/address/address_to_payable_address.sol @@ -0,0 +1,10 @@ +contract C { + function f(address a) public pure { + address b; + address payable c = a; + c = b; + } +} +// ---- +// TypeError: (80-101): Type address is not implicitly convertible to expected type address payable. +// TypeError: (115-116): Type address is not implicitly convertible to expected type address payable. diff --git a/test/libsolidity/syntaxTests/types/address/address_tuple_fail.sol b/test/libsolidity/syntaxTests/types/address/address_tuple_fail.sol new file mode 100644 index 00000000..17e9e7c1 --- /dev/null +++ b/test/libsolidity/syntaxTests/types/address/address_tuple_fail.sol @@ -0,0 +1,8 @@ +contract C { + function f() public view returns (address payable a, address b) { + (address c, address payable d) = (address(this), address(0)); + (a,b) = (c,d); + } +} +// ---- +// TypeError: (169-174): Type tuple(address,address payable) is not implicitly convertible to expected type tuple(address payable,address). diff --git a/test/libsolidity/syntaxTests/types/address/address_tuple_fine.sol b/test/libsolidity/syntaxTests/types/address/address_tuple_fine.sol new file mode 100644 index 00000000..846de1f4 --- /dev/null +++ b/test/libsolidity/syntaxTests/types/address/address_tuple_fine.sol @@ -0,0 +1,6 @@ +contract C { + function f() public view returns (address payable a, address b) { + (address c, address payable d) = (address(this), address(0)); + (a,b) = (d,c); + } +}
\ No newline at end of file diff --git a/test/libsolidity/syntaxTests/types/address/contract_no_fallback_to_payable_address.sol b/test/libsolidity/syntaxTests/types/address/contract_no_fallback_to_payable_address.sol new file mode 100644 index 00000000..777bce00 --- /dev/null +++ b/test/libsolidity/syntaxTests/types/address/contract_no_fallback_to_payable_address.sol @@ -0,0 +1,8 @@ +contract C { + function f() public view { + address payable a = address(this); + a; + } +} +// ---- +// TypeError: (46-79): Type address is not implicitly convertible to expected type address payable. diff --git a/test/libsolidity/syntaxTests/types/address/contract_non_payable_fallback_to_payable_address.sol b/test/libsolidity/syntaxTests/types/address/contract_non_payable_fallback_to_payable_address.sol new file mode 100644 index 00000000..6518eebb --- /dev/null +++ b/test/libsolidity/syntaxTests/types/address/contract_non_payable_fallback_to_payable_address.sol @@ -0,0 +1,10 @@ +contract C { + function f() public view { + address payable a = address(this); + a; + } + function() external { + } +} +// ---- +// TypeError: (46-79): Type address is not implicitly convertible to expected type address payable. diff --git a/test/libsolidity/syntaxTests/types/address/contract_payable_fallback_to_payable_address.sol b/test/libsolidity/syntaxTests/types/address/contract_payable_fallback_to_payable_address.sol new file mode 100644 index 00000000..359beeb4 --- /dev/null +++ b/test/libsolidity/syntaxTests/types/address/contract_payable_fallback_to_payable_address.sol @@ -0,0 +1,9 @@ +contract C { + function f() public view { + address payable a = address(this); + a; + } + function() external payable { + } +} +// ---- diff --git a/test/libsolidity/syntaxTests/types/address/contract_payable_fallback_to_payable_address_implicitly.sol b/test/libsolidity/syntaxTests/types/address/contract_payable_fallback_to_payable_address_implicitly.sol new file mode 100644 index 00000000..4b20b1c6 --- /dev/null +++ b/test/libsolidity/syntaxTests/types/address/contract_payable_fallback_to_payable_address_implicitly.sol @@ -0,0 +1,10 @@ +contract C { + function f() public view { + address payable a = this; + a; + } + function() external payable { + } +} +// ---- +// TypeError: (46-70): Type contract C is not implicitly convertible to expected type address payable. diff --git a/test/libsolidity/syntaxTests/types/address/literal_to_address.sol b/test/libsolidity/syntaxTests/types/address/literal_to_address.sol new file mode 100644 index 00000000..9d599ea5 --- /dev/null +++ b/test/libsolidity/syntaxTests/types/address/literal_to_address.sol @@ -0,0 +1,8 @@ +contract C { + function f() public pure { + address a = address(0); + a = address(1); + address b = 0x0123456789012345678901234567890123456789; + b = 0x9876543210987654321098765432109876543210; + } +} diff --git a/test/libsolidity/syntaxTests/types/address/literal_to_payable_address.sol b/test/libsolidity/syntaxTests/types/address/literal_to_payable_address.sol new file mode 100644 index 00000000..97f4d85d --- /dev/null +++ b/test/libsolidity/syntaxTests/types/address/literal_to_payable_address.sol @@ -0,0 +1,8 @@ +contract C { + function f() public pure { + address payable a = address(0); + a = address(1); + address payable b = 0x0123456789012345678901234567890123456789; + b = 0x9876543210987654321098765432109876543210; + } +}
\ No newline at end of file diff --git a/test/libsolidity/syntaxTests/types/address/nonpayable_address_to_contract_payable_fallback.sol b/test/libsolidity/syntaxTests/types/address/nonpayable_address_to_contract_payable_fallback.sol new file mode 100644 index 00000000..e13a0897 --- /dev/null +++ b/test/libsolidity/syntaxTests/types/address/nonpayable_address_to_contract_payable_fallback.sol @@ -0,0 +1,10 @@ +contract C { + function f() public pure returns (C c) { + address a = address(2); + c = C(a); + } + function() external payable { + } +} +// ---- +// TypeError: (92-96): Explicit type conversion not allowed from non-payable "address" to "contract C", which has a payable fallback function. diff --git a/test/libsolidity/syntaxTests/types/address/payable_address_to_address.sol b/test/libsolidity/syntaxTests/types/address/payable_address_to_address.sol new file mode 100644 index 00000000..f5dbf937 --- /dev/null +++ b/test/libsolidity/syntaxTests/types/address/payable_address_to_address.sol @@ -0,0 +1,7 @@ +contract C { + function f(address payable a) public pure { + address payable b; + address c = a; + c = b; + } +}
\ No newline at end of file diff --git a/test/libsolidity/syntaxTests/types/address_to_contract_implicitly.sol b/test/libsolidity/syntaxTests/types/address_to_contract_implicitly.sol deleted file mode 100644 index efab7c27..00000000 --- a/test/libsolidity/syntaxTests/types/address_to_contract_implicitly.sol +++ /dev/null @@ -1,7 +0,0 @@ -contract C { - function f() public view { - C c = address(2); - } -} -// ---- -// TypeError: (46-62): Type address is not implicitly convertible to expected type contract C. |