Allow fallback function to return data.

This commit is contained in:
chriseth 2020-11-05 18:03:32 +01:00
parent b62de4f16d
commit fda352094f
15 changed files with 128 additions and 21 deletions

View File

@ -1831,15 +1831,21 @@ void TypeChecker::typeCheckFallbackFunction(FunctionDefinition const& _function)
); );
if (_function.visibility() != Visibility::External) if (_function.visibility() != Visibility::External)
m_errorReporter.typeError(1159_error, _function.location(), "Fallback function must be defined as \"external\"."); m_errorReporter.typeError(1159_error, _function.location(), "Fallback function must be defined as \"external\".");
if (!_function.returnParameters().empty())
if (!_function.returnParameters().empty() || !_function.parameters().empty())
{ {
if (_function.returnParameters().size() > 1 || *type(*_function.returnParameters().front()) != *TypeProvider::bytesMemory()) if (
m_errorReporter.typeError(5570_error, _function.returnParameterList()->location(), "Fallback function can only have a single \"bytes memory\" return value."); _function.returnParameters().size() != 1 ||
else *type(*_function.returnParameters().front()) != *TypeProvider::bytesMemory() ||
m_errorReporter.typeError(6151_error, _function.returnParameterList()->location(), "Return values for fallback functions are not yet implemented."); _function.parameters().size() != 1 ||
*type(*_function.parameters().front()) != *TypeProvider::bytesCalldata()
)
m_errorReporter.typeError(
5570_error,
_function.returnParameterList()->location(),
"Fallback function either has to have the signature \"fallback()\" or \"fallback(bytes calldata) returns (bytes memory)\"."
);
} }
if (!_function.parameters().empty())
m_errorReporter.typeError(3978_error, _function.parameterList().location(), "Fallback function cannot take parameters.");
} }
void TypeChecker::typeCheckReceiveFunction(FunctionDefinition const& _function) void TypeChecker::typeCheckReceiveFunction(FunctionDefinition const& _function)

View File

@ -467,10 +467,21 @@ void ContractCompiler::appendFunctionSelector(ContractDefinition const& _contrac
appendCallValueCheck(); appendCallValueCheck();
solAssert(fallback->isFallback(), ""); solAssert(fallback->isFallback(), "");
solAssert(FunctionType(*fallback).parameterTypes().empty(), ""); m_context.setStackOffset(0);
solAssert(FunctionType(*fallback).returnParameterTypes().empty(), "");
if (!FunctionType(*fallback).parameterTypes().empty())
m_context << u256(0) << Instruction::CALLDATASIZE;
fallback->accept(*this); fallback->accept(*this);
m_context << Instruction::STOP;
if (FunctionType(*fallback).returnParameterTypes().empty())
m_context << Instruction::STOP;
else
{
m_context << Instruction::DUP1 << Instruction::MLOAD << Instruction::SWAP1;
m_context << u256(0x20) << Instruction::ADD;
m_context << Instruction::RETURN;
}
} }
else else
m_context.appendRevert("Unknown signature and no fallback defined"); m_context.appendRevert("Unknown signature and no fallback defined");
@ -479,6 +490,7 @@ void ContractCompiler::appendFunctionSelector(ContractDefinition const& _contrac
for (auto const& it: interfaceFunctions) for (auto const& it: interfaceFunctions)
{ {
m_context.setStackOffset(1);
FunctionTypePointer const& functionType = it.second; FunctionTypePointer const& functionType = it.second;
solAssert(functionType->hasDeclaration(), ""); solAssert(functionType->hasDeclaration(), "");
CompilerContext::LocationSetter locationSetter(m_context, functionType->declaration()); CompilerContext::LocationSetter locationSetter(m_context, functionType->declaration());
@ -588,7 +600,9 @@ bool ContractCompiler::visit(FunctionDefinition const& _function)
// reserve additional slots: [retarg0] ... [retargm] // reserve additional slots: [retarg0] ... [retargm]
unsigned parametersSize = CompilerUtils::sizeOnStack(_function.parameters()); unsigned parametersSize = CompilerUtils::sizeOnStack(_function.parameters());
if (!_function.isConstructor()) if (_function.isFallback())
m_context.adjustStackOffset(static_cast<int>(parametersSize));
else if (!_function.isConstructor())
// adding 1 for return address. // adding 1 for return address.
m_context.adjustStackOffset(static_cast<int>(parametersSize) + 1); m_context.adjustStackOffset(static_cast<int>(parametersSize) + 1);
for (ASTPointer<VariableDeclaration> const& variable: _function.parameters()) for (ASTPointer<VariableDeclaration> const& variable: _function.parameters())
@ -628,7 +642,8 @@ bool ContractCompiler::visit(FunctionDefinition const& _function)
unsigned const c_returnValuesSize = CompilerUtils::sizeOnStack(_function.returnParameters()); unsigned const c_returnValuesSize = CompilerUtils::sizeOnStack(_function.returnParameters());
vector<int> stackLayout; vector<int> stackLayout;
stackLayout.push_back(static_cast<int>(c_returnValuesSize)); // target of return address if (!_function.isConstructor() && !_function.isFallback())
stackLayout.push_back(static_cast<int>(c_returnValuesSize)); // target of return address
stackLayout += vector<int>(c_argumentsSize, -1); // discard all arguments stackLayout += vector<int>(c_argumentsSize, -1); // discard all arguments
for (size_t i = 0; i < c_returnValuesSize; ++i) for (size_t i = 0; i < c_returnValuesSize; ++i)
stackLayout.push_back(static_cast<int>(i)); stackLayout.push_back(static_cast<int>(i));
@ -639,7 +654,7 @@ bool ContractCompiler::visit(FunctionDefinition const& _function)
errinfo_sourceLocation(_function.location()) << errinfo_sourceLocation(_function.location()) <<
errinfo_comment("Stack too deep, try removing local variables.") errinfo_comment("Stack too deep, try removing local variables.")
); );
while (stackLayout.back() != static_cast<int>(stackLayout.size() - 1)) while (!stackLayout.empty() && stackLayout.back() != static_cast<int>(stackLayout.size() - 1))
if (stackLayout.back() < 0) if (stackLayout.back() < 0)
{ {
m_context << Instruction::POP; m_context << Instruction::POP;

View File

@ -652,8 +652,16 @@ string IRGenerator::dispatchRoutine(ContractDefinition const& _contract)
{ {
string fallbackCode; string fallbackCode;
if (!fallback->isPayable()) if (!fallback->isPayable())
fallbackCode += callValueCheck(); fallbackCode += callValueCheck() + "\n";
fallbackCode += m_context.enqueueFunctionForCodeGeneration(*fallback) + "() stop()"; if (fallback->parameters().empty())
fallbackCode += m_context.enqueueFunctionForCodeGeneration(*fallback) + "() stop()";
else
{
solAssert(fallback->parameters().size() == 1 && fallback->returnParameters().size() == 1, "");
fallbackCode += "let retval := " + m_context.enqueueFunctionForCodeGeneration(*fallback) + "(0, calldatasize())\n";
fallbackCode += "return(add(retval, 0x20), mload(retval))\n";
}
t("fallback", fallbackCode); t("fallback", fallbackCode);
} }

View File

@ -0,0 +1,18 @@
contract A {
uint public x;
fallback () external {
if (x == 2) return;
x++;
}
}
// ====
// compileViaYul: also
// ----
// ()
// x() -> 1
// ()
// x() -> 2
// ()
// x() -> 2
// ()
// x() -> 2

View File

@ -0,0 +1,17 @@
contract A {
uint public x;
fallback (bytes calldata _input) external returns (bytes memory) {
x = _input.length;
return "";
}
function f() public returns (bool, bytes memory) {
(bool success, bytes memory retval) = address(this).call("abc");
return (success, retval);
}
}
// ====
// compileViaYul: also
// EVMVersion: >=byzantium
// ----
// f() -> 0x01, 0x40, 0x00
// x() -> 3

View File

@ -0,0 +1,16 @@
contract A {
bytes public x;
fallback (bytes calldata _input) external returns (bytes memory) {
x = _input;
return "";
}
function f() public returns (bool, bytes memory) {
(bool success, bytes memory retval) = address(this).call("abc");
return (success, retval);
}
}
// ====
// EVMVersion: >=byzantium
// ----
// f() -> 0x01, 0x40, 0x00
// x() -> 0x20, 3, "abc"

View File

@ -0,0 +1,14 @@
contract A {
fallback (bytes calldata _input) external returns (bytes memory) {
return _input;
}
function f() public returns (bool, bytes memory) {
(bool success, bytes memory retval) = address(this).call("abc");
return (success, retval);
}
}
// ====
// compileViaYul: also
// EVMVersion: >=byzantium
// ----
// f() -> 0x01, 0x40, 0x03, 0x6162630000000000000000000000000000000000000000000000000000000000

View File

@ -2,4 +2,4 @@ contract C {
fallback(uint256) external {} fallback(uint256) external {}
} }
// ---- // ----
// TypeError 3978: (25-34): Fallback function cannot take parameters. // TypeError 5570: (44-44): Fallback function either has to have the signature "fallback()" or "fallback(bytes calldata) returns (bytes memory)".

View File

@ -0,0 +1,9 @@
contract C {
fallback() external returns (bytes memory _output) {}
}
contract D {
fallback(bytes calldata _input) external {}
}
// ----
// TypeError 5570: (45-67): Fallback function either has to have the signature "fallback()" or "fallback(bytes calldata) returns (bytes memory)".
// TypeError 5570: (131-131): Fallback function either has to have the signature "fallback()" or "fallback(bytes calldata) returns (bytes memory)".

View File

@ -2,4 +2,4 @@ contract C {
fallback() external returns (bytes memory, bytes memory) {} fallback() external returns (bytes memory, bytes memory) {}
} }
// ---- // ----
// TypeError 5570: (45-73): Fallback function can only have a single "bytes memory" return value. // TypeError 5570: (45-73): Fallback function either has to have the signature "fallback()" or "fallback(bytes calldata) returns (bytes memory)".

View File

@ -2,4 +2,4 @@ contract C {
fallback() external returns (uint256) {} fallback() external returns (uint256) {}
} }
// ---- // ----
// TypeError 5570: (45-54): Fallback function can only have a single "bytes memory" return value. // TypeError 5570: (45-54): Fallback function either has to have the signature "fallback()" or "fallback(bytes calldata) returns (bytes memory)".

View File

@ -2,4 +2,4 @@ contract C {
fallback() external returns (bytes memory) {} fallback() external returns (bytes memory) {}
} }
// ---- // ----
// TypeError 6151: (45-59): Return values for fallback functions are not yet implemented. // TypeError 5570: (45-59): Fallback function either has to have the signature "fallback()" or "fallback(bytes calldata) returns (bytes memory)".

View File

@ -0,0 +1,4 @@
contract C {
fallback(bytes calldata _input) external returns (bytes memory _output) {}
}
// ----

View File

@ -3,4 +3,4 @@ contract C {
fallback(uint a) external { x = 2; } fallback(uint a) external { x = 2; }
} }
// ---- // ----
// TypeError 3978: (37-45): Fallback function cannot take parameters. // TypeError 5570: (55-55): Fallback function either has to have the signature "fallback()" or "fallback(bytes calldata) returns (bytes memory)".

View File

@ -2,4 +2,4 @@ contract C {
fallback() external returns (uint) { } fallback() external returns (uint) { }
} }
// ---- // ----
// TypeError 5570: (45-51): Fallback function can only have a single "bytes memory" return value. // TypeError 5570: (45-51): Fallback function either has to have the signature "fallback()" or "fallback(bytes calldata) returns (bytes memory)".