Merge pull request #7385 from ethereum/fallbackSplit

Split fallback.
This commit is contained in:
chriseth 2019-11-04 17:38:21 +01:00 committed by GitHub
commit cb743d648a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
137 changed files with 1340 additions and 386 deletions

View File

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

View File

@ -437,7 +437,7 @@ New version:
function f(uint y) external {
x = y;
}
function() payable external {}
receive() payable external {}
}
contract New {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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 + "\"?"

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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())),

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -30,6 +30,7 @@ object \"C_6\" {
}
default { }
}
if iszero(calldatasize()) { }
revert(0, 0)
function abi_decode_tuple_(headStart, dataEnd)
{

View File

@ -45,6 +45,7 @@ object \"C_6\" {
default {}
}
if iszero(calldatasize()) { }
revert(0, 0)

View File

@ -88,8 +88,8 @@ contract MultiSigWallet {
_;
}
/// @dev Fallback function allows to deposit ether.
function()
/// @dev Receive function allows to deposit ether.
receive()
external
payable
{

View File

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

View File

@ -140,6 +140,6 @@ contract module {
require( msg.sender == moduleHandlerAddress );
_;
}
function() external payable {
receive() external payable {
}
}

View File

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

View File

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

View File

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

View File

@ -151,6 +151,6 @@ override returns (bool success) {
return 30;
}
}
function() external payable {
receive() external payable {
}
}

View File

@ -1,5 +1,5 @@
contract test {
function() external {}
fallback() external {}
}
// ----
// :test

View File

@ -1,5 +1,5 @@
contract test {
function () external payable {}
fallback () external payable {}
}
// ----
// :test

View File

@ -0,0 +1,16 @@
contract test {
receive() external payable {}
fallback() external {}
}
// ----
// :test
// [
// {
// "stateMutability": "nonpayable",
// "type": "fallback"
// },
// {
// "stateMutability": "payable",
// "type": "receive"
// }
// ]

View File

@ -0,0 +1,11 @@
contract test {
receive() external payable {}
}
// ----
// :test
// [
// {
// "stateMutability": "payable",
// "type": "receive"
// }
// ]

View File

@ -1,5 +1,5 @@
contract C {
function() external payable {
fallback() external payable {
}
}

View 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"
}

View File

@ -0,0 +1,8 @@
contract C {
receive() external payable {
}
fallback() external payable {
}
}
// ----

View 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"
}

View File

@ -1,5 +1,5 @@
contract C {
function() external {}
fallback() external {}
}
// ----

View 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"
}

View File

@ -0,0 +1,6 @@
contract C {
receive() external payable {
}
}
// ----

View 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"
}

View File

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

View File

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

View File

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

View File

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

View File

@ -1,5 +1,5 @@
contract TransferTest {
function() external payable {
fallback() external payable {
// This used to cause an ICE
address(this).transfer;
}

View File

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

View 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

View File

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

View File

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

View File

@ -0,0 +1,6 @@
contract C {
receive () payable external { }
}
// ----
// (), 1 ether
// (), 1 ether: 1 -> FAILURE

View 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

View File

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

View File

@ -25,7 +25,7 @@ contract B is A {
address public mySistersAddress = 0x999999cf1046e68e36E1aA2E0E07105eDDD1f08E;
function () external {
fallback () external {
used = 0;
}

View File

@ -0,0 +1,5 @@
contract C {
fallback(uint256) external {}
}
// ----
// TypeError: (25-34): Fallback function cannot take parameters.

View File

@ -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"?

View File

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

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

View File

@ -0,0 +1,6 @@
contract A {
receive() external payable { }
}
contract C is A {
fallback() external payable { }
}

View File

@ -0,0 +1,3 @@
contract C {
fallback() external payable { }
}

View File

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

View File

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

View File

@ -0,0 +1,4 @@
contract C {
fallback() external payable { }
function f() internal pure { }
}

View File

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

View File

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

View File

@ -0,0 +1,5 @@
contract C {
fallback() external returns (uint256) {}
}
// ----
// TypeError: (45-54): Fallback function can only have a single "bytes memory" return value.

View File

@ -0,0 +1,5 @@
contract C {
fallback() external returns (bytes memory) {}
}
// ----
// TypeError: (45-59): Return values for fallback functions are not yet implemented.

View File

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

View File

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

View File

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

View File

@ -0,0 +1,6 @@
contract C {
fallback() external {}
}
contract D is C {
fallback() override external {}
}

View File

@ -0,0 +1,8 @@
contract C {
fallback() external {}
}
contract D is C {
}
contract E is D {
fallback() override external {}
}

View File

@ -0,0 +1,8 @@
contract C {
fallback() external {}
}
contract D is C {
fallback() external {}
}
// ----
// TypeError: (58-80): Overriding function is missing 'override' specifier.

View File

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

View File

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

View File

@ -0,0 +1,6 @@
contract C {
receive() external payable {}
}
contract D is C {
receive() override external payable {}
}

View File

@ -0,0 +1,8 @@
contract C {
receive() external payable {}
}
contract D is C {
}
contract E is D {
receive() override external payable {}
}

View File

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

View File

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

View File

@ -1,6 +1,6 @@
contract C {
uint[] x;
function() external {
fallback() external {
uint[] storage y = x;
assembly {
pop(y)

View File

@ -1,6 +1,6 @@
contract C {
uint[] x;
function() external {
fallback() external {
uint[] storage y = x;
assembly {
y_slot := 1

View File

@ -1,6 +1,6 @@
contract C {
uint[] x;
function() external {
fallback() external {
uint[] storage y = x;
assembly {
pop(y_slot)

View File

@ -1,6 +1,6 @@
contract C {
uint[] x;
function() external {
fallback() external {
uint[] memory y = x;
assembly {
pop(y_slot)

View File

@ -1,4 +1,4 @@
contract C {
uint x;
function() external { x = 2; }
fallback() external { x = 2; }
}

View File

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

View File

@ -1,5 +1,5 @@
library C {
function() external {}
fallback() external {}
}
// ----
// TypeError: (16-38): Libraries cannot have fallback functions.

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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