Add payable and non-payable state mutability to AddressType.

This commit is contained in:
Daniel Kirchner 2018-09-05 17:59:55 +02:00
parent 9214c7c34f
commit 12aaca1645
47 changed files with 468 additions and 82 deletions

View File

@ -7,6 +7,7 @@ How to update your code:
* Make your fallback functions ``external``. * 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 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(...)``. * 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: Breaking Changes:
* ABI Encoder: Properly pad data from calldata (``msg.data`` and external function parameters). Use ``abi.encodePacked`` for unpadded encoding. * 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 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 decimal literals to ``bytesXX`` types.
* Type System: Disallow explicit and implicit conversions from hex literals to ``bytesXX`` types of different size. * 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. * 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. * 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. * References Resolver: Turn missing storage locations into an error. This was already the case in the experimental 0.5.0 mode.

View File

@ -68,7 +68,7 @@ This is as opposed to the more intuitive sending pattern:
pragma solidity >0.4.24; pragma solidity >0.4.24;
contract SendContract { contract SendContract {
address public richest; address payable public richest;
uint public mostSent; uint public mostSent;
constructor() public payable { constructor() public payable {

View File

@ -334,7 +334,7 @@ inheritable properties of contracts and may be overridden by derived contracts.
contract owned { contract owned {
constructor() public { owner = msg.sender; } constructor() public { owner = msg.sender; }
address owner; address payable owner;
// This contract only defines a modifier but does not use // This contract only defines a modifier but does not use
// it: it will be used in derived contracts. // 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); require(success);
// results in test.x becoming == 1. // 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, // If someone sends ether to that contract,
// the transfer will fail, i.e. this returns false here. // 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 { contract owned {
constructor() public { owner = msg.sender; } constructor() public { owner = msg.sender; }
address owner; address payable owner;
} }
// Use `is` to derive from another contract. Derived // Use `is` to derive from another contract. Derived
@ -963,7 +968,7 @@ seen in the following example::
contract owned { contract owned {
constructor() public { owner = msg.sender; } constructor() public { owner = msg.sender; }
address owner; address payable owner;
} }
contract mortal is owned { contract mortal is owned {
@ -992,7 +997,7 @@ derived override, but this function will bypass
contract owned { contract owned {
constructor() public { owner = msg.sender; } constructor() public { owner = msg.sender; }
address owner; address payable owner;
} }
contract mortal is owned { contract mortal is owned {

View File

@ -421,7 +421,7 @@ a message string for ``require``, but not for ``assert``.
pragma solidity >0.4.24; pragma solidity >0.4.24;
contract Sharer { 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."); require(msg.value % 2 == 0, "Even value required.");
uint balanceBeforeTransfer = address(this).balance; uint balanceBeforeTransfer = address(this).balance;
addr.transfer(msg.value / 2); addr.transfer(msg.value / 2);

View File

@ -329,7 +329,7 @@ Global Variables
starting from the second and prepends the given four-byte selector 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)), ...)``` - ``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.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.difficulty`` (``uint``): current block difficulty
- ``block.gaslimit`` (``uint``): current block gaslimit - ``block.gaslimit`` (``uint``): current block gaslimit
- ``block.number`` (``uint``): current block number - ``block.number`` (``uint``): current block number
@ -337,11 +337,11 @@ Global Variables
- ``gasleft() returns (uint256)``: remaining gas - ``gasleft() returns (uint256)``: remaining gas
- ``msg.data`` (``bytes``): complete calldata - ``msg.data`` (``bytes``): complete calldata
- ``msg.gas`` (``uint``): remaining gas - deprecated in version 0.4.21 and to be replaced by ``gasleft()`` - ``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 - ``msg.value`` (``uint``): number of wei sent with the message
- ``now`` (``uint``): current block timestamp (alias for ``block.timestamp``) - ``now`` (``uint``): current block timestamp (alias for ``block.timestamp``)
- ``tx.gasprice`` (``uint``): gas price of the transaction - ``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) - ``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)``: abort execution and revert state changes if condition is ``false`` (use for malformed input or error in external component)
- ``require(bool condition, string message)``: abort execution and revert state changes if condition is ``false`` (use for malformed input or error in external component). Also provide error message. - ``require(bool condition, string 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 - ``selfdestruct(address recipient)``: destroy the current contract, sending its funds to the given address
- ``suicide(address recipient)``: a deprecated alias to ``selfdestruct`` - ``suicide(address recipient)``: a deprecated alias to ``selfdestruct``
- ``<address>.balance`` (``uint256``): balance of the :ref:`address` in Wei - ``<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 payable>.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>.transfer(uint256 amount)``: send given amount of Wei to :ref:`address`, throws on failure
.. note:: .. note::
Do not rely on ``block.timestamp``, ``now`` and ``blockhash`` as a source of randomness, Do not rely on ``block.timestamp``, ``now`` and ``blockhash`` as a source of randomness,

View File

@ -192,7 +192,7 @@ Never use tx.origin for authorization. Let's say you have a wallet contract like
owner = msg.sender; owner = msg.sender;
} }
function transferTo(address dest, uint amount) public { function transferTo(address payable dest, uint amount) public {
require(tx.origin == owner); require(tx.origin == owner);
dest.transfer(amount); 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; pragma solidity >0.4.24;
interface TxUserWallet { interface TxUserWallet {
function transferTo(address dest, uint amount) external; function transferTo(address payable dest, uint amount) external;
} }
contract TxAttackWallet { contract TxAttackWallet {
address owner; address payable owner;
constructor() public { constructor() public {
owner = msg.sender; owner = msg.sender;

View File

@ -231,7 +231,7 @@ activate themselves.
// Parameters of the auction. Times are either // Parameters of the auction. Times are either
// absolute unix timestamps (seconds since 1970-01-01) // absolute unix timestamps (seconds since 1970-01-01)
// or time periods in seconds. // or time periods in seconds.
address public beneficiary; address payable public beneficiary;
uint public auctionEndTime; uint public auctionEndTime;
// Current state of the auction. // Current state of the auction.
@ -258,7 +258,7 @@ activate themselves.
/// beneficiary address `_beneficiary`. /// beneficiary address `_beneficiary`.
constructor( constructor(
uint _biddingTime, uint _biddingTime,
address _beneficiary address payable _beneficiary
) public { ) public {
beneficiary = _beneficiary; beneficiary = _beneficiary;
auctionEndTime = now + _biddingTime; auctionEndTime = now + _biddingTime;
@ -396,7 +396,7 @@ high or low invalid bids.
uint deposit; uint deposit;
} }
address public beneficiary; address payable public beneficiary;
uint public biddingEnd; uint public biddingEnd;
uint public revealEnd; uint public revealEnd;
bool public ended; bool public ended;
@ -421,7 +421,7 @@ high or low invalid bids.
constructor( constructor(
uint _biddingTime, uint _biddingTime,
uint _revealTime, uint _revealTime,
address _beneficiary address payable _beneficiary
) public { ) public {
beneficiary = _beneficiary; beneficiary = _beneficiary;
biddingEnd = now + _biddingTime; biddingEnd = now + _biddingTime;
@ -545,8 +545,8 @@ Safe Remote Purchase
contract Purchase { contract Purchase {
uint public value; uint public value;
address public seller; address payable public seller;
address public buyer; address payable public buyer;
enum State { Created, Locked, Inactive } enum State { Created, Locked, Inactive }
State public state; State public state;
@ -990,11 +990,11 @@ The full contract
pragma solidity ^0.4.24; pragma solidity ^0.4.24;
contract SimplePaymentChannel { contract SimplePaymentChannel {
address public sender; // The account sending payments. address payable public sender; // The account sending payments.
address public recipient; // The account receiving the payments. address payable public recipient; // The account receiving the payments.
uint256 public expiration; // Timeout in case the recipient never closes. uint256 public expiration; // Timeout in case the recipient never closes.
constructor (address _recipient, uint256 duration) constructor (address payable _recipient, uint256 duration)
public public
payable payable
{ {

View File

@ -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``: 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: Operators:
@ -113,7 +122,8 @@ Operators:
or you can use ``address(uint160(uint256(b)))``, which results in ``0x777788889999AaAAbBbbCcccddDdeeeEfFFfCcCc``. or you can use ``address(uint160(uint256(b)))``, which results in ``0x777788889999AaAAbBbbCcccddDdeeeEfFFfCcCc``.
.. note:: .. 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: .. _members-of-addresses:
@ -125,11 +135,11 @@ Members of Addresses
For a quick reference, see :ref:`address_related`. For a quick reference, see :ref:`address_related`.
It is possible to query the balance of an address using the property ``balance`` 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; address myAddress = this;
if (x.balance < 10 && myAddress.balance >= 10) x.transfer(10); if (x.balance < 10 && myAddress.balance >= 10) x.transfer(10);
@ -211,11 +221,14 @@ Contract Types
Every :ref:`contract<contracts>` defines its own type. Every :ref:`contract<contracts>` defines its own type.
You can implicitly convert contracts to contracts they inherit from, 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:: .. note::
Starting with version 0.5.0 contracts do not derive from the address type, 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 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 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 { struct Campaign {
address beneficiary; address payable beneficiary;
uint fundingGoal; uint fundingGoal;
uint numFunders; uint numFunders;
uint amount; uint amount;
@ -870,7 +883,7 @@ shown in the following example:
uint numCampaigns; uint numCampaigns;
mapping (uint => Campaign) campaigns; 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 campaignID = numCampaigns++; // campaignID is return variable
// Creates new struct and saves in storage. We leave out the mapping type. // Creates new struct and saves in storage. We leave out the mapping type.
campaigns[campaignID] = Campaign(beneficiary, goal, 0, 0); campaigns[campaignID] = Campaign(beneficiary, goal, 0, 0);

View File

@ -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.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.difficulty`` (``uint``): current block difficulty
- ``block.gaslimit`` (``uint``): current block gaslimit - ``block.gaslimit`` (``uint``): current block gaslimit
- ``block.number`` (``uint``): current block number - ``block.number`` (``uint``): current block number
@ -65,12 +65,12 @@ Block and Transaction Properties
- ``gasleft() returns (uint256)``: remaining gas - ``gasleft() returns (uint256)``: remaining gas
- ``msg.data`` (``bytes``): complete calldata - ``msg.data`` (``bytes``): complete calldata
- ``msg.gas`` (``uint``): remaining gas - deprecated in version 0.4.21 and to be replaced by ``gasleft()`` - ``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.sig`` (``bytes4``): first four bytes of the calldata (i.e. function identifier)
- ``msg.value`` (``uint``): number of wei sent with the message - ``msg.value`` (``uint``): number of wei sent with the message
- ``now`` (``uint``): current block timestamp (alias for ``block.timestamp``) - ``now`` (``uint``): current block timestamp (alias for ``block.timestamp``)
- ``tx.gasprice`` (``uint``): gas price of the transaction - ``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:: .. note::
The values of all members of ``msg``, including ``msg.sender`` and The values of all members of ``msg``, including ``msg.sender`` and
@ -161,9 +161,9 @@ Address Related
``<address>.balance`` (``uint256``): ``<address>.balance`` (``uint256``):
balance of the :ref:`address` in Wei 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 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 send given amount of Wei to :ref:`address`, returns ``false`` on failure, forwards 2300 gas stipend, not adjustable
``<address>.call(bytes memory) returns (bool)``: ``<address>.call(bytes memory) returns (bool)``:
issue low-level ``CALL`` with the given payload, returns ``false`` on failure, forwards all available gas, adjustable 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): ``this`` (current contract's type):
the current contract, explicitly convertible to :ref:`address` 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` destroy the current contract, sending its funds to the given :ref:`address`
``suicide(address recipient)``: ``suicide(address payable recipient)``:
deprecated alias to ``selfdestruct`` deprecated alias to ``selfdestruct``
Furthermore, all functions of the current contract are callable directly including the current function. Furthermore, all functions of the current contract are callable directly including the current function.

View File

@ -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(), 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>("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>("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>("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>("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)) make_shared<MagicVariableDeclaration>("tx", make_shared<MagicType>(MagicType::Kind::Transaction))
}) })
{ {

View File

@ -119,11 +119,19 @@ bool ReferencesResolver::visit(ElementaryTypeName const& _typeName)
{ {
// for non-address types this was already caught by the parser // for non-address types this was already caught by the parser
solAssert(_typeName.annotation().type->category() == Type::Category::Address, ""); solAssert(_typeName.annotation().type->category() == Type::Category::Address, "");
if (!( switch(*_typeName.stateMutability())
*_typeName.stateMutability() == StateMutability::Payable || {
*_typeName.stateMutability() == StateMutability::NonPayable case StateMutability::Payable:
)) case StateMutability::NonPayable:
m_errorReporter.typeError(_typeName.location(), "Address types can only be payable or non-payable."); _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; return true;

View File

@ -571,6 +571,9 @@ TypePointers TypeChecker::typeCheckABIDecodeAndRetrieveReturnType(FunctionCall c
// data locations. Furthermore, storage can be a little dangerous and // data locations. Furthermore, storage can be a little dangerous and
// calldata is not really implemented anyway. // calldata is not really implemented anyway.
actualType = ReferenceType::copyForLocationIfReference(DataLocation::Memory, actualType); 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( solAssert(
!actualType->dataStoredIn(DataLocation::CallData) && !actualType->dataStoredIn(DataLocation::CallData) &&
!actualType->dataStoredIn(DataLocation::Storage), !actualType->dataStoredIn(DataLocation::Storage),
@ -1732,14 +1735,39 @@ bool TypeChecker::visit(FunctionCall const& _functionCall)
dataLoc = argRefType->location(); dataLoc = argRefType->location();
resultType = ReferenceType::copyForLocationIfReference(dataLoc, resultType); resultType = ReferenceType::copyForLocationIfReference(dataLoc, resultType);
if (!argType->isExplicitlyConvertibleTo(*resultType)) if (!argType->isExplicitlyConvertibleTo(*resultType))
m_errorReporter.typeError( {
_functionCall.location(), if (resultType->category() == Type::Category::Contract && argType->category() == Type::Category::Address)
"Explicit type conversion not allowed from \"" + {
argType->toString() + solAssert(dynamic_cast<ContractType const*>(resultType.get())->isPayable(), "");
"\" to \"" + solAssert(dynamic_cast<AddressType const*>(argType.get())->stateMutability() < StateMutability::Payable, "");
resultType->toString() + 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().type = resultType;
_functionCall.annotation().isPure = isPure; _functionCall.annotation().isPure = isPure;
@ -2103,7 +2131,7 @@ bool TypeChecker::visit(MemberAccess const& _memberAccess)
"after argument-dependent lookup in " + exprType->toString() + "after argument-dependent lookup in " + exprType->toString() +
(memberName == "value" ? " - did you forget the \"payable\" modifier?" : "."); (memberName == "value" ? " - did you forget the \"payable\" modifier?" : ".");
if (exprType->category() == Type::Category::Contract) 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) if (addressMember.name == memberName)
{ {
Identifier const* var = dynamic_cast<Identifier const*>(&_memberAccess.expression()); Identifier const* var = dynamic_cast<Identifier const*>(&_memberAccess.expression());
@ -2354,7 +2382,7 @@ void TypeChecker::endVisit(Literal const& _literal)
if (_literal.looksLikeAddress()) if (_literal.looksLikeAddress())
{ {
// Assign type here if it even looks like an address. This prevents double errors for invalid addresses // 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; string msg;
if (_literal.value().length() != 42) // "0x" + 40 hex digits if (_literal.value().length() != 42) // "0x" + 40 hex digits

View File

@ -299,7 +299,7 @@ TypePointer Type::fromElementaryTypeName(ElementaryTypeNameToken const& _type)
case Token::Byte: case Token::Byte:
return make_shared<FixedBytesType>(1); return make_shared<FixedBytesType>(1);
case Token::Address: case Token::Address:
return make_shared<AddressType>(); return make_shared<AddressType>(StateMutability::NonPayable);
case Token::Bool: case Token::Bool:
return make_shared<BoolType>(); return make_shared<BoolType>();
case Token::Bytes: case Token::Bytes:
@ -340,6 +340,17 @@ TypePointer Type::fromElementaryTypeName(string const& _name)
} }
return ref->copyForLocation(location, true); 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 else
{ {
solAssert(nameParts.size() == 1, "Storage location suffix only allowed for reference types"); solAssert(nameParts.size() == 1, "Storage location suffix only allowed for reference types");
@ -439,20 +450,47 @@ MemberList::MemberMap Type::boundFunctions(Type const& _type, ContractDefinition
return members; return members;
} }
AddressType::AddressType(StateMutability _stateMutability):
m_stateMutability(_stateMutability)
{
solAssert(m_stateMutability == StateMutability::Payable || m_stateMutability == StateMutability::NonPayable, "");
}
string AddressType::richIdentifier() const 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 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) || return isImplicitlyConvertibleTo(_convertTo) ||
_convertTo.category() == Category::Contract ||
_convertTo.category() == Category::Integer || _convertTo.category() == Category::Integer ||
(_convertTo.category() == Category::FixedBytes && 160 == dynamic_cast<FixedBytesType const&>(_convertTo).numBytes() * 8); (_convertTo.category() == Category::FixedBytes && 160 == dynamic_cast<FixedBytesType const&>(_convertTo).numBytes() * 8);
} }
string AddressType::toString(bool) const string AddressType::toString(bool) const
{
if (m_stateMutability == StateMutability::Payable)
return "address payable";
else
return "address";
}
string AddressType::canonicalName() const
{ {
return "address"; return "address";
} }
@ -479,17 +517,29 @@ TypePointer AddressType::binaryOperatorResult(Token::Value _operator, TypePointe
return Type::commonType(shared_from_this(), _other); return Type::commonType(shared_from_this(), _other);
} }
bool AddressType::operator==(Type const& _other) const
{
if (_other.category() != category())
return false;
AddressType const& other = dynamic_cast<AddressType const&>(_other);
return other.m_stateMutability == m_stateMutability;
}
MemberList::MemberMap AddressType::nativeMembers(ContractDefinition const*) const MemberList::MemberMap AddressType::nativeMembers(ContractDefinition const*) const
{ {
return { MemberList::MemberMap members = {
{"balance", make_shared<IntegerType>(256)}, {"balance", make_shared<IntegerType>(256)},
{"call", make_shared<FunctionType>(strings{"bytes memory"}, strings{"bool", "bytes memory"}, FunctionType::Kind::BareCall, false, StateMutability::Payable)}, {"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)}, {"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)}, {"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)}
{"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)}
}; };
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 namespace
@ -1476,7 +1526,9 @@ bool ContractType::isImplicitlyConvertibleTo(Type const& _convertTo) const
bool ContractType::isExplicitlyConvertibleTo(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 bool ContractType::isPayable() const
@ -3275,7 +3327,7 @@ MemberList::MemberMap MagicType::nativeMembers(ContractDefinition const*) const
{ {
case Kind::Block: case Kind::Block:
return MemberList::MemberMap({ return MemberList::MemberMap({
{"coinbase", make_shared<AddressType>()}, {"coinbase", make_shared<AddressType>(StateMutability::Payable)},
{"timestamp", make_shared<IntegerType>(256)}, {"timestamp", make_shared<IntegerType>(256)},
{"blockhash", make_shared<FunctionType>(strings{"uint"}, strings{"bytes32"}, FunctionType::Kind::BlockHash, false, StateMutability::View)}, {"blockhash", make_shared<FunctionType>(strings{"uint"}, strings{"bytes32"}, FunctionType::Kind::BlockHash, false, StateMutability::View)},
{"difficulty", make_shared<IntegerType>(256)}, {"difficulty", make_shared<IntegerType>(256)},
@ -3284,7 +3336,7 @@ MemberList::MemberMap MagicType::nativeMembers(ContractDefinition const*) const
}); });
case Kind::Message: case Kind::Message:
return MemberList::MemberMap({ return MemberList::MemberMap({
{"sender", make_shared<AddressType>()}, {"sender", make_shared<AddressType>(StateMutability::Payable)},
{"gas", make_shared<IntegerType>(256)}, {"gas", make_shared<IntegerType>(256)},
{"value", make_shared<IntegerType>(256)}, {"value", make_shared<IntegerType>(256)},
{"data", make_shared<ArrayType>(DataLocation::CallData)}, {"data", make_shared<ArrayType>(DataLocation::CallData)},
@ -3292,7 +3344,7 @@ MemberList::MemberMap MagicType::nativeMembers(ContractDefinition const*) const
}); });
case Kind::Transaction: case Kind::Transaction:
return MemberList::MemberMap({ return MemberList::MemberMap({
{"origin", make_shared<AddressType>()}, {"origin", make_shared<AddressType>(StateMutability::Payable)},
{"gasprice", make_shared<IntegerType>(256)} {"gasprice", make_shared<IntegerType>(256)}
}); });
case Kind::ABI: case Kind::ABI:

View File

@ -321,15 +321,16 @@ class AddressType: public Type
public: public:
virtual Category category() const override { return Category::Address; } virtual Category category() const override { return Category::Address; }
explicit AddressType() explicit AddressType(StateMutability _stateMutability);
{
}
virtual std::string richIdentifier() const override; virtual std::string richIdentifier() const override;
virtual bool isImplicitlyConvertibleTo(Type const& _other) const override;
virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const override; virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const override;
virtual TypePointer unaryOperatorResult(Token::Value _operator) const override; virtual TypePointer unaryOperatorResult(Token::Value _operator) const override;
virtual TypePointer binaryOperatorResult(Token::Value _operator, TypePointer const& _other) 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 calldataEncodedSize(bool _padded = true) const override { return _padded ? 32 : 160 / 8; }
virtual unsigned storageBytes() const override { return 160 / 8; } virtual unsigned storageBytes() const override { return 160 / 8; }
virtual bool isValueType() const override { return true; } virtual bool isValueType() const override { return true; }
@ -337,11 +338,17 @@ public:
virtual MemberList::MemberMap nativeMembers(ContractDefinition const*) const override; virtual MemberList::MemberMap nativeMembers(ContractDefinition const*) const override;
virtual std::string toString(bool _short) 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 u256 literalValue(Literal const* _literal) const override;
virtual TypePointer encodingType() const override { return shared_from_this(); } virtual TypePointer encodingType() const override { return shared_from_this(); }
virtual TypePointer interfaceType(bool) 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()) if (isSuper())
return TypePointer{}; return TypePointer{};
return std::make_shared<AddressType>(); return std::make_shared<AddressType>(isPayable() ? StateMutability::Payable : StateMutability::NonPayable);
} }
virtual TypePointer interfaceType(bool _inLibrary) const override virtual TypePointer interfaceType(bool _inLibrary) const override
{ {

View File

@ -242,8 +242,14 @@ string ABIFunctions::cleanupFunction(Type const& _type, bool _revertOnFailure)
break; break;
} }
case Type::Category::Contract: 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; break;
}
case Type::Category::Enum: case Type::Category::Enum:
{ {
size_t members = dynamic_cast<EnumType const&>(_type).numberOfMembers(); size_t members = dynamic_cast<EnumType const&>(_type).numberOfMembers();

View File

@ -1259,7 +1259,7 @@ bool ExpressionCompiler::visit(MemberAccess const& _memberAccess)
identifier = FunctionType(*function).externalIdentifier(); identifier = FunctionType(*function).externalIdentifier();
else else
solAssert(false, "Contract member is neither variable nor function."); 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; m_context << identifier;
} }
else else
@ -1277,15 +1277,24 @@ bool ExpressionCompiler::visit(MemberAccess const& _memberAccess)
{ {
utils().convertType( utils().convertType(
*_memberAccess.expression().annotation().type, *_memberAccess.expression().annotation().type,
AddressType(), AddressType(StateMutability::NonPayable),
true true
); );
m_context << Instruction::BALANCE; 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( utils().convertType(
*_memberAccess.expression().annotation().type, *_memberAccess.expression().annotation().type,
AddressType(), AddressType(StateMutability::Payable),
true
);
}
else if ((set<string>{"call", "callcode", "delegatecall", "staticcall"}).count(member))
utils().convertType(
*_memberAccess.expression().annotation().type,
AddressType(StateMutability::NonPayable),
true true
); );
else else

View File

@ -0,0 +1,6 @@
contract C {
struct S {
address payable a;
address b;
}
}

View File

@ -0,0 +1,7 @@
contract C {
address a;
function f(address b) public pure returns (address c) {
address d = b;
return d;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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);
}
}

View File

@ -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.

View File

@ -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.

View File

@ -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 {
}
}

View File

@ -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.

View File

@ -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.

View File

@ -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;
}
}
// ----

View File

@ -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.

View File

@ -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.

View File

@ -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.

View File

@ -0,0 +1,5 @@
contract C {
function f(address payable a) public {
selfdestruct(a);
}
}

View File

@ -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.

View File

@ -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.

View File

@ -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.

View File

@ -0,0 +1,8 @@
contract C {
function f() public pure returns (C c) {
c = C(address(2));
}
function() external payable {
}
}
// ----

View File

@ -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.

View File

@ -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).

View File

@ -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);
}
}

View File

@ -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.

View File

@ -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.

View File

@ -0,0 +1,9 @@
contract C {
function f() public view {
address payable a = address(this);
a;
}
function() external payable {
}
}
// ----

View File

@ -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.

View File

@ -0,0 +1,8 @@
contract C {
function f() public pure {
address a = address(0);
a = address(1);
address b = 0x0123456789012345678901234567890123456789;
b = 0x9876543210987654321098765432109876543210;
}
}

View File

@ -0,0 +1,8 @@
contract C {
function f() public pure {
address payable a = address(0);
a = address(1);
address payable b = 0x0123456789012345678901234567890123456789;
b = 0x9876543210987654321098765432109876543210;
}
}

View File

@ -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.

View File

@ -0,0 +1,7 @@
contract C {
function f(address payable a) public pure {
address payable b;
address c = a;
c = b;
}
}

View File

@ -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.