mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
commit
cb743d648a
@ -8,6 +8,7 @@ Breaking changes:
|
||||
* Command line interface: Add option to disable or choose hash method between IPFS and Swarm for the bytecode metadata.
|
||||
* General: Disallow explicit conversions from external function types to ``address`` and add a member called ``address`` to them as replacement.
|
||||
* General: New reserved keywords: ``virtual``.
|
||||
* General: Split unnamed fallback functions into two cases defined using ``fallback()`` and ``receive()``.
|
||||
* Standard JSON Interface: Add option to disable or choose hash method between IPFS and Swarm for the bytecode metadata.
|
||||
* Syntax: ``push(element)`` for dynamic storage arrays do not return the new length anymore.
|
||||
* Syntax: Abstract contracts need to be marked explicitly as abstract by using the ``abstract`` keyword.
|
||||
|
@ -437,7 +437,7 @@ New version:
|
||||
function f(uint y) external {
|
||||
x = y;
|
||||
}
|
||||
function() payable external {}
|
||||
receive() payable external {}
|
||||
}
|
||||
|
||||
contract New {
|
||||
|
@ -39,6 +39,15 @@ This section highlights changes that affect syntax and semantics.
|
||||
that can hold both the type of the base and the type of the exponent, as with symmentric
|
||||
operations. Additionally, signed types are allowed for the base of the exponetation.
|
||||
|
||||
* The unnamed function commonly referred to as "fallback function" was split up into a new
|
||||
fallback function that is defined using the ``fallback`` keyword and a receive ether function
|
||||
defined using the ``receive`` keyword. If present, the receive ether function is called
|
||||
whenever the call data is empty. The new fallback function is called when no
|
||||
other function matches. It can be payable in which case it may accept value
|
||||
or non-payable in which case transactions not matching any other function
|
||||
which send value will revert. If you only implement the receive and not the fallback function, calling a non-existing function on your contract in error is not possible anymore. Unless you are following an upgrade or proxy
|
||||
pattern, you should not need to implement the fallback function.
|
||||
|
||||
|
||||
How to update your code
|
||||
=======================
|
||||
@ -47,6 +56,8 @@ This section gives detailed instructions on how to update prior code for every b
|
||||
|
||||
* Change ``address(f)`` to ``f.address`` for ``f`` being of external function type.
|
||||
|
||||
* Replace ``function () external [payable] { ... }`` by either ``receive() external payable { ... }``, ``fallback() external [payable] { ... }`` or both. Prefer using a ``receive`` function only, whenever possible.
|
||||
|
||||
* Change ``uint length = array.push(value)`` to ``array.push(value);``. The new length can be
|
||||
accessed via ``array.length``.
|
||||
|
||||
|
@ -444,8 +444,8 @@ JSON
|
||||
The JSON format for a contract's interface is given by an array of function and/or event descriptions.
|
||||
A function description is a JSON object with the fields:
|
||||
|
||||
- ``type``: ``"function"``, ``"constructor"``, or ``"fallback"`` (the :ref:`unnamed "default" function <fallback-function>`).
|
||||
- ``name``: the name of the function.
|
||||
- ``type``: ``"function"``, ``"constructor"``, ``"receive"`` (the :ref:`"receive Ether" function <receive-ether-function>`) or ``"fallback"`` (the :ref:`"default" function <fallback-function>`);
|
||||
- ``name``: the name of the function;
|
||||
- ``inputs``: an array of objects, each of which contains:
|
||||
|
||||
* ``name``: the name of the parameter.
|
||||
|
@ -222,23 +222,23 @@ This behaviour is also in line with the ``STATICCALL`` opcode.
|
||||
not do state-changing operations, but it cannot check that the contract that will be called
|
||||
at runtime is actually of that type.
|
||||
|
||||
.. index:: ! fallback function, function;fallback
|
||||
.. index:: ! receive ether function, function;receive ! receive
|
||||
|
||||
.. _fallback-function:
|
||||
.. _receive-ether-function:
|
||||
|
||||
Fallback Function
|
||||
=================
|
||||
Receive Ether Function
|
||||
======================
|
||||
|
||||
A contract can have exactly one unnamed function. This function cannot have
|
||||
arguments, cannot return anything and has to have ``external`` visibility.
|
||||
It is executed on a call to the contract if none of the other
|
||||
functions match the given function identifier (or if no data was supplied at
|
||||
all).
|
||||
|
||||
Furthermore, this function is executed whenever the contract receives plain
|
||||
Ether (without data). To receive Ether and add it to the total balance of the contract, the fallback function
|
||||
must be marked ``payable``. If no such function exists, the contract cannot receive
|
||||
Ether through regular transactions and throws an exception.
|
||||
A contract can have at most one ``receive`` function, declared using ``receive() external payable``.
|
||||
This function cannot have arguments, cannot return anything and must have
|
||||
``external`` visibility and ``payable`` state mutability. It is executed on a
|
||||
call to the contract with empty calldata. This is the function that is executed
|
||||
on plain Ether transfers (e.g. via `.send()` or `.transfer()`). If no such
|
||||
function exists, but a payable :ref:`fallback function <fallback-function>`
|
||||
exists, the fallback function will be called on a plain Ether transfer. If
|
||||
neither a receive Ether nor a payable fallback function is present, the
|
||||
contract cannot receive Ether through regular transactions and throws an
|
||||
exception.
|
||||
|
||||
In the worst case, the fallback function can only rely on 2300 gas being
|
||||
available (for example when `send` or `transfer` is used), leaving little
|
||||
@ -250,33 +250,64 @@ will consume more gas than the 2300 gas stipend:
|
||||
- Calling an external function which consumes a large amount of gas
|
||||
- Sending Ether
|
||||
|
||||
.. warning::
|
||||
Contracts that receive Ether directly (without a function call, i.e. using ``send`` or ``transfer``)
|
||||
but do not define a receive Ether function or a payable fallback function
|
||||
throw an exception, sending back the Ether (this was different
|
||||
before Solidity v0.4.0). So if you want your contract to receive Ether,
|
||||
you have to implement a receive Ether function (using payable fallback functions for receiving Ether is
|
||||
not recommended, since it would not fail on interface confusions).
|
||||
|
||||
|
||||
.. warning::
|
||||
A contract without a receive Ether function can receive Ether as a recipient of a `coinbase transaction` (aka `miner block reward`)
|
||||
or as a destination of a ``selfdestruct``.
|
||||
|
||||
A contract cannot react to such Ether transfers and thus also cannot reject them. This is a design choice of the EVM and Solidity cannot work around it.
|
||||
|
||||
It also means that ``address(this).balance`` can be higher than the sum of some manual accounting implemented in a contract (i.e. having a counter updated in the receive Ether function).
|
||||
|
||||
Below you can see an example of a Sink contract that uses function ``receive``.
|
||||
|
||||
::
|
||||
|
||||
pragma solidity >=0.5.0 <0.7.0;
|
||||
|
||||
// This contract keeps all Ether sent to it with no way
|
||||
// to get it back.
|
||||
contract Sink {
|
||||
receive() external payable { }
|
||||
}
|
||||
|
||||
.. index:: ! fallback function, function;fallback
|
||||
|
||||
.. _fallback-function:
|
||||
|
||||
Fallback Function
|
||||
=================
|
||||
|
||||
A contract can have at most one ``fallback`` function, declared using ``fallback () external [payable]``.
|
||||
This function cannot have arguments, cannot return anything and must have ``external`` visibility.
|
||||
It is executed on a call to the contract if none of the other
|
||||
functions match the given function signature, or if no data was supplied at
|
||||
all and there is no :ref:`receive Ether function <receive-ether-function>`.
|
||||
The fallback function always receives data, but in order to also receive Ether
|
||||
it must be marked ``payable``.
|
||||
|
||||
In the worst case, if a payable fallback function is also used in place of a receive function, it can only rely on 2300 gas being
|
||||
available (see :ref:`receive Ether function <receive-ether-function>` for a brief description of the implications of this).
|
||||
|
||||
Like any function, the fallback function can execute complex operations as long as there is enough gas passed on to it.
|
||||
|
||||
.. warning::
|
||||
The fallback function is also executed if the caller meant to call
|
||||
a function that is not available. If you want to implement the fallback
|
||||
function only to receive ether, you should add a check
|
||||
like ``require(msg.data.length == 0)`` to prevent invalid calls.
|
||||
|
||||
.. warning::
|
||||
Contracts that receive Ether directly (without a function call, i.e. using ``send`` or ``transfer``)
|
||||
but do not define a fallback function
|
||||
throw an exception, sending back the Ether (this was different
|
||||
before Solidity v0.4.0). So if you want your contract to receive Ether,
|
||||
you have to implement a payable fallback function.
|
||||
|
||||
.. warning::
|
||||
A contract without a payable fallback function can receive Ether as a recipient of a `coinbase transaction` (aka `miner block reward`)
|
||||
or as a destination of a ``selfdestruct``.
|
||||
A ``payable`` fallback function is also executed for plain Ether transfers, if no :ref:`receive Ether function <receive-ether-function>`
|
||||
is present. It is recommended to always define a receive Ether function as well, if you define a payable fallback function
|
||||
to distinguish Ether transfers from interface confusions.
|
||||
|
||||
.. note::
|
||||
Even though the fallback function cannot have arguments, one can still use ``msg.data`` to retrieve
|
||||
any payload supplied with the call.
|
||||
|
||||
A contract cannot react to such Ether transfers and thus also cannot reject them. This is a design choice of the EVM and Solidity cannot work around it.
|
||||
|
||||
It also means that ``address(this).balance`` can be higher than the sum of some manual accounting implemented in a contract (i.e. having a counter updated in the fallback function).
|
||||
|
||||
::
|
||||
|
||||
pragma solidity >=0.5.0 <0.7.0;
|
||||
@ -287,15 +318,23 @@ Like any function, the fallback function can execute complex operations as long
|
||||
// Sending Ether to this contract will cause an exception,
|
||||
// because the fallback function does not have the `payable`
|
||||
// modifier.
|
||||
function() external { x = 1; }
|
||||
fallback() external { x = 1; }
|
||||
uint x;
|
||||
}
|
||||
|
||||
contract TestPayable {
|
||||
// This function is called for all messages sent to
|
||||
// this contract, except plain Ether transfers
|
||||
// (there is no other function except the receive function).
|
||||
// Any call with non-empty calldata to this contract will execute
|
||||
// the fallback function (even if Ether is sent along with the call).
|
||||
fallback() external payable { x = 1; y = msg.value; }
|
||||
|
||||
// This contract keeps all Ether sent to it with no way
|
||||
// to get it back.
|
||||
contract Sink {
|
||||
function() external payable { }
|
||||
// This function is called for plain Ether transfers, i.e.
|
||||
// for every call with empty calldata.
|
||||
receive() external payable { x = 2; y = msg.value; }
|
||||
uint x;
|
||||
uint y;
|
||||
}
|
||||
|
||||
contract Caller {
|
||||
@ -305,14 +344,27 @@ Like any function, the fallback function can execute complex operations as long
|
||||
// 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)));
|
||||
// fallback function.
|
||||
// It has to be converted to the ``address payable`` type to even allow calling ``send`` on it.
|
||||
address payable testPayable = payable(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.
|
||||
return testPayable.send(2 ether);
|
||||
}
|
||||
|
||||
function callTestPayable(TestPayable test) public returns (bool) {
|
||||
(bool success,) = address(test).call(abi.encodeWithSignature("nonExistingFunction()"));
|
||||
require(success);
|
||||
// results in test.x becoming == 1 and test.y becoming 0.
|
||||
(success,) = address(test).call.value(1)(abi.encodeWithSignature("nonExistingFunction()"));
|
||||
require(success);
|
||||
// results in test.x becoming == 1 and test.y becoming 1.
|
||||
|
||||
// If someone sends Ether to that contract, the receive function in TestPayable will be called.
|
||||
require(address(test).send(2 ether));
|
||||
// results in test.x becoming == 2 and test.y becoming 2 ether.
|
||||
}
|
||||
}
|
||||
|
||||
.. index:: ! overload
|
||||
|
@ -224,7 +224,7 @@ Now someone tricks you into sending ether to the address of this attack wallet:
|
||||
owner = msg.sender;
|
||||
}
|
||||
|
||||
function() external {
|
||||
fallback() external {
|
||||
TxUserWallet(msg.sender).transferTo(owner, msg.sender.balance);
|
||||
}
|
||||
}
|
||||
|
@ -273,6 +273,7 @@ Ordering helps readers identify which functions they can call and to find the co
|
||||
Functions should be grouped according to their visibility and ordered:
|
||||
|
||||
- constructor
|
||||
- receive function (if exists)
|
||||
- fallback function (if exists)
|
||||
- external
|
||||
- public
|
||||
@ -290,7 +291,11 @@ Yes::
|
||||
// ...
|
||||
}
|
||||
|
||||
function() external {
|
||||
receive() external payable {
|
||||
// ...
|
||||
}
|
||||
|
||||
fallback() external {
|
||||
// ...
|
||||
}
|
||||
|
||||
@ -322,7 +327,10 @@ No::
|
||||
// External functions
|
||||
// ...
|
||||
|
||||
function() external {
|
||||
fallback() external {
|
||||
// ...
|
||||
}
|
||||
receive() external payable {
|
||||
// ...
|
||||
}
|
||||
|
||||
@ -384,20 +392,29 @@ No::
|
||||
y = 2;
|
||||
long_variable = 3;
|
||||
|
||||
Don't include a whitespace in the fallback function:
|
||||
Don't include a whitespace in the receive and fallback functions:
|
||||
|
||||
Yes::
|
||||
|
||||
function() external {
|
||||
receive() external payable {
|
||||
...
|
||||
}
|
||||
|
||||
fallback() external {
|
||||
...
|
||||
}
|
||||
|
||||
No::
|
||||
|
||||
function () external {
|
||||
receive () external payable {
|
||||
...
|
||||
}
|
||||
|
||||
fallback () external {
|
||||
...
|
||||
}
|
||||
|
||||
|
||||
Control Structures
|
||||
==================
|
||||
|
||||
|
@ -232,7 +232,7 @@ or if the Ether transfer is rejected by the receiving account. The ``transfer``
|
||||
reverts on failure.
|
||||
|
||||
.. note::
|
||||
If ``x`` is a contract address, its code (more specifically: its :ref:`fallback-function`, if present) will be executed together with the ``transfer`` call (this is a feature of the EVM and cannot be prevented). If that execution runs out of gas or fails in any way, the Ether transfer will be reverted and the current contract will stop with an exception.
|
||||
If ``x`` is a contract address, its code (more specifically: its :ref:`receive-ether-function`, if present, or otherwise its :ref:`fallback-function`, if present) will be executed together with the ``transfer`` call (this is a feature of the EVM and cannot be prevented). If that execution runs out of gas or fails in any way, the Ether transfer will be reverted and the current contract will stop with an exception.
|
||||
|
||||
* ``send``
|
||||
|
||||
|
@ -157,6 +157,7 @@ namespace langutil
|
||||
K(Emit, "emit", 0) \
|
||||
K(Event, "event", 0) \
|
||||
K(External, "external", 0) \
|
||||
K(Fallback, "fallback", 0) \
|
||||
K(For, "for", 0) \
|
||||
K(Function, "function", 0) \
|
||||
K(Hex, "hex", 0) \
|
||||
@ -177,6 +178,7 @@ namespace langutil
|
||||
K(Pragma, "pragma", 0) \
|
||||
K(Private, "private", 0) \
|
||||
K(Pure, "pure", 0) \
|
||||
K(Receive, "receive", 0) \
|
||||
K(Return, "return", 0) \
|
||||
K(Returns, "returns", 0) \
|
||||
K(Storage, "storage", 0) \
|
||||
|
@ -107,6 +107,9 @@ bool ContractLevelChecker::LessFunction::operator()(FunctionDefinition const* _a
|
||||
if (_a->name() != _b->name())
|
||||
return _a->name() < _b->name();
|
||||
|
||||
if (_a->kind() != _b->kind())
|
||||
return _a->kind() < _b->kind();
|
||||
|
||||
return boost::lexicographical_compare(
|
||||
FunctionType(*_a).asCallableFunction(false)->parameterTypes(),
|
||||
FunctionType(*_b).asCallableFunction(false)->parameterTypes(),
|
||||
@ -133,12 +136,11 @@ bool ContractLevelChecker::check(ContractDefinition const& _contract)
|
||||
checkAmbiguousOverrides(_contract);
|
||||
checkAbstractFunctions(_contract);
|
||||
checkBaseConstructorArguments(_contract);
|
||||
checkConstructor(_contract);
|
||||
checkFallbackFunction(_contract);
|
||||
checkExternalTypeClashes(_contract);
|
||||
checkHashCollisions(_contract);
|
||||
checkLibraryRequirements(_contract);
|
||||
checkBaseABICompatibility(_contract);
|
||||
checkPayableFallbackWithoutReceive(_contract);
|
||||
|
||||
return Error::containsOnlyWarnings(m_errorReporter.errors());
|
||||
}
|
||||
@ -150,6 +152,7 @@ void ContractLevelChecker::checkDuplicateFunctions(ContractDefinition const& _co
|
||||
map<string, vector<FunctionDefinition const*>> functions;
|
||||
FunctionDefinition const* constructor = nullptr;
|
||||
FunctionDefinition const* fallback = nullptr;
|
||||
FunctionDefinition const* receive = nullptr;
|
||||
for (FunctionDefinition const* function: _contract.definedFunctions())
|
||||
if (function->isConstructor())
|
||||
{
|
||||
@ -171,6 +174,16 @@ void ContractLevelChecker::checkDuplicateFunctions(ContractDefinition const& _co
|
||||
);
|
||||
fallback = function;
|
||||
}
|
||||
else if (function->isReceive())
|
||||
{
|
||||
if (receive)
|
||||
m_errorReporter.declarationError(
|
||||
function->location(),
|
||||
SecondarySourceLocation().append("Another declaration is here:", receive->location()),
|
||||
"Only one receive function is allowed."
|
||||
);
|
||||
receive = function;
|
||||
}
|
||||
else
|
||||
{
|
||||
solAssert(!function->name().empty(), "");
|
||||
@ -496,48 +509,6 @@ void ContractLevelChecker::annotateBaseConstructorArguments(
|
||||
|
||||
}
|
||||
|
||||
void ContractLevelChecker::checkConstructor(ContractDefinition const& _contract)
|
||||
{
|
||||
FunctionDefinition const* constructor = _contract.constructor();
|
||||
if (!constructor)
|
||||
return;
|
||||
|
||||
if (!constructor->returnParameters().empty())
|
||||
m_errorReporter.typeError(constructor->returnParameterList()->location(), "Non-empty \"returns\" directive for constructor.");
|
||||
if (constructor->stateMutability() != StateMutability::NonPayable && constructor->stateMutability() != StateMutability::Payable)
|
||||
m_errorReporter.typeError(
|
||||
constructor->location(),
|
||||
"Constructor must be payable or non-payable, but is \"" +
|
||||
stateMutabilityToString(constructor->stateMutability()) +
|
||||
"\"."
|
||||
);
|
||||
if (constructor->visibility() != FunctionDefinition::Visibility::Public && constructor->visibility() != FunctionDefinition::Visibility::Internal)
|
||||
m_errorReporter.typeError(constructor->location(), "Constructor must be public or internal.");
|
||||
}
|
||||
|
||||
void ContractLevelChecker::checkFallbackFunction(ContractDefinition const& _contract)
|
||||
{
|
||||
FunctionDefinition const* fallback = _contract.fallbackFunction();
|
||||
if (!fallback)
|
||||
return;
|
||||
|
||||
if (_contract.isLibrary())
|
||||
m_errorReporter.typeError(fallback->location(), "Libraries cannot have fallback functions.");
|
||||
if (fallback->stateMutability() != StateMutability::NonPayable && fallback->stateMutability() != StateMutability::Payable)
|
||||
m_errorReporter.typeError(
|
||||
fallback->location(),
|
||||
"Fallback function must be payable or non-payable, but is \"" +
|
||||
stateMutabilityToString(fallback->stateMutability()) +
|
||||
"\"."
|
||||
);
|
||||
if (!fallback->parameters().empty())
|
||||
m_errorReporter.typeError(fallback->parameterList().location(), "Fallback function cannot take parameters.");
|
||||
if (!fallback->returnParameters().empty())
|
||||
m_errorReporter.typeError(fallback->returnParameterList()->location(), "Fallback function cannot return values.");
|
||||
if (fallback->visibility() != FunctionDefinition::Visibility::External)
|
||||
m_errorReporter.typeError(fallback->location(), "Fallback function must be defined as \"external\".");
|
||||
}
|
||||
|
||||
void ContractLevelChecker::checkExternalTypeClashes(ContractDefinition const& _contract)
|
||||
{
|
||||
map<string, vector<pair<Declaration const*, FunctionTypePointer>>> externalDeclarations;
|
||||
@ -876,3 +847,14 @@ ContractLevelChecker::ModifierMultiSet const& ContractLevelChecker::inheritedMod
|
||||
|
||||
return m_contractBaseModifiers[_contract] = set;
|
||||
}
|
||||
|
||||
void ContractLevelChecker::checkPayableFallbackWithoutReceive(ContractDefinition const& _contract)
|
||||
{
|
||||
if (auto const* fallback = _contract.fallbackFunction())
|
||||
if (fallback->isPayable() && !_contract.interfaceFunctionList().empty() && !_contract.receiveFunction())
|
||||
m_errorReporter.warning(
|
||||
_contract.location(),
|
||||
"This contract has a payable fallback function, but no receive ether function. Consider adding a receive ether function.",
|
||||
SecondarySourceLocation{}.append("The payable fallback function is defined here.", fallback->location())
|
||||
);
|
||||
}
|
||||
|
@ -83,8 +83,6 @@ private:
|
||||
FunctionDefinition const* _baseConstructor,
|
||||
ASTNode const* _argumentNode
|
||||
);
|
||||
void checkConstructor(ContractDefinition const& _contract);
|
||||
void checkFallbackFunction(ContractDefinition const& _contract);
|
||||
/// Checks that different functions with external visibility end up having different
|
||||
/// external argument types (i.e. different signature).
|
||||
void checkExternalTypeClashes(ContractDefinition const& _contract);
|
||||
@ -108,6 +106,9 @@ private:
|
||||
FunctionMultiSet const& inheritedFunctions(ContractDefinition const* _contract) const;
|
||||
ModifierMultiSet const& inheritedModifiers(ContractDefinition const* _contract) const;
|
||||
|
||||
/// Warns if the contract has a payable fallback, but no receive ether function.
|
||||
void checkPayableFallbackWithoutReceive(ContractDefinition const& _contract);
|
||||
|
||||
langutil::ErrorReporter& m_errorReporter;
|
||||
|
||||
/// Cache for inheritedFunctions().
|
||||
|
@ -295,7 +295,7 @@ bool SyntaxChecker::visit(FunctionDefinition const& _function)
|
||||
{
|
||||
if (_function.noVisibilitySpecified())
|
||||
{
|
||||
string suggestedVisibility = _function.isFallback() || m_isInterface ? "external" : "public";
|
||||
string suggestedVisibility = _function.isFallback() || _function.isReceive() || m_isInterface ? "external" : "public";
|
||||
m_errorReporter.syntaxError(
|
||||
_function.location(),
|
||||
"No visibility specified. Did you intend to add \"" + suggestedVisibility + "\"?"
|
||||
|
@ -326,11 +326,12 @@ bool TypeChecker::visit(StructDefinition const& _struct)
|
||||
bool TypeChecker::visit(FunctionDefinition const& _function)
|
||||
{
|
||||
bool isLibraryFunction = _function.inContractKind() == ContractDefinition::ContractKind::Library;
|
||||
|
||||
if (_function.isPayable())
|
||||
{
|
||||
if (isLibraryFunction)
|
||||
m_errorReporter.typeError(_function.location(), "Library functions cannot be payable.");
|
||||
if (!_function.isConstructor() && !_function.isFallback() && !_function.isPartOfExternalInterface())
|
||||
if (_function.isOrdinary() && !_function.isPartOfExternalInterface())
|
||||
m_errorReporter.typeError(_function.location(), "Internal functions cannot be payable.");
|
||||
}
|
||||
auto checkArgumentAndReturnParameter = [&](VariableDeclaration const& var) {
|
||||
@ -424,6 +425,15 @@ bool TypeChecker::visit(FunctionDefinition const& _function)
|
||||
m_errorReporter.typeError(_function.location(), "Constructor must be implemented if declared.");
|
||||
else if (isLibraryFunction && _function.visibility() <= FunctionDefinition::Visibility::Internal)
|
||||
m_errorReporter.typeError(_function.location(), "Internal library function must be implemented if declared.");
|
||||
|
||||
|
||||
if (_function.isFallback())
|
||||
typeCheckFallbackFunction(_function);
|
||||
else if (_function.isReceive())
|
||||
typeCheckReceiveFunction(_function);
|
||||
else if (_function.isConstructor())
|
||||
typeCheckConstructor(_function);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -1703,6 +1713,71 @@ void TypeChecker::typeCheckFunctionCall(
|
||||
typeCheckFunctionGeneralChecks(_functionCall, _functionType);
|
||||
}
|
||||
|
||||
void TypeChecker::typeCheckFallbackFunction(FunctionDefinition const& _function)
|
||||
{
|
||||
solAssert(_function.isFallback(), "");
|
||||
|
||||
if (_function.inContractKind() == ContractDefinition::ContractKind::Library)
|
||||
m_errorReporter.typeError(_function.location(), "Libraries cannot have fallback functions.");
|
||||
if (_function.stateMutability() != StateMutability::NonPayable && _function.stateMutability() != StateMutability::Payable)
|
||||
m_errorReporter.typeError(
|
||||
_function.location(),
|
||||
"Fallback function must be payable or non-payable, but is \"" +
|
||||
stateMutabilityToString(_function.stateMutability()) +
|
||||
"\"."
|
||||
);
|
||||
if (_function.visibility() != FunctionDefinition::Visibility::External)
|
||||
m_errorReporter.typeError(_function.location(), "Fallback function must be defined as \"external\".");
|
||||
if (!_function.returnParameters().empty())
|
||||
{
|
||||
if (_function.returnParameters().size() > 1 || *type(*_function.returnParameters().front()) != *TypeProvider::bytesMemory())
|
||||
m_errorReporter.typeError(_function.returnParameterList()->location(), "Fallback function can only have a single \"bytes memory\" return value.");
|
||||
else
|
||||
m_errorReporter.typeError(_function.returnParameterList()->location(), "Return values for fallback functions are not yet implemented.");
|
||||
}
|
||||
if (!_function.parameters().empty())
|
||||
m_errorReporter.typeError(_function.parameterList().location(), "Fallback function cannot take parameters.");
|
||||
}
|
||||
|
||||
void TypeChecker::typeCheckReceiveFunction(FunctionDefinition const& _function)
|
||||
{
|
||||
solAssert(_function.isReceive(), "");
|
||||
|
||||
if (_function.inContractKind() == ContractDefinition::ContractKind::Library)
|
||||
m_errorReporter.typeError(_function.location(), "Libraries cannot have receive ether functions.");
|
||||
|
||||
if (_function.stateMutability() != StateMutability::Payable)
|
||||
m_errorReporter.typeError(
|
||||
_function.location(),
|
||||
"Receive ether function must be payable, but is \"" +
|
||||
stateMutabilityToString(_function.stateMutability()) +
|
||||
"\"."
|
||||
);
|
||||
if (_function.visibility() != FunctionDefinition::Visibility::External)
|
||||
m_errorReporter.typeError(_function.location(), "Receive ether function must be defined as \"external\".");
|
||||
if (!_function.returnParameters().empty())
|
||||
m_errorReporter.typeError(_function.returnParameterList()->location(), "Receive ether function cannot return values.");
|
||||
if (!_function.parameters().empty())
|
||||
m_errorReporter.typeError(_function.parameterList().location(), "Receive ether function cannot take parameters.");
|
||||
}
|
||||
|
||||
|
||||
void TypeChecker::typeCheckConstructor(FunctionDefinition const& _function)
|
||||
{
|
||||
solAssert(_function.isConstructor(), "");
|
||||
if (!_function.returnParameters().empty())
|
||||
m_errorReporter.typeError(_function.returnParameterList()->location(), "Non-empty \"returns\" directive for constructor.");
|
||||
if (_function.stateMutability() != StateMutability::NonPayable && _function.stateMutability() != StateMutability::Payable)
|
||||
m_errorReporter.typeError(
|
||||
_function.location(),
|
||||
"Constructor must be payable or non-payable, but is \"" +
|
||||
stateMutabilityToString(_function.stateMutability()) +
|
||||
"\"."
|
||||
);
|
||||
if (_function.visibility() != FunctionDefinition::Visibility::Public && _function.visibility() != FunctionDefinition::Visibility::Internal)
|
||||
m_errorReporter.typeError(_function.location(), "Constructor must be public or internal.");
|
||||
}
|
||||
|
||||
void TypeChecker::typeCheckABIEncodeFunctions(
|
||||
FunctionCall const& _functionCall,
|
||||
FunctionTypePointer _functionType
|
||||
|
@ -96,6 +96,10 @@ private:
|
||||
FunctionTypePointer _functionType
|
||||
);
|
||||
|
||||
void typeCheckFallbackFunction(FunctionDefinition const& _function);
|
||||
void typeCheckReceiveFunction(FunctionDefinition const& _function);
|
||||
void typeCheckConstructor(FunctionDefinition const& _function);
|
||||
|
||||
/// Performs general number and type checks of arguments against function call and struct ctor FunctionCall node parameters.
|
||||
void typeCheckFunctionGeneralChecks(
|
||||
FunctionCall const& _functionCall,
|
||||
|
@ -174,6 +174,7 @@ void ViewPureChecker::endVisit(FunctionDefinition const& _funDef)
|
||||
!_funDef.body().statements().empty() &&
|
||||
!_funDef.isConstructor() &&
|
||||
!_funDef.isFallback() &&
|
||||
!_funDef.isReceive() &&
|
||||
!_funDef.annotation().superFunction
|
||||
)
|
||||
m_errorReporter.warning(
|
||||
|
@ -163,6 +163,15 @@ FunctionDefinition const* ContractDefinition::fallbackFunction() const
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
FunctionDefinition const* ContractDefinition::receiveFunction() const
|
||||
{
|
||||
for (ContractDefinition const* contract: annotation().linearizedBaseContracts)
|
||||
for (FunctionDefinition const* f: contract->definedFunctions())
|
||||
if (f->isReceive())
|
||||
return f;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
vector<EventDefinition const*> const& ContractDefinition::interfaceEvents() const
|
||||
{
|
||||
if (!m_interfaceEvents)
|
||||
|
@ -435,6 +435,9 @@ public:
|
||||
/// Returns the fallback function or nullptr if no fallback function was specified.
|
||||
FunctionDefinition const* fallbackFunction() const;
|
||||
|
||||
/// Returns the ether receiver function or nullptr if no receive function was specified.
|
||||
FunctionDefinition const* receiveFunction() const;
|
||||
|
||||
std::string fullyQualifiedName() const { return sourceUnitName() + ":" + name(); }
|
||||
|
||||
TypePointer type() const override;
|
||||
@ -657,7 +660,7 @@ public:
|
||||
ASTPointer<ASTString> const& _name,
|
||||
Declaration::Visibility _visibility,
|
||||
StateMutability _stateMutability,
|
||||
bool _isConstructor,
|
||||
Token _kind,
|
||||
ASTPointer<OverrideSpecifier> const& _overrides,
|
||||
ASTPointer<ASTString> const& _documentation,
|
||||
ASTPointer<ParameterList> const& _parameters,
|
||||
@ -669,26 +672,31 @@ public:
|
||||
Documented(_documentation),
|
||||
ImplementationOptional(_body != nullptr),
|
||||
m_stateMutability(_stateMutability),
|
||||
m_isConstructor(_isConstructor),
|
||||
m_kind(_kind),
|
||||
m_functionModifiers(_modifiers),
|
||||
m_body(_body)
|
||||
{}
|
||||
{
|
||||
solAssert(_kind == Token::Constructor || _kind == Token::Function || _kind == Token::Fallback || _kind == Token::Receive, "");
|
||||
}
|
||||
|
||||
void accept(ASTVisitor& _visitor) override;
|
||||
void accept(ASTConstVisitor& _visitor) const override;
|
||||
|
||||
StateMutability stateMutability() const { return m_stateMutability; }
|
||||
bool isConstructor() const { return m_isConstructor; }
|
||||
bool isFallback() const { return !m_isConstructor && name().empty(); }
|
||||
bool isOverridable() const { return !isFallback() && !isConstructor(); }
|
||||
bool isOrdinary() const { return m_kind == Token::Function; }
|
||||
bool isConstructor() const { return m_kind == Token::Constructor; }
|
||||
bool isFallback() const { return m_kind == Token::Fallback; }
|
||||
bool isReceive() const { return m_kind == Token::Receive; }
|
||||
Token kind() const { return m_kind; }
|
||||
bool isOverridable() const { return !isConstructor(); }
|
||||
bool isPayable() const { return m_stateMutability == StateMutability::Payable; }
|
||||
std::vector<ASTPointer<ModifierInvocation>> const& modifiers() const { return m_functionModifiers; }
|
||||
Block const& body() const { solAssert(m_body, ""); return *m_body; }
|
||||
bool isVisibleInContract() const override
|
||||
{
|
||||
return Declaration::isVisibleInContract() && !isConstructor() && !isFallback();
|
||||
return Declaration::isVisibleInContract() && isOrdinary();
|
||||
}
|
||||
bool isPartOfExternalInterface() const override { return isPublic() && !isConstructor() && !isFallback(); }
|
||||
bool isPartOfExternalInterface() const override { return isPublic() && isOrdinary(); }
|
||||
|
||||
/// @returns the external signature of the function
|
||||
/// That consists of the name of the function followed by the types of the
|
||||
@ -707,7 +715,7 @@ public:
|
||||
|
||||
private:
|
||||
StateMutability m_stateMutability;
|
||||
bool m_isConstructor;
|
||||
Token const m_kind;
|
||||
std::vector<ASTPointer<ModifierInvocation>> m_functionModifiers;
|
||||
ASTPointer<Block> m_body;
|
||||
};
|
||||
|
@ -332,7 +332,7 @@ bool ASTJsonConverter::visit(FunctionDefinition const& _node)
|
||||
std::vector<pair<string, Json::Value>> attributes = {
|
||||
make_pair("name", _node.name()),
|
||||
make_pair("documentation", _node.documentation() ? Json::Value(*_node.documentation()) : Json::nullValue),
|
||||
make_pair("kind", _node.isConstructor() ? "constructor" : (_node.isFallback() ? "fallback" : "function")),
|
||||
make_pair("kind", TokenTraits::toString(_node.kind())),
|
||||
make_pair("stateMutability", stateMutabilityToString(_node.stateMutability())),
|
||||
make_pair("superFunction", idOrNull(_node.annotation().superFunction)),
|
||||
make_pair("visibility", Declaration::visibilityToString(_node.visibility())),
|
||||
|
@ -1462,8 +1462,9 @@ BoolResult ContractType::isExplicitlyConvertibleTo(Type const& _convertTo) const
|
||||
|
||||
bool ContractType::isPayable() const
|
||||
{
|
||||
auto receiveFunction = m_contract.receiveFunction();
|
||||
auto fallbackFunction = m_contract.fallbackFunction();
|
||||
return fallbackFunction && fallbackFunction->isPayable();
|
||||
return receiveFunction || (fallbackFunction && fallbackFunction->isPayable());
|
||||
}
|
||||
|
||||
TypeResult ContractType::unaryOperatorResult(Token _operator) const
|
||||
|
@ -846,7 +846,8 @@ public:
|
||||
/// See documentation of m_super
|
||||
bool isSuper() const { return m_super; }
|
||||
|
||||
// @returns true if and only if the contract has a payable fallback function
|
||||
// @returns true if and only if the contract has a receive ether function or a payable fallback function, i.e.
|
||||
// if it has code that will be executed on plain ether transfers
|
||||
bool isPayable() const;
|
||||
|
||||
ContractDefinition const& contractDefinition() const { return m_contract; }
|
||||
|
@ -336,6 +336,9 @@ namespace
|
||||
// Helper function to check if any function is payable
|
||||
bool hasPayableFunctions(ContractDefinition const& _contract)
|
||||
{
|
||||
if (_contract.receiveFunction())
|
||||
return true;
|
||||
|
||||
FunctionDefinition const* fallback = _contract.fallbackFunction();
|
||||
if (fallback && fallback->isPayable())
|
||||
return true;
|
||||
@ -362,6 +365,9 @@ void ContractCompiler::appendFunctionSelector(ContractDefinition const& _contrac
|
||||
FunctionDefinition const* fallback = _contract.fallbackFunction();
|
||||
solAssert(!_contract.isLibrary() || !fallback, "Libraries can't have fallback functions");
|
||||
|
||||
FunctionDefinition const* etherReceiver = _contract.receiveFunction();
|
||||
solAssert(!_contract.isLibrary() || !fallback, "Libraries can't have ether receiver functions");
|
||||
|
||||
bool needToAddCallvalueCheck = true;
|
||||
if (!hasPayableFunctions(_contract) && !interfaceFunctions.empty() && !_contract.isLibrary())
|
||||
{
|
||||
@ -369,11 +375,15 @@ void ContractCompiler::appendFunctionSelector(ContractDefinition const& _contrac
|
||||
needToAddCallvalueCheck = false;
|
||||
}
|
||||
|
||||
eth::AssemblyItem notFound = m_context.newTag();
|
||||
// directly jump to fallback if the data is too short to contain a function selector
|
||||
eth::AssemblyItem notFoundOrReceiveEther = m_context.newTag();
|
||||
// If there is neither a fallback nor a receive ether function, we only need one label to jump to, which
|
||||
// always reverts.
|
||||
eth::AssemblyItem notFound = (!fallback && !etherReceiver) ? notFoundOrReceiveEther : m_context.newTag();
|
||||
|
||||
// directly jump to fallback or ether receiver if the data is too short to contain a function selector
|
||||
// also guards against short data
|
||||
m_context << u256(4) << Instruction::CALLDATASIZE << Instruction::LT;
|
||||
m_context.appendConditionalJumpTo(notFound);
|
||||
m_context.appendConditionalJumpTo(notFoundOrReceiveEther);
|
||||
|
||||
// retrieve the function signature hash from the calldata
|
||||
if (!interfaceFunctions.empty())
|
||||
@ -391,23 +401,44 @@ void ContractCompiler::appendFunctionSelector(ContractDefinition const& _contrac
|
||||
appendInternalSelector(callDataUnpackerEntryPoints, sortedIDs, notFound, m_optimiserSettings.expectedExecutionsPerDeployment);
|
||||
}
|
||||
|
||||
m_context << notFound;
|
||||
m_context << notFoundOrReceiveEther;
|
||||
|
||||
if (fallback)
|
||||
{
|
||||
solAssert(!_contract.isLibrary(), "");
|
||||
if (!fallback->isPayable() && needToAddCallvalueCheck)
|
||||
appendCallValueCheck();
|
||||
|
||||
solAssert(fallback->isFallback(), "");
|
||||
solAssert(FunctionType(*fallback).parameterTypes().empty(), "");
|
||||
solAssert(FunctionType(*fallback).returnParameterTypes().empty(), "");
|
||||
fallback->accept(*this);
|
||||
m_context << Instruction::STOP;
|
||||
}
|
||||
else
|
||||
// TODO: error message here?
|
||||
if (!fallback && !etherReceiver)
|
||||
m_context.appendRevert();
|
||||
else
|
||||
{
|
||||
if (etherReceiver)
|
||||
{
|
||||
// directly jump to fallback, if there is calldata
|
||||
m_context << Instruction::CALLDATASIZE;
|
||||
m_context.appendConditionalJumpTo(notFound);
|
||||
|
||||
solAssert(!_contract.isLibrary(), "");
|
||||
solAssert(etherReceiver->isReceive(), "");
|
||||
solAssert(FunctionType(*etherReceiver).parameterTypes().empty(), "");
|
||||
solAssert(FunctionType(*etherReceiver).returnParameterTypes().empty(), "");
|
||||
etherReceiver->accept(*this);
|
||||
m_context << Instruction::STOP;
|
||||
}
|
||||
|
||||
m_context << notFound;
|
||||
if (fallback)
|
||||
{
|
||||
solAssert(!_contract.isLibrary(), "");
|
||||
if (!fallback->isPayable() && needToAddCallvalueCheck)
|
||||
appendCallValueCheck();
|
||||
|
||||
solAssert(fallback->isFallback(), "");
|
||||
solAssert(FunctionType(*fallback).parameterTypes().empty(), "");
|
||||
solAssert(FunctionType(*fallback).returnParameterTypes().empty(), "");
|
||||
fallback->accept(*this);
|
||||
m_context << Instruction::STOP;
|
||||
}
|
||||
else
|
||||
// TODO: error message here?
|
||||
m_context.appendRevert();
|
||||
}
|
||||
|
||||
|
||||
for (auto const& it: interfaceFunctions)
|
||||
{
|
||||
@ -582,7 +613,7 @@ bool ContractCompiler::visit(FunctionDefinition const& _function)
|
||||
if (!_function.isConstructor())
|
||||
{
|
||||
solAssert(m_context.numberOfLocalVariables() == 0, "");
|
||||
if (!_function.isFallback())
|
||||
if (!_function.isFallback() && !_function.isReceive())
|
||||
m_context.appendJump(eth::AssemblyItem::JumpType::OutOfFunction);
|
||||
}
|
||||
|
||||
|
@ -264,6 +264,7 @@ string IRGenerator::dispatchRoutine(ContractDefinition const& _contract)
|
||||
</cases>
|
||||
default {}
|
||||
}
|
||||
if iszero(calldatasize()) { <receiveEther> }
|
||||
<fallback>
|
||||
)X");
|
||||
t("shr224", m_utils.shiftRightFunction(224));
|
||||
@ -310,6 +311,10 @@ string IRGenerator::dispatchRoutine(ContractDefinition const& _contract)
|
||||
}
|
||||
else
|
||||
t("fallback", "revert(0, 0)");
|
||||
if (FunctionDefinition const* etherReceiver = _contract.receiveFunction())
|
||||
t("receiveEther", generateFunction(*etherReceiver) + "() stop()");
|
||||
else
|
||||
t("receiveEther", "");
|
||||
return t.render();
|
||||
}
|
||||
|
||||
|
@ -636,12 +636,9 @@ string CHC::predicateName(ASTNode const* _node)
|
||||
string prefix;
|
||||
if (auto funDef = dynamic_cast<FunctionDefinition const*>(_node))
|
||||
{
|
||||
prefix = funDef->isConstructor() ?
|
||||
"constructor" :
|
||||
funDef->isFallback() ?
|
||||
"fallback" :
|
||||
"function_" + funDef->name();
|
||||
prefix += "_";
|
||||
prefix += TokenTraits::toString(funDef->kind());
|
||||
if (!funDef->name().empty())
|
||||
prefix += "_" + funDef->name() + "_";
|
||||
}
|
||||
return prefix + to_string(_node->id());
|
||||
}
|
||||
|
@ -89,15 +89,16 @@ Json::Value ABI::generate(ContractDefinition const& _contractDef)
|
||||
);
|
||||
abi.emplace(std::move(method));
|
||||
}
|
||||
if (_contractDef.fallbackFunction())
|
||||
{
|
||||
FunctionType const* externalFunctionType = FunctionType(*_contractDef.fallbackFunction(), false).interfaceFunctionType();
|
||||
solAssert(!!externalFunctionType, "");
|
||||
Json::Value method;
|
||||
method["type"] = "fallback";
|
||||
method["stateMutability"] = stateMutabilityToString(externalFunctionType->stateMutability());
|
||||
abi.emplace(std::move(method));
|
||||
}
|
||||
for (auto const* fallbackOrReceive: {_contractDef.fallbackFunction(), _contractDef.receiveFunction()})
|
||||
if (fallbackOrReceive)
|
||||
{
|
||||
FunctionType const* externalFunctionType = FunctionType(*fallbackOrReceive, false).interfaceFunctionType();
|
||||
solAssert(!!externalFunctionType, "");
|
||||
Json::Value method;
|
||||
method["type"] = TokenTraits::toString(fallbackOrReceive->kind());
|
||||
method["stateMutability"] = stateMutabilityToString(externalFunctionType->stateMutability());
|
||||
abi.emplace(std::move(method));
|
||||
}
|
||||
for (auto const& it: _contractDef.interfaceEvents())
|
||||
{
|
||||
Json::Value event;
|
||||
|
@ -1434,8 +1434,8 @@ Json::Value CompilerStack::gasEstimates(string const& _contractName) const
|
||||
Json::Value internalFunctions(Json::objectValue);
|
||||
for (auto const& it: contract.definedFunctions())
|
||||
{
|
||||
/// Exclude externally visible functions, constructor and the fallback function
|
||||
if (it->isPartOfExternalInterface() || it->isConstructor() || it->isFallback())
|
||||
/// Exclude externally visible functions, constructor, fallback and receive ether function
|
||||
if (it->isPartOfExternalInterface() || !it->isOrdinary())
|
||||
continue;
|
||||
|
||||
size_t entry = functionEntryPoint(_contractName, *it);
|
||||
|
@ -300,19 +300,23 @@ ASTPointer<ContractDefinition> Parser::parseContractDefinition()
|
||||
Token currentTokenValue = m_scanner->currentToken();
|
||||
if (currentTokenValue == Token::RBrace)
|
||||
break;
|
||||
else if (currentTokenValue == Token::Function || currentTokenValue == Token::Constructor)
|
||||
// This can be a function or a state variable of function type (especially
|
||||
// complicated to distinguish fallback function from function type state variable)
|
||||
subNodes.push_back(parseFunctionDefinitionOrFunctionTypeStateVariable());
|
||||
else if (
|
||||
(currentTokenValue == Token::Function && m_scanner->peekNextToken() != Token::LParen) ||
|
||||
currentTokenValue == Token::Constructor ||
|
||||
currentTokenValue == Token::Receive ||
|
||||
currentTokenValue == Token::Fallback
|
||||
)
|
||||
subNodes.push_back(parseFunctionDefinition());
|
||||
else if (currentTokenValue == Token::Struct)
|
||||
subNodes.push_back(parseStructDefinition());
|
||||
else if (currentTokenValue == Token::Enum)
|
||||
subNodes.push_back(parseEnumDefinition());
|
||||
else if (
|
||||
currentTokenValue == Token::Identifier ||
|
||||
currentTokenValue == Token::Mapping ||
|
||||
TokenTraits::isElementaryTypeName(currentTokenValue)
|
||||
)
|
||||
currentTokenValue == Token::Identifier ||
|
||||
currentTokenValue == Token::Mapping ||
|
||||
TokenTraits::isElementaryTypeName(currentTokenValue) ||
|
||||
(currentTokenValue == Token::Function && m_scanner->peekNextToken() == Token::LParen)
|
||||
)
|
||||
{
|
||||
VarDeclParserOptions options;
|
||||
options.isStateVariable = true;
|
||||
@ -457,64 +461,28 @@ StateMutability Parser::parseStateMutability()
|
||||
return stateMutability;
|
||||
}
|
||||
|
||||
Parser::FunctionHeaderParserResult Parser::parseFunctionHeader(bool _onlyFuncType, bool _allowFuncDef)
|
||||
Parser::FunctionHeaderParserResult Parser::parseFunctionHeader(bool _isStateVariable)
|
||||
{
|
||||
RecursionGuard recursionGuard(*this);
|
||||
FunctionHeaderParserResult result;
|
||||
|
||||
result.isConstructor = false;
|
||||
result.overrides = nullptr;
|
||||
|
||||
if (m_scanner->currentToken() == Token::Constructor)
|
||||
result.isConstructor = true;
|
||||
else if (m_scanner->currentToken() != Token::Function)
|
||||
solAssert(false, "Function or constructor expected.");
|
||||
m_scanner->next();
|
||||
|
||||
if (result.isConstructor)
|
||||
result.name = make_shared<ASTString>();
|
||||
else if (_onlyFuncType || m_scanner->currentToken() == Token::LParen)
|
||||
result.name = make_shared<ASTString>();
|
||||
else if (m_scanner->currentToken() == Token::Constructor)
|
||||
fatalParserError(string(
|
||||
"This function is named \"constructor\" but is not the constructor of the contract. "
|
||||
"If you intend this to be a constructor, use \"constructor(...) { ... }\" without the \"function\" keyword to define it."
|
||||
));
|
||||
else
|
||||
result.name = expectIdentifierToken();
|
||||
|
||||
|
||||
VarDeclParserOptions options;
|
||||
options.allowLocationSpecifier = true;
|
||||
result.parameters = parseParameterList(options);
|
||||
while (true)
|
||||
{
|
||||
Token token = m_scanner->currentToken();
|
||||
if (_allowFuncDef && token == Token::Identifier)
|
||||
{
|
||||
// If the name is empty (and this is not a constructor),
|
||||
// then this can either be a modifier (fallback function declaration)
|
||||
// or the name of the state variable (function type name plus variable).
|
||||
if ((result.name->empty() && !result.isConstructor) && (
|
||||
m_scanner->peekNextToken() == Token::Semicolon ||
|
||||
m_scanner->peekNextToken() == Token::Assign
|
||||
))
|
||||
// Variable declaration, break here.
|
||||
break;
|
||||
else
|
||||
result.modifiers.push_back(parseModifierInvocation());
|
||||
}
|
||||
if (!_isStateVariable && token == Token::Identifier)
|
||||
result.modifiers.push_back(parseModifierInvocation());
|
||||
else if (TokenTraits::isVisibilitySpecifier(token))
|
||||
{
|
||||
if (result.visibility != Declaration::Visibility::Default)
|
||||
{
|
||||
// There is the special case of a public state variable of function type.
|
||||
// Detect this and return early.
|
||||
if (
|
||||
(result.visibility == Declaration::Visibility::External || result.visibility == Declaration::Visibility::Internal) &&
|
||||
result.modifiers.empty() &&
|
||||
(result.name->empty() && !result.isConstructor)
|
||||
)
|
||||
if (_isStateVariable && (result.visibility == Declaration::Visibility::External || result.visibility == Declaration::Visibility::Internal))
|
||||
break;
|
||||
parserError(string(
|
||||
"Visibility already specified as \"" +
|
||||
@ -540,7 +508,7 @@ Parser::FunctionHeaderParserResult Parser::parseFunctionHeader(bool _onlyFuncTyp
|
||||
else
|
||||
result.stateMutability = parseStateMutability();
|
||||
}
|
||||
else if (_allowFuncDef && token == Token::Override)
|
||||
else if (!_isStateVariable && token == Token::Override)
|
||||
{
|
||||
if (result.overrides)
|
||||
parserError("Override already specified.");
|
||||
@ -561,7 +529,7 @@ Parser::FunctionHeaderParserResult Parser::parseFunctionHeader(bool _onlyFuncTyp
|
||||
return result;
|
||||
}
|
||||
|
||||
ASTPointer<ASTNode> Parser::parseFunctionDefinitionOrFunctionTypeStateVariable()
|
||||
ASTPointer<ASTNode> Parser::parseFunctionDefinition()
|
||||
{
|
||||
RecursionGuard recursionGuard(*this);
|
||||
ASTNodeFactory nodeFactory(*this);
|
||||
@ -569,57 +537,67 @@ ASTPointer<ASTNode> Parser::parseFunctionDefinitionOrFunctionTypeStateVariable()
|
||||
if (m_scanner->currentCommentLiteral() != "")
|
||||
docstring = make_shared<ASTString>(m_scanner->currentCommentLiteral());
|
||||
|
||||
FunctionHeaderParserResult header = parseFunctionHeader(false, true);
|
||||
|
||||
if (
|
||||
header.isConstructor ||
|
||||
!header.modifiers.empty() ||
|
||||
!header.name->empty() ||
|
||||
header.overrides ||
|
||||
m_scanner->currentToken() == Token::Semicolon ||
|
||||
m_scanner->currentToken() == Token::LBrace
|
||||
)
|
||||
Token kind = m_scanner->currentToken();
|
||||
ASTPointer<ASTString> name;
|
||||
if (kind == Token::Function)
|
||||
{
|
||||
// this has to be a function
|
||||
ASTPointer<Block> block = ASTPointer<Block>();
|
||||
nodeFactory.markEndPosition();
|
||||
if (m_scanner->currentToken() != Token::Semicolon)
|
||||
m_scanner->next();
|
||||
if (
|
||||
m_scanner->currentToken() == Token::Constructor ||
|
||||
m_scanner->currentToken() == Token::Fallback ||
|
||||
m_scanner->currentToken() == Token::Receive
|
||||
)
|
||||
{
|
||||
block = parseBlock();
|
||||
nodeFactory.setEndPositionFromNode(block);
|
||||
std::string expected = std::map<Token, std::string>{
|
||||
{Token::Constructor, "constructor"},
|
||||
{Token::Fallback, "fallback function"},
|
||||
{Token::Receive, "receive function"},
|
||||
}.at(m_scanner->currentToken());
|
||||
name = make_shared<ASTString>(TokenTraits::toString(m_scanner->currentToken()));
|
||||
string message{
|
||||
"This function is named \"" + *name + "\" but is not the " + expected + " of the contract. "
|
||||
"If you intend this to be a " + expected + ", use \"" + *name + "(...) { ... }\" without "
|
||||
"the \"function\" keyword to define it."
|
||||
};
|
||||
if (m_scanner->currentToken() == Token::Constructor)
|
||||
parserError(message);
|
||||
else
|
||||
parserWarning(message);
|
||||
m_scanner->next();
|
||||
}
|
||||
else
|
||||
m_scanner->next(); // just consume the ';'
|
||||
return nodeFactory.createNode<FunctionDefinition>(
|
||||
header.name,
|
||||
header.visibility,
|
||||
header.stateMutability,
|
||||
header.isConstructor,
|
||||
header.overrides,
|
||||
docstring,
|
||||
header.parameters,
|
||||
header.modifiers,
|
||||
header.returnParameters,
|
||||
block
|
||||
);
|
||||
name = expectIdentifierToken();
|
||||
}
|
||||
else
|
||||
{
|
||||
// this has to be a state variable
|
||||
ASTPointer<TypeName> type = nodeFactory.createNode<FunctionTypeName>(
|
||||
header.parameters,
|
||||
header.returnParameters,
|
||||
header.visibility,
|
||||
header.stateMutability
|
||||
);
|
||||
type = parseTypeNameSuffix(type, nodeFactory);
|
||||
VarDeclParserOptions options;
|
||||
options.isStateVariable = true;
|
||||
options.allowInitialValue = true;
|
||||
auto node = parseVariableDeclaration(options, type);
|
||||
expectToken(Token::Semicolon);
|
||||
return node;
|
||||
solAssert(kind == Token::Constructor || kind == Token::Fallback || kind == Token::Receive, "");
|
||||
m_scanner->next();
|
||||
name = make_shared<ASTString>();
|
||||
}
|
||||
|
||||
FunctionHeaderParserResult header = parseFunctionHeader(false);
|
||||
|
||||
ASTPointer<Block> block;
|
||||
nodeFactory.markEndPosition();
|
||||
if (m_scanner->currentToken() != Token::Semicolon)
|
||||
{
|
||||
block = parseBlock();
|
||||
nodeFactory.setEndPositionFromNode(block);
|
||||
}
|
||||
else
|
||||
m_scanner->next(); // just consume the ';'
|
||||
return nodeFactory.createNode<FunctionDefinition>(
|
||||
name,
|
||||
header.visibility,
|
||||
header.stateMutability,
|
||||
kind,
|
||||
header.overrides,
|
||||
docstring,
|
||||
header.parameters,
|
||||
header.modifiers,
|
||||
header.returnParameters,
|
||||
block
|
||||
);
|
||||
}
|
||||
|
||||
ASTPointer<StructDefinition> Parser::parseStructDefinition()
|
||||
@ -691,6 +669,14 @@ ASTPointer<VariableDeclaration> Parser::parseVariableDeclaration(
|
||||
if (type != nullptr)
|
||||
nodeFactory.setEndPositionFromNode(type);
|
||||
}
|
||||
|
||||
if (dynamic_cast<FunctionTypeName*>(type.get()) && _options.isStateVariable && m_scanner->currentToken() == Token::LBrace)
|
||||
fatalParserError(
|
||||
"Expected a state variable declaration. If you intended this as a fallback function "
|
||||
"or a function to handle plain ether transactions, use the \"fallback\" keyword "
|
||||
"or the \"ether\" keyword instead."
|
||||
);
|
||||
|
||||
bool isIndexed = false;
|
||||
bool isDeclaredConst = false;
|
||||
ASTPointer<OverrideSpecifier> overrides = nullptr;
|
||||
@ -986,8 +972,8 @@ ASTPointer<FunctionTypeName> Parser::parseFunctionType()
|
||||
{
|
||||
RecursionGuard recursionGuard(*this);
|
||||
ASTNodeFactory nodeFactory(*this);
|
||||
FunctionHeaderParserResult header = parseFunctionHeader(true, false);
|
||||
solAssert(!header.isConstructor, "Tried to parse type as constructor.");
|
||||
expectToken(Token::Function);
|
||||
FunctionHeaderParserResult header = parseFunctionHeader(true);
|
||||
return nodeFactory.createNode<FunctionTypeName>(
|
||||
header.parameters,
|
||||
header.returnParameters,
|
||||
|
@ -70,9 +70,7 @@ private:
|
||||
/// This struct is shared for parsing a function header and a function type.
|
||||
struct FunctionHeaderParserResult
|
||||
{
|
||||
bool isConstructor;
|
||||
ASTPointer<OverrideSpecifier> overrides;
|
||||
ASTPointer<ASTString> name;
|
||||
ASTPointer<ParameterList> parameters;
|
||||
ASTPointer<ParameterList> returnParameters;
|
||||
Declaration::Visibility visibility = Declaration::Visibility::Default;
|
||||
@ -93,9 +91,8 @@ private:
|
||||
Declaration::Visibility parseVisibilitySpecifier();
|
||||
ASTPointer<OverrideSpecifier> parseOverrideSpecifier();
|
||||
StateMutability parseStateMutability();
|
||||
FunctionHeaderParserResult parseFunctionHeader(bool _onlyFuncType, bool _allowFuncDef);
|
||||
ASTPointer<ASTNode> parseFunctionDefinitionOrFunctionTypeStateVariable();
|
||||
ASTPointer<FunctionDefinition> parseFunctionDefinition(ASTString const* _contractName);
|
||||
FunctionHeaderParserResult parseFunctionHeader(bool _isStateVariable);
|
||||
ASTPointer<ASTNode> parseFunctionDefinition();
|
||||
ASTPointer<StructDefinition> parseStructDefinition();
|
||||
ASTPointer<EnumDefinition> parseEnumDefinition();
|
||||
ASTPointer<EnumValue> parseEnumValue();
|
||||
|
@ -30,6 +30,7 @@ object \"C_6\" {
|
||||
}
|
||||
default { }
|
||||
}
|
||||
if iszero(calldatasize()) { }
|
||||
revert(0, 0)
|
||||
function abi_decode_tuple_(headStart, dataEnd)
|
||||
{
|
||||
|
@ -45,6 +45,7 @@ object \"C_6\" {
|
||||
|
||||
default {}
|
||||
}
|
||||
if iszero(calldatasize()) { }
|
||||
revert(0, 0)
|
||||
|
||||
|
||||
|
@ -88,8 +88,8 @@ contract MultiSigWallet {
|
||||
_;
|
||||
}
|
||||
|
||||
/// @dev Fallback function allows to deposit ether.
|
||||
function()
|
||||
/// @dev Receive function allows to deposit ether.
|
||||
receive()
|
||||
external
|
||||
payable
|
||||
{
|
||||
|
@ -275,7 +275,7 @@ contract ico is safeMath {
|
||||
require( msg.sender.send(_val) );
|
||||
}
|
||||
|
||||
function () external payable {
|
||||
receive () external payable {
|
||||
/*
|
||||
Callback function. Simply calls the buy function as a beneficiary and there is no affiliate address.
|
||||
If they call the contract without any function then this process will be taken place.
|
||||
|
@ -140,6 +140,6 @@ contract module {
|
||||
require( msg.sender == moduleHandlerAddress );
|
||||
_;
|
||||
}
|
||||
function() external payable {
|
||||
receive() external payable {
|
||||
}
|
||||
}
|
||||
|
@ -382,9 +382,9 @@ contract Wallet is multisig, multiowned, daylimit {
|
||||
selfdestruct(_to);
|
||||
}
|
||||
|
||||
// gets called when no other function matches
|
||||
function() external payable {
|
||||
// just being sent some cash?
|
||||
// gets called for plain ether transfers
|
||||
receive() external payable {
|
||||
// did we actually receive value?
|
||||
if (msg.value > 0)
|
||||
emit Deposit(msg.sender, msg.value);
|
||||
}
|
||||
|
@ -174,7 +174,7 @@ abstract contract DAOInterface {
|
||||
// );
|
||||
|
||||
/// @notice Create Token with `msg.sender` as the beneficiary
|
||||
function () external payable;
|
||||
receive() external payable;
|
||||
|
||||
|
||||
/// @dev This function is used to send ether back
|
||||
@ -389,7 +389,7 @@ contract DAO is DAOInterface, Token, TokenCreation {
|
||||
allowedRecipients[curator] = true;
|
||||
}
|
||||
|
||||
function () external payable {
|
||||
receive() external payable override(DAOInterface, TokenCreation) {
|
||||
if (now < closingTime + creationGracePeriod && msg.sender != address(extraBalance))
|
||||
createTokenProxy(msg.sender);
|
||||
else
|
||||
|
@ -50,7 +50,7 @@ contract ManagedAccount is ManagedAccountInterface{
|
||||
// When the contract receives a transaction without data this is called.
|
||||
// It counts the amount of ether it receives and stores it in
|
||||
// accumulatedInput.
|
||||
function() external payable {
|
||||
receive() external payable {
|
||||
accumulatedInput += msg.value;
|
||||
}
|
||||
|
||||
|
@ -151,6 +151,6 @@ override returns (bool success) {
|
||||
return 30;
|
||||
}
|
||||
}
|
||||
function() external payable {
|
||||
receive() external payable {
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
contract test {
|
||||
function() external {}
|
||||
fallback() external {}
|
||||
}
|
||||
// ----
|
||||
// :test
|
||||
|
@ -1,5 +1,5 @@
|
||||
contract test {
|
||||
function () external payable {}
|
||||
fallback () external payable {}
|
||||
}
|
||||
// ----
|
||||
// :test
|
@ -0,0 +1,16 @@
|
||||
contract test {
|
||||
receive() external payable {}
|
||||
fallback() external {}
|
||||
}
|
||||
// ----
|
||||
// :test
|
||||
// [
|
||||
// {
|
||||
// "stateMutability": "nonpayable",
|
||||
// "type": "fallback"
|
||||
// },
|
||||
// {
|
||||
// "stateMutability": "payable",
|
||||
// "type": "receive"
|
||||
// }
|
||||
// ]
|
11
test/libsolidity/ABIJson/receive_ether_function.sol
Normal file
11
test/libsolidity/ABIJson/receive_ether_function.sol
Normal file
@ -0,0 +1,11 @@
|
||||
contract test {
|
||||
receive() external payable {}
|
||||
}
|
||||
// ----
|
||||
// :test
|
||||
// [
|
||||
// {
|
||||
// "stateMutability": "payable",
|
||||
// "type": "receive"
|
||||
// }
|
||||
// ]
|
@ -1,5 +1,5 @@
|
||||
contract C {
|
||||
function() external payable {
|
||||
fallback() external payable {
|
||||
}
|
||||
}
|
||||
|
||||
|
108
test/libsolidity/ASTJSON/fallback_and_reveice_ether.json
Normal file
108
test/libsolidity/ASTJSON/fallback_and_reveice_ether.json
Normal file
@ -0,0 +1,108 @@
|
||||
{
|
||||
"absolutePath": "a",
|
||||
"exportedSymbols":
|
||||
{
|
||||
"C":
|
||||
[
|
||||
9
|
||||
]
|
||||
},
|
||||
"id": 10,
|
||||
"nodeType": "SourceUnit",
|
||||
"nodes":
|
||||
[
|
||||
{
|
||||
"abstract": false,
|
||||
"baseContracts": [],
|
||||
"contractDependencies": [],
|
||||
"contractKind": "contract",
|
||||
"documentation": null,
|
||||
"fullyImplemented": true,
|
||||
"id": 9,
|
||||
"linearizedBaseContracts":
|
||||
[
|
||||
9
|
||||
],
|
||||
"name": "C",
|
||||
"nodeType": "ContractDefinition",
|
||||
"nodes":
|
||||
[
|
||||
{
|
||||
"body":
|
||||
{
|
||||
"id": 3,
|
||||
"nodeType": "Block",
|
||||
"src": "42:5:1",
|
||||
"statements": []
|
||||
},
|
||||
"documentation": null,
|
||||
"id": 4,
|
||||
"implemented": true,
|
||||
"kind": "receive",
|
||||
"modifiers": [],
|
||||
"name": "",
|
||||
"nodeType": "FunctionDefinition",
|
||||
"overrides": null,
|
||||
"parameters":
|
||||
{
|
||||
"id": 1,
|
||||
"nodeType": "ParameterList",
|
||||
"parameters": [],
|
||||
"src": "22:2:1"
|
||||
},
|
||||
"returnParameters":
|
||||
{
|
||||
"id": 2,
|
||||
"nodeType": "ParameterList",
|
||||
"parameters": [],
|
||||
"src": "42:0:1"
|
||||
},
|
||||
"scope": 9,
|
||||
"src": "15:32:1",
|
||||
"stateMutability": "payable",
|
||||
"superFunction": null,
|
||||
"visibility": "external"
|
||||
},
|
||||
{
|
||||
"body":
|
||||
{
|
||||
"id": 7,
|
||||
"nodeType": "Block",
|
||||
"src": "78:5:1",
|
||||
"statements": []
|
||||
},
|
||||
"documentation": null,
|
||||
"id": 8,
|
||||
"implemented": true,
|
||||
"kind": "fallback",
|
||||
"modifiers": [],
|
||||
"name": "",
|
||||
"nodeType": "FunctionDefinition",
|
||||
"overrides": null,
|
||||
"parameters":
|
||||
{
|
||||
"id": 5,
|
||||
"nodeType": "ParameterList",
|
||||
"parameters": [],
|
||||
"src": "58:2:1"
|
||||
},
|
||||
"returnParameters":
|
||||
{
|
||||
"id": 6,
|
||||
"nodeType": "ParameterList",
|
||||
"parameters": [],
|
||||
"src": "78:0:1"
|
||||
},
|
||||
"scope": 9,
|
||||
"src": "50:33:1",
|
||||
"stateMutability": "payable",
|
||||
"superFunction": null,
|
||||
"visibility": "external"
|
||||
}
|
||||
],
|
||||
"scope": 10,
|
||||
"src": "0:85:1"
|
||||
}
|
||||
],
|
||||
"src": "0:86:1"
|
||||
}
|
8
test/libsolidity/ASTJSON/fallback_and_reveice_ether.sol
Normal file
8
test/libsolidity/ASTJSON/fallback_and_reveice_ether.sol
Normal file
@ -0,0 +1,8 @@
|
||||
contract C {
|
||||
receive() external payable {
|
||||
}
|
||||
fallback() external payable {
|
||||
}
|
||||
}
|
||||
|
||||
// ----
|
176
test/libsolidity/ASTJSON/fallback_and_reveice_ether_legacy.json
Normal file
176
test/libsolidity/ASTJSON/fallback_and_reveice_ether_legacy.json
Normal file
@ -0,0 +1,176 @@
|
||||
{
|
||||
"attributes":
|
||||
{
|
||||
"absolutePath": "a",
|
||||
"exportedSymbols":
|
||||
{
|
||||
"C":
|
||||
[
|
||||
9
|
||||
]
|
||||
}
|
||||
},
|
||||
"children":
|
||||
[
|
||||
{
|
||||
"attributes":
|
||||
{
|
||||
"abstract": false,
|
||||
"baseContracts":
|
||||
[
|
||||
null
|
||||
],
|
||||
"contractDependencies":
|
||||
[
|
||||
null
|
||||
],
|
||||
"contractKind": "contract",
|
||||
"documentation": null,
|
||||
"fullyImplemented": true,
|
||||
"linearizedBaseContracts":
|
||||
[
|
||||
9
|
||||
],
|
||||
"name": "C",
|
||||
"scope": 10
|
||||
},
|
||||
"children":
|
||||
[
|
||||
{
|
||||
"attributes":
|
||||
{
|
||||
"documentation": null,
|
||||
"implemented": true,
|
||||
"isConstructor": false,
|
||||
"kind": "receive",
|
||||
"modifiers":
|
||||
[
|
||||
null
|
||||
],
|
||||
"name": "",
|
||||
"overrides": null,
|
||||
"scope": 9,
|
||||
"stateMutability": "payable",
|
||||
"superFunction": null,
|
||||
"visibility": "external"
|
||||
},
|
||||
"children":
|
||||
[
|
||||
{
|
||||
"attributes":
|
||||
{
|
||||
"parameters":
|
||||
[
|
||||
null
|
||||
]
|
||||
},
|
||||
"children": [],
|
||||
"id": 1,
|
||||
"name": "ParameterList",
|
||||
"src": "22:2:1"
|
||||
},
|
||||
{
|
||||
"attributes":
|
||||
{
|
||||
"parameters":
|
||||
[
|
||||
null
|
||||
]
|
||||
},
|
||||
"children": [],
|
||||
"id": 2,
|
||||
"name": "ParameterList",
|
||||
"src": "42:0:1"
|
||||
},
|
||||
{
|
||||
"attributes":
|
||||
{
|
||||
"statements":
|
||||
[
|
||||
null
|
||||
]
|
||||
},
|
||||
"children": [],
|
||||
"id": 3,
|
||||
"name": "Block",
|
||||
"src": "42:5:1"
|
||||
}
|
||||
],
|
||||
"id": 4,
|
||||
"name": "FunctionDefinition",
|
||||
"src": "15:32:1"
|
||||
},
|
||||
{
|
||||
"attributes":
|
||||
{
|
||||
"documentation": null,
|
||||
"implemented": true,
|
||||
"isConstructor": false,
|
||||
"kind": "fallback",
|
||||
"modifiers":
|
||||
[
|
||||
null
|
||||
],
|
||||
"name": "",
|
||||
"overrides": null,
|
||||
"scope": 9,
|
||||
"stateMutability": "payable",
|
||||
"superFunction": null,
|
||||
"visibility": "external"
|
||||
},
|
||||
"children":
|
||||
[
|
||||
{
|
||||
"attributes":
|
||||
{
|
||||
"parameters":
|
||||
[
|
||||
null
|
||||
]
|
||||
},
|
||||
"children": [],
|
||||
"id": 5,
|
||||
"name": "ParameterList",
|
||||
"src": "58:2:1"
|
||||
},
|
||||
{
|
||||
"attributes":
|
||||
{
|
||||
"parameters":
|
||||
[
|
||||
null
|
||||
]
|
||||
},
|
||||
"children": [],
|
||||
"id": 6,
|
||||
"name": "ParameterList",
|
||||
"src": "78:0:1"
|
||||
},
|
||||
{
|
||||
"attributes":
|
||||
{
|
||||
"statements":
|
||||
[
|
||||
null
|
||||
]
|
||||
},
|
||||
"children": [],
|
||||
"id": 7,
|
||||
"name": "Block",
|
||||
"src": "78:5:1"
|
||||
}
|
||||
],
|
||||
"id": 8,
|
||||
"name": "FunctionDefinition",
|
||||
"src": "50:33:1"
|
||||
}
|
||||
],
|
||||
"id": 9,
|
||||
"name": "ContractDefinition",
|
||||
"src": "0:85:1"
|
||||
}
|
||||
],
|
||||
"id": 10,
|
||||
"name": "SourceUnit",
|
||||
"src": "0:86:1"
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
contract C {
|
||||
function() external {}
|
||||
fallback() external {}
|
||||
}
|
||||
|
||||
// ----
|
||||
|
72
test/libsolidity/ASTJSON/receive_ether.json
Normal file
72
test/libsolidity/ASTJSON/receive_ether.json
Normal file
@ -0,0 +1,72 @@
|
||||
{
|
||||
"absolutePath": "a",
|
||||
"exportedSymbols":
|
||||
{
|
||||
"C":
|
||||
[
|
||||
5
|
||||
]
|
||||
},
|
||||
"id": 6,
|
||||
"nodeType": "SourceUnit",
|
||||
"nodes":
|
||||
[
|
||||
{
|
||||
"abstract": false,
|
||||
"baseContracts": [],
|
||||
"contractDependencies": [],
|
||||
"contractKind": "contract",
|
||||
"documentation": null,
|
||||
"fullyImplemented": true,
|
||||
"id": 5,
|
||||
"linearizedBaseContracts":
|
||||
[
|
||||
5
|
||||
],
|
||||
"name": "C",
|
||||
"nodeType": "ContractDefinition",
|
||||
"nodes":
|
||||
[
|
||||
{
|
||||
"body":
|
||||
{
|
||||
"id": 3,
|
||||
"nodeType": "Block",
|
||||
"src": "42:5:1",
|
||||
"statements": []
|
||||
},
|
||||
"documentation": null,
|
||||
"id": 4,
|
||||
"implemented": true,
|
||||
"kind": "receive",
|
||||
"modifiers": [],
|
||||
"name": "",
|
||||
"nodeType": "FunctionDefinition",
|
||||
"overrides": null,
|
||||
"parameters":
|
||||
{
|
||||
"id": 1,
|
||||
"nodeType": "ParameterList",
|
||||
"parameters": [],
|
||||
"src": "22:2:1"
|
||||
},
|
||||
"returnParameters":
|
||||
{
|
||||
"id": 2,
|
||||
"nodeType": "ParameterList",
|
||||
"parameters": [],
|
||||
"src": "42:0:1"
|
||||
},
|
||||
"scope": 5,
|
||||
"src": "15:32:1",
|
||||
"stateMutability": "payable",
|
||||
"superFunction": null,
|
||||
"visibility": "external"
|
||||
}
|
||||
],
|
||||
"scope": 6,
|
||||
"src": "0:49:1"
|
||||
}
|
||||
],
|
||||
"src": "0:50:1"
|
||||
}
|
6
test/libsolidity/ASTJSON/receive_ether.sol
Normal file
6
test/libsolidity/ASTJSON/receive_ether.sol
Normal file
@ -0,0 +1,6 @@
|
||||
contract C {
|
||||
receive() external payable {
|
||||
}
|
||||
}
|
||||
|
||||
// ----
|
112
test/libsolidity/ASTJSON/receive_ether_legacy.json
Normal file
112
test/libsolidity/ASTJSON/receive_ether_legacy.json
Normal file
@ -0,0 +1,112 @@
|
||||
{
|
||||
"attributes":
|
||||
{
|
||||
"absolutePath": "a",
|
||||
"exportedSymbols":
|
||||
{
|
||||
"C":
|
||||
[
|
||||
5
|
||||
]
|
||||
}
|
||||
},
|
||||
"children":
|
||||
[
|
||||
{
|
||||
"attributes":
|
||||
{
|
||||
"abstract": false,
|
||||
"baseContracts":
|
||||
[
|
||||
null
|
||||
],
|
||||
"contractDependencies":
|
||||
[
|
||||
null
|
||||
],
|
||||
"contractKind": "contract",
|
||||
"documentation": null,
|
||||
"fullyImplemented": true,
|
||||
"linearizedBaseContracts":
|
||||
[
|
||||
5
|
||||
],
|
||||
"name": "C",
|
||||
"scope": 6
|
||||
},
|
||||
"children":
|
||||
[
|
||||
{
|
||||
"attributes":
|
||||
{
|
||||
"documentation": null,
|
||||
"implemented": true,
|
||||
"isConstructor": false,
|
||||
"kind": "receive",
|
||||
"modifiers":
|
||||
[
|
||||
null
|
||||
],
|
||||
"name": "",
|
||||
"overrides": null,
|
||||
"scope": 5,
|
||||
"stateMutability": "payable",
|
||||
"superFunction": null,
|
||||
"visibility": "external"
|
||||
},
|
||||
"children":
|
||||
[
|
||||
{
|
||||
"attributes":
|
||||
{
|
||||
"parameters":
|
||||
[
|
||||
null
|
||||
]
|
||||
},
|
||||
"children": [],
|
||||
"id": 1,
|
||||
"name": "ParameterList",
|
||||
"src": "22:2:1"
|
||||
},
|
||||
{
|
||||
"attributes":
|
||||
{
|
||||
"parameters":
|
||||
[
|
||||
null
|
||||
]
|
||||
},
|
||||
"children": [],
|
||||
"id": 2,
|
||||
"name": "ParameterList",
|
||||
"src": "42:0:1"
|
||||
},
|
||||
{
|
||||
"attributes":
|
||||
{
|
||||
"statements":
|
||||
[
|
||||
null
|
||||
]
|
||||
},
|
||||
"children": [],
|
||||
"id": 3,
|
||||
"name": "Block",
|
||||
"src": "42:5:1"
|
||||
}
|
||||
],
|
||||
"id": 4,
|
||||
"name": "FunctionDefinition",
|
||||
"src": "15:32:1"
|
||||
}
|
||||
],
|
||||
"id": 5,
|
||||
"name": "ContractDefinition",
|
||||
"src": "0:49:1"
|
||||
}
|
||||
],
|
||||
"id": 6,
|
||||
"name": "SourceUnit",
|
||||
"src": "0:50:1"
|
||||
}
|
@ -313,7 +313,7 @@ BOOST_AUTO_TEST_CASE(regular_functions_exclude_fallback)
|
||||
char const* sourceCode = R"(
|
||||
contract A {
|
||||
uint public x;
|
||||
function() external { x = 2; }
|
||||
fallback() external { x = 2; }
|
||||
}
|
||||
)";
|
||||
testCreationTimeGas(sourceCode);
|
||||
|
@ -1236,7 +1236,7 @@ BOOST_AUTO_TEST_CASE(transfer_ether)
|
||||
}
|
||||
|
||||
contract C {
|
||||
function () external payable {
|
||||
receive () external payable {
|
||||
revert();
|
||||
}
|
||||
}
|
||||
@ -1900,7 +1900,7 @@ BOOST_AUTO_TEST_CASE(contracts_as_addresses)
|
||||
{
|
||||
char const* sourceCode = R"(
|
||||
contract helper {
|
||||
function() external payable { } // can receive ether
|
||||
receive() external payable { } // can receive ether
|
||||
}
|
||||
contract test {
|
||||
helper h;
|
||||
@ -2514,24 +2514,6 @@ BOOST_AUTO_TEST_CASE(super_alone)
|
||||
)
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(inherited_fallback_function)
|
||||
{
|
||||
char const* sourceCode = R"(
|
||||
contract A {
|
||||
uint data;
|
||||
function() external { data = 1; }
|
||||
function getData() public returns (uint r) { return data; }
|
||||
}
|
||||
contract B is A {}
|
||||
)";
|
||||
ALSO_VIA_YUL(
|
||||
compileAndRun(sourceCode, 0, "B");
|
||||
ABI_CHECK(callContractFunction("getData()"), encodeArgs(0));
|
||||
ABI_CHECK(callContractFunction(""), encodeArgs());
|
||||
ABI_CHECK(callContractFunction("getData()"), encodeArgs(1));
|
||||
)
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(default_fallback_throws)
|
||||
{
|
||||
char const* sourceCode = R"YY(
|
||||
@ -2561,27 +2543,6 @@ BOOST_AUTO_TEST_CASE(default_fallback_throws)
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(short_data_calls_fallback)
|
||||
{
|
||||
char const* sourceCode = R"(
|
||||
contract A {
|
||||
uint public x;
|
||||
// Signature is d88e0b00
|
||||
function fow() public { x = 3; }
|
||||
function () external { x = 2; }
|
||||
}
|
||||
)";
|
||||
compileAndRun(sourceCode);
|
||||
// should call fallback
|
||||
sendMessage(asBytes("\xd8\x8e\x0b"), false, 0);
|
||||
BOOST_CHECK(m_transactionSuccessful);
|
||||
ABI_CHECK(callContractFunction("x()"), encodeArgs(2));
|
||||
// should call function
|
||||
sendMessage(asBytes(string("\xd8\x8e\x0b") + string(1, 0)), false, 0);
|
||||
BOOST_CHECK(m_transactionSuccessful);
|
||||
ABI_CHECK(callContractFunction("x()"), encodeArgs(3));
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(event)
|
||||
{
|
||||
char const* sourceCode = R"(
|
||||
@ -3363,13 +3324,13 @@ BOOST_AUTO_TEST_CASE(generic_call)
|
||||
char const* sourceCode = R"**(
|
||||
contract receiver {
|
||||
uint public received;
|
||||
function receive(uint256 x) public payable { received = x; }
|
||||
function recv(uint256 x) public payable { received = x; }
|
||||
}
|
||||
contract sender {
|
||||
constructor() public payable {}
|
||||
function doSend(address rec) public returns (uint d)
|
||||
{
|
||||
bytes4 signature = bytes4(bytes32(keccak256("receive(uint256)")));
|
||||
bytes4 signature = bytes4(bytes32(keccak256("recv(uint256)")));
|
||||
rec.call.value(2)(abi.encodeWithSelector(signature, 23));
|
||||
return receiver(rec).received();
|
||||
}
|
||||
@ -3390,7 +3351,7 @@ BOOST_AUTO_TEST_CASE(generic_delegatecall)
|
||||
address public sender;
|
||||
uint public value;
|
||||
constructor() public payable {}
|
||||
function receive(uint256 x) public payable { received = x; sender = msg.sender; value = msg.value; }
|
||||
function recv(uint256 x) public payable { received = x; sender = msg.sender; value = msg.value; }
|
||||
}
|
||||
contract Sender {
|
||||
uint public received;
|
||||
@ -3399,7 +3360,7 @@ BOOST_AUTO_TEST_CASE(generic_delegatecall)
|
||||
constructor() public payable {}
|
||||
function doSend(address rec) public payable
|
||||
{
|
||||
bytes4 signature = bytes4(bytes32(keccak256("receive(uint256)")));
|
||||
bytes4 signature = bytes4(bytes32(keccak256("recv(uint256)")));
|
||||
(bool success,) = rec.delegatecall(abi.encodeWithSelector(signature, 23));
|
||||
success;
|
||||
}
|
||||
@ -3588,12 +3549,12 @@ BOOST_AUTO_TEST_CASE(call_forward_bytes)
|
||||
char const* sourceCode = R"(
|
||||
contract receiver {
|
||||
uint public received;
|
||||
function receive(uint x) public { received += x + 1; }
|
||||
function() external { received = 0x80; }
|
||||
function recv(uint x) public { received += x + 1; }
|
||||
fallback() external { received = 0x80; }
|
||||
}
|
||||
contract sender {
|
||||
constructor() public { rec = new receiver(); }
|
||||
function() external { savedData = msg.data; }
|
||||
fallback() external { savedData = msg.data; }
|
||||
function forward() public returns (bool) { address(rec).call(savedData); return true; }
|
||||
function clear() public returns (bool) { delete savedData; return true; }
|
||||
function val() public returns (uint) { return rec.received(); }
|
||||
@ -3602,7 +3563,7 @@ BOOST_AUTO_TEST_CASE(call_forward_bytes)
|
||||
}
|
||||
)";
|
||||
compileAndRun(sourceCode, 0, "sender");
|
||||
ABI_CHECK(callContractFunction("receive(uint256)", 7), bytes());
|
||||
ABI_CHECK(callContractFunction("recv(uint256)", 7), bytes());
|
||||
ABI_CHECK(callContractFunction("val()"), encodeArgs(0));
|
||||
ABI_CHECK(callContractFunction("forward()"), encodeArgs(true));
|
||||
ABI_CHECK(callContractFunction("val()"), encodeArgs(8));
|
||||
@ -3617,7 +3578,7 @@ BOOST_AUTO_TEST_CASE(call_forward_bytes_length)
|
||||
char const* sourceCode = R"(
|
||||
contract receiver {
|
||||
uint public calledLength;
|
||||
function() external { calledLength = msg.data.length; }
|
||||
fallback() external { calledLength = msg.data.length; }
|
||||
}
|
||||
contract sender {
|
||||
receiver rec;
|
||||
@ -3661,12 +3622,12 @@ BOOST_AUTO_TEST_CASE(copying_bytes_multiassign)
|
||||
char const* sourceCode = R"(
|
||||
contract receiver {
|
||||
uint public received;
|
||||
function receive(uint x) public { received += x + 1; }
|
||||
function() external { received = 0x80; }
|
||||
function recv(uint x) public { received += x + 1; }
|
||||
fallback() external { received = 0x80; }
|
||||
}
|
||||
contract sender {
|
||||
constructor() public { rec = new receiver(); }
|
||||
function() external { savedData1 = savedData2 = msg.data; }
|
||||
fallback() external { savedData1 = savedData2 = msg.data; }
|
||||
function forward(bool selector) public returns (bool) {
|
||||
if (selector) { address(rec).call(savedData1); delete savedData1; }
|
||||
else { address(rec).call(savedData2); delete savedData2; }
|
||||
@ -3679,7 +3640,7 @@ BOOST_AUTO_TEST_CASE(copying_bytes_multiassign)
|
||||
}
|
||||
)";
|
||||
compileAndRun(sourceCode, 0, "sender");
|
||||
ABI_CHECK(callContractFunction("receive(uint256)", 7), bytes());
|
||||
ABI_CHECK(callContractFunction("recv(uint256)", 7), bytes());
|
||||
ABI_CHECK(callContractFunction("val()"), encodeArgs(0));
|
||||
ABI_CHECK(callContractFunction("forward(bool)", true), encodeArgs(true));
|
||||
ABI_CHECK(callContractFunction("val()"), encodeArgs(8));
|
||||
@ -3693,7 +3654,7 @@ BOOST_AUTO_TEST_CASE(delete_removes_bytes_data)
|
||||
{
|
||||
char const* sourceCode = R"(
|
||||
contract c {
|
||||
function() external { data = msg.data; }
|
||||
fallback() external { data = msg.data; }
|
||||
function del() public returns (bool) { delete data; return true; }
|
||||
bytes data;
|
||||
}
|
||||
@ -3710,7 +3671,7 @@ BOOST_AUTO_TEST_CASE(copy_from_calldata_removes_bytes_data)
|
||||
char const* sourceCode = R"(
|
||||
contract c {
|
||||
function set() public returns (bool) { data = msg.data; return true; }
|
||||
function() external { data = msg.data; }
|
||||
fallback() external { data = msg.data; }
|
||||
bytes data;
|
||||
}
|
||||
)";
|
||||
@ -6205,7 +6166,7 @@ BOOST_AUTO_TEST_CASE(failing_send)
|
||||
char const* sourceCode = R"(
|
||||
contract Helper {
|
||||
uint[] data;
|
||||
function () external {
|
||||
fallback () external {
|
||||
data[9]; // trigger exception
|
||||
}
|
||||
}
|
||||
@ -6225,11 +6186,11 @@ BOOST_AUTO_TEST_CASE(failing_send)
|
||||
|
||||
BOOST_AUTO_TEST_CASE(send_zero_ether)
|
||||
{
|
||||
// Sending zero ether to a contract should still invoke the fallback function
|
||||
// Sending zero ether to a contract should still invoke the receive ether function
|
||||
// (it previously did not because the gas stipend was not provided by the EVM)
|
||||
char const* sourceCode = R"(
|
||||
contract Receiver {
|
||||
function () external payable {
|
||||
receive () external payable {
|
||||
}
|
||||
}
|
||||
contract Main {
|
||||
@ -8804,7 +8765,7 @@ BOOST_AUTO_TEST_CASE(reject_ether_sent_to_library)
|
||||
function f(address payable x) public returns (bool) {
|
||||
return x.send(1);
|
||||
}
|
||||
function () external payable {}
|
||||
receive () external payable {}
|
||||
}
|
||||
)";
|
||||
compileAndRun(sourceCode, 0, "lib");
|
||||
@ -10461,7 +10422,7 @@ BOOST_AUTO_TEST_CASE(mutex)
|
||||
else
|
||||
return fund.withdrawUnprotected(10);
|
||||
}
|
||||
function() external payable {
|
||||
fallback() external payable {
|
||||
callDepth++;
|
||||
if (callDepth < 4)
|
||||
attackInternal();
|
||||
@ -10526,7 +10487,7 @@ BOOST_AUTO_TEST_CASE(payable_function)
|
||||
function f() payable public returns (uint) {
|
||||
return msg.value;
|
||||
}
|
||||
function() external payable {
|
||||
fallback() external payable {
|
||||
a = msg.value + 1;
|
||||
}
|
||||
}
|
||||
@ -10568,7 +10529,7 @@ BOOST_AUTO_TEST_CASE(non_payable_throw)
|
||||
function msgvalue() internal returns (uint) {
|
||||
return msg.value;
|
||||
}
|
||||
function() external {
|
||||
fallback() external {
|
||||
update();
|
||||
}
|
||||
function update() internal {
|
||||
@ -12312,7 +12273,7 @@ BOOST_AUTO_TEST_CASE(bubble_up_error_messages_through_transfer)
|
||||
{
|
||||
char const* sourceCode = R"(
|
||||
contract D {
|
||||
function() external payable {
|
||||
receive() external payable {
|
||||
revert("message");
|
||||
}
|
||||
function f() public {
|
||||
@ -12469,7 +12430,7 @@ BOOST_AUTO_TEST_CASE(interface_contract)
|
||||
interface I {
|
||||
event A();
|
||||
function f() external returns (bool);
|
||||
function() external payable;
|
||||
fallback() external payable;
|
||||
}
|
||||
|
||||
contract A is I {
|
||||
@ -12481,7 +12442,7 @@ BOOST_AUTO_TEST_CASE(interface_contract)
|
||||
return true;
|
||||
}
|
||||
|
||||
function() external payable {
|
||||
fallback() override external payable {
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2,15 +2,15 @@ contract Small {
|
||||
uint public a;
|
||||
uint[] public b;
|
||||
function f1(uint x) public returns (uint) { a = x; b[uint8(msg.data[0])] = x; }
|
||||
function () external payable {}
|
||||
fallback () external payable {}
|
||||
}
|
||||
// ----
|
||||
// creation:
|
||||
// codeDepositCost: 83800
|
||||
// codeDepositCost: 84800
|
||||
// executionCost: 135
|
||||
// totalCost: 83935
|
||||
// totalCost: 84935
|
||||
// external:
|
||||
// fallback: 118
|
||||
// fallback: 129
|
||||
// a(): 383
|
||||
// b(uint256): 802
|
||||
// f1(uint256): 40663
|
||||
|
@ -2,7 +2,7 @@ contract Small {
|
||||
uint public a;
|
||||
uint[] public b;
|
||||
function f1(uint x) public returns (uint) { a = x; b[uint8(msg.data[0])] = x; }
|
||||
function () external payable {}
|
||||
fallback () external payable {}
|
||||
}
|
||||
// ====
|
||||
// optimize: true
|
||||
|
@ -1,5 +1,5 @@
|
||||
contract TransferTest {
|
||||
function() external payable {
|
||||
fallback() external payable {
|
||||
// This used to cause an ICE
|
||||
address(this).transfer;
|
||||
}
|
||||
|
@ -0,0 +1,17 @@
|
||||
contract C {
|
||||
uint x;
|
||||
uint y;
|
||||
fallback () payable external { ++x; }
|
||||
receive () payable external { ++y; }
|
||||
function f() external returns (uint, uint) { return (x, y); }
|
||||
}
|
||||
// ----
|
||||
// f() -> 0, 0
|
||||
// () ->
|
||||
// f() -> 0, 1
|
||||
// (), 1 ether ->
|
||||
// f() -> 0, 2
|
||||
// (): 1 ->
|
||||
// f() -> 1, 2
|
||||
// (), 1 ether: 1 ->
|
||||
// f() -> 2, 2
|
10
test/libsolidity/semanticTests/fallback/inherited.sol
Normal file
10
test/libsolidity/semanticTests/fallback/inherited.sol
Normal file
@ -0,0 +1,10 @@
|
||||
contract A {
|
||||
uint data;
|
||||
fallback() external { data = 1; }
|
||||
function getData() public returns (uint r) { return data; }
|
||||
}
|
||||
contract B is A {}
|
||||
// ----
|
||||
// getData() -> 0
|
||||
// (): 42 ->
|
||||
// getData() -> 1
|
@ -0,0 +1,17 @@
|
||||
contract A {
|
||||
uint public x;
|
||||
// Signature is d88e0b00
|
||||
function fow() public { x = 3; }
|
||||
fallback () external { x = 2; }
|
||||
}
|
||||
// ----
|
||||
// (): hex"d88e0b"
|
||||
// x() -> 2
|
||||
// (): hex"d88e0b00"
|
||||
// x() -> 3
|
||||
// (): hex"d88e"
|
||||
// x() -> 2
|
||||
// (): hex"d88e0b00"
|
||||
// x() -> 3
|
||||
// (): hex"d8"
|
||||
// x() -> 2
|
@ -0,0 +1,12 @@
|
||||
contract A {
|
||||
uint public x;
|
||||
receive () external payable { ++x; }
|
||||
}
|
||||
// ----
|
||||
// x() -> 0
|
||||
// ()
|
||||
// x() -> 1
|
||||
// (), 1 ether
|
||||
// x() -> 2
|
||||
// (): hex"00" -> FAILURE
|
||||
// (), 1 ether: hex"00" -> FAILURE
|
@ -0,0 +1,6 @@
|
||||
contract C {
|
||||
receive () payable external { }
|
||||
}
|
||||
// ----
|
||||
// (), 1 ether
|
||||
// (), 1 ether: 1 -> FAILURE
|
12
test/libsolidity/semanticTests/receive/inherited.sol
Normal file
12
test/libsolidity/semanticTests/receive/inherited.sol
Normal file
@ -0,0 +1,12 @@
|
||||
contract A {
|
||||
uint data;
|
||||
receive() external payable { ++data; }
|
||||
function getData() public returns (uint r) { return data; }
|
||||
}
|
||||
contract B is A {}
|
||||
// ----
|
||||
// getData() -> 0
|
||||
// () ->
|
||||
// getData() -> 1
|
||||
// (), 1 ether ->
|
||||
// getData() -> 2
|
@ -2,7 +2,7 @@ contract A {
|
||||
uint public data;
|
||||
uint public balance;
|
||||
bytes public externalData;
|
||||
function() external payable {
|
||||
fallback() external payable {
|
||||
data += 1;
|
||||
balance = msg.value;
|
||||
externalData = msg.data;
|
||||
|
@ -25,7 +25,7 @@ contract B is A {
|
||||
|
||||
address public mySistersAddress = 0x999999cf1046e68e36E1aA2E0E07105eDDD1f08E;
|
||||
|
||||
function () external {
|
||||
fallback () external {
|
||||
used = 0;
|
||||
}
|
||||
|
||||
|
5
test/libsolidity/syntaxTests/fallback/arguments.sol
Normal file
5
test/libsolidity/syntaxTests/fallback/arguments.sol
Normal file
@ -0,0 +1,5 @@
|
||||
contract C {
|
||||
fallback(uint256) external {}
|
||||
}
|
||||
// ----
|
||||
// TypeError: (25-34): Fallback function cannot take parameters.
|
@ -1,6 +1,6 @@
|
||||
contract C {
|
||||
// Check that visibility is also enforced for the fallback function.
|
||||
function() {}
|
||||
fallback() {}
|
||||
}
|
||||
// ----
|
||||
// SyntaxError: (90-103): No visibility specified. Did you intend to add "external"?
|
||||
|
@ -0,0 +1,5 @@
|
||||
contract C {
|
||||
function fallback() external pure {}
|
||||
}
|
||||
// ----
|
||||
// Warning: (26-34): This function is named "fallback" but is not the fallback function of the contract. If you intend this to be a fallback function, use "fallback(...) { ... }" without the "function" keyword to define it.
|
5
test/libsolidity/syntaxTests/fallback/old_syntax.sol
Normal file
5
test/libsolidity/syntaxTests/fallback/old_syntax.sol
Normal file
@ -0,0 +1,5 @@
|
||||
contract C {
|
||||
function() external {}
|
||||
}
|
||||
// ----
|
||||
// ParserError: (37-38): Expected a state variable declaration. If you intended this as a fallback function or a function to handle plain ether transactions, use the "fallback" keyword or the "ether" keyword instead.
|
@ -0,0 +1,6 @@
|
||||
contract A {
|
||||
receive() external payable { }
|
||||
}
|
||||
contract C is A {
|
||||
fallback() external payable { }
|
||||
}
|
@ -0,0 +1,3 @@
|
||||
contract C {
|
||||
fallback() external payable { }
|
||||
}
|
@ -0,0 +1,6 @@
|
||||
contract C {
|
||||
fallback() external payable { }
|
||||
function f() public pure { }
|
||||
}
|
||||
// ----
|
||||
// Warning: (0-83): This contract has a payable fallback function, but no receive ether function. Consider adding a receive ether function.
|
@ -0,0 +1,8 @@
|
||||
contract A {
|
||||
function f() external pure {}
|
||||
}
|
||||
contract C is A {
|
||||
fallback() external payable { }
|
||||
}
|
||||
// ----
|
||||
// Warning: (49-104): This contract has a payable fallback function, but no receive ether function. Consider adding a receive ether function.
|
@ -0,0 +1,4 @@
|
||||
contract C {
|
||||
fallback() external payable { }
|
||||
function f() internal pure { }
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
contract C {
|
||||
uint x;
|
||||
function() external pure { x = 2; }
|
||||
fallback() external pure { x = 2; }
|
||||
}
|
||||
// ----
|
||||
// TypeError: (29-64): Fallback function must be payable or non-payable, but is "pure".
|
||||
|
@ -0,0 +1,5 @@
|
||||
contract C {
|
||||
fallback() external returns (bytes memory, bytes memory) {}
|
||||
}
|
||||
// ----
|
||||
// TypeError: (45-73): Fallback function can only have a single "bytes memory" return value.
|
@ -0,0 +1,5 @@
|
||||
contract C {
|
||||
fallback() external returns (uint256) {}
|
||||
}
|
||||
// ----
|
||||
// TypeError: (45-54): Fallback function can only have a single "bytes memory" return value.
|
@ -0,0 +1,5 @@
|
||||
contract C {
|
||||
fallback() external returns (bytes memory) {}
|
||||
}
|
||||
// ----
|
||||
// TypeError: (45-59): Return values for fallback functions are not yet implemented.
|
@ -1,6 +1,6 @@
|
||||
contract C {
|
||||
uint x;
|
||||
function() external view { x = 2; }
|
||||
fallback() external view { x = 2; }
|
||||
}
|
||||
// ----
|
||||
// TypeError: (29-64): Fallback function must be payable or non-payable, but is "view".
|
||||
|
@ -1,4 +1,4 @@
|
||||
==== Source: a ====
|
||||
contract A {}
|
||||
==== Source: dir/a/b/c ====
|
||||
import "../../.././a" as x; contract B is x.A { function() external { x.A r = x.A(20); r; } }
|
||||
import "../../.././a" as x; contract B is x.A { fallback() external { x.A r = x.A(20); r; } }
|
||||
|
@ -0,0 +1,8 @@
|
||||
contract C {
|
||||
receive() external payable {}
|
||||
}
|
||||
contract D is C {
|
||||
fallback() override external {}
|
||||
}
|
||||
// ----
|
||||
// TypeError: (76-84): Function has override specified but does not override anything.
|
@ -0,0 +1,6 @@
|
||||
contract C {
|
||||
fallback() external {}
|
||||
}
|
||||
contract D is C {
|
||||
fallback() override external {}
|
||||
}
|
@ -0,0 +1,8 @@
|
||||
contract C {
|
||||
fallback() external {}
|
||||
}
|
||||
contract D is C {
|
||||
}
|
||||
contract E is D {
|
||||
fallback() override external {}
|
||||
}
|
@ -0,0 +1,8 @@
|
||||
contract C {
|
||||
fallback() external {}
|
||||
}
|
||||
contract D is C {
|
||||
fallback() external {}
|
||||
}
|
||||
// ----
|
||||
// TypeError: (58-80): Overriding function is missing 'override' specifier.
|
@ -0,0 +1,10 @@
|
||||
contract C {
|
||||
fallback() external {}
|
||||
}
|
||||
contract D is C {
|
||||
}
|
||||
contract E is D {
|
||||
fallback() external {}
|
||||
}
|
||||
// ----
|
||||
// TypeError: (78-100): Overriding function is missing 'override' specifier.
|
@ -0,0 +1,8 @@
|
||||
contract C {
|
||||
fallback() external {}
|
||||
}
|
||||
contract D is C {
|
||||
receive() override external payable {}
|
||||
}
|
||||
// ----
|
||||
// TypeError: (68-76): Function has override specified but does not override anything.
|
@ -0,0 +1,6 @@
|
||||
contract C {
|
||||
receive() external payable {}
|
||||
}
|
||||
contract D is C {
|
||||
receive() override external payable {}
|
||||
}
|
@ -0,0 +1,8 @@
|
||||
contract C {
|
||||
receive() external payable {}
|
||||
}
|
||||
contract D is C {
|
||||
}
|
||||
contract E is D {
|
||||
receive() override external payable {}
|
||||
}
|
@ -0,0 +1,8 @@
|
||||
contract C {
|
||||
receive() external payable {}
|
||||
}
|
||||
contract D is C {
|
||||
receive() external payable {}
|
||||
}
|
||||
// ----
|
||||
// TypeError: (65-94): Overriding function is missing 'override' specifier.
|
@ -0,0 +1,10 @@
|
||||
contract C {
|
||||
receive() external payable {}
|
||||
}
|
||||
contract D is C {
|
||||
}
|
||||
contract E is D {
|
||||
receive() external payable {}
|
||||
}
|
||||
// ----
|
||||
// TypeError: (85-114): Overriding function is missing 'override' specifier.
|
@ -1,6 +1,6 @@
|
||||
contract C {
|
||||
uint[] x;
|
||||
function() external {
|
||||
fallback() external {
|
||||
uint[] storage y = x;
|
||||
assembly {
|
||||
pop(y)
|
||||
|
@ -1,6 +1,6 @@
|
||||
contract C {
|
||||
uint[] x;
|
||||
function() external {
|
||||
fallback() external {
|
||||
uint[] storage y = x;
|
||||
assembly {
|
||||
y_slot := 1
|
||||
|
@ -1,6 +1,6 @@
|
||||
contract C {
|
||||
uint[] x;
|
||||
function() external {
|
||||
fallback() external {
|
||||
uint[] storage y = x;
|
||||
assembly {
|
||||
pop(y_slot)
|
||||
|
@ -1,6 +1,6 @@
|
||||
contract C {
|
||||
uint[] x;
|
||||
function() external {
|
||||
fallback() external {
|
||||
uint[] memory y = x;
|
||||
assembly {
|
||||
pop(y_slot)
|
||||
|
@ -1,4 +1,4 @@
|
||||
contract C {
|
||||
uint x;
|
||||
function() external { x = 2; }
|
||||
fallback() external { x = 2; }
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
contract C {
|
||||
uint x;
|
||||
function(uint a) external { x = 2; }
|
||||
fallback(uint a) external { x = 2; }
|
||||
}
|
||||
// ----
|
||||
// TypeError: (37-45): Fallback function cannot take parameters.
|
||||
|
@ -1,5 +1,5 @@
|
||||
library C {
|
||||
function() external {}
|
||||
fallback() external {}
|
||||
}
|
||||
// ----
|
||||
// TypeError: (16-38): Libraries cannot have fallback functions.
|
||||
|
@ -0,0 +1,6 @@
|
||||
library C {
|
||||
receive() external payable {}
|
||||
}
|
||||
// ----
|
||||
// TypeError: (16-45): Library functions cannot be payable.
|
||||
// TypeError: (16-45): Libraries cannot have receive ether functions.
|
@ -1,5 +1,5 @@
|
||||
contract C {
|
||||
function() external returns (uint) { }
|
||||
fallback() external returns (uint) { }
|
||||
}
|
||||
// ----
|
||||
// TypeError: (45-51): Fallback function cannot return values.
|
||||
// TypeError: (45-51): Fallback function can only have a single "bytes memory" return value.
|
||||
|
@ -1,7 +1,7 @@
|
||||
contract C {
|
||||
uint x;
|
||||
function() external { x = 2; }
|
||||
function() external { x = 3; }
|
||||
fallback() external { x = 2; }
|
||||
fallback() external { x = 3; }
|
||||
}
|
||||
// ----
|
||||
// DeclarationError: (64-94): Only one fallback function is allowed.
|
||||
|
@ -1,7 +1,7 @@
|
||||
contract A {
|
||||
uint x;
|
||||
function() external { x = 1; }
|
||||
fallback() external { x = 1; }
|
||||
}
|
||||
contract C is A {
|
||||
function() override external { x = 2; }
|
||||
fallback() override external { x = 2; }
|
||||
}
|
||||
|
@ -1,3 +1,3 @@
|
||||
contract test { function() external { uint x = 1; uint y = 2; x || y; } }
|
||||
contract test { fallback() external { uint x = 1; uint y = 2; x || y; } }
|
||||
// ----
|
||||
// TypeError: (62-68): Operator || not compatible with types uint256 and uint256
|
||||
|
@ -1,3 +1,3 @@
|
||||
contract test { function() external { uint x = 1; uint y = 2; x && y; } }
|
||||
contract test { fallback() external { uint x = 1; uint y = 2; x && y; } }
|
||||
// ----
|
||||
// TypeError: (62-68): Operator && not compatible with types uint256 and uint256
|
||||
|
@ -1,3 +1,3 @@
|
||||
contract test { function() external { uint x = 1; !x; } }
|
||||
contract test { fallback() external { uint x = 1; !x; } }
|
||||
// ----
|
||||
// TypeError: (50-52): Unary operator ! cannot be applied to type uint256
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user