mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Merge pull request #10214 from ethereum/fallbackReturn
Allow fallback function to return data.
This commit is contained in:
@@ -394,6 +394,10 @@ bool OverrideProxy::OverrideComparator::operator<(OverrideComparator const& _oth
|
||||
if (functionKind != _other.functionKind)
|
||||
return *functionKind < *_other.functionKind;
|
||||
|
||||
// Parameters do not matter for non-regular functions.
|
||||
if (functionKind != Token::Function)
|
||||
return false;
|
||||
|
||||
if (!parameterTypes || !_other.parameterTypes)
|
||||
return false;
|
||||
|
||||
@@ -574,16 +578,19 @@ void OverrideChecker::checkOverride(OverrideProxy const& _overriding, OverridePr
|
||||
FunctionType const* functionType = _overriding.functionType();
|
||||
FunctionType const* superType = _super.functionType();
|
||||
|
||||
solAssert(functionType->hasEqualParameterTypes(*superType), "Override doesn't have equal parameters!");
|
||||
if (_overriding.functionKind() != Token::Fallback)
|
||||
{
|
||||
solAssert(functionType->hasEqualParameterTypes(*superType), "Override doesn't have equal parameters!");
|
||||
|
||||
if (!functionType->hasEqualReturnTypes(*superType))
|
||||
overrideError(
|
||||
_overriding,
|
||||
_super,
|
||||
4822_error,
|
||||
"Overriding " + _overriding.astNodeName() + " return types differ.",
|
||||
"Overridden " + _overriding.astNodeName() + " is here:"
|
||||
);
|
||||
if (!functionType->hasEqualReturnTypes(*superType))
|
||||
overrideError(
|
||||
_overriding,
|
||||
_super,
|
||||
4822_error,
|
||||
"Overriding " + _overriding.astNodeName() + " return types differ.",
|
||||
"Overridden " + _overriding.astNodeName() + " is here:"
|
||||
);
|
||||
}
|
||||
|
||||
// Stricter mutability is always okay except when super is Payable
|
||||
if (
|
||||
|
||||
@@ -1831,15 +1831,21 @@ void TypeChecker::typeCheckFallbackFunction(FunctionDefinition const& _function)
|
||||
);
|
||||
if (_function.visibility() != Visibility::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())
|
||||
m_errorReporter.typeError(5570_error, _function.returnParameterList()->location(), "Fallback function can only have a single \"bytes memory\" return value.");
|
||||
else
|
||||
m_errorReporter.typeError(6151_error, _function.returnParameterList()->location(), "Return values for fallback functions are not yet implemented.");
|
||||
if (
|
||||
_function.returnParameters().size() != 1 ||
|
||||
*type(*_function.returnParameters().front()) != *TypeProvider::bytesMemory() ||
|
||||
_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)
|
||||
|
||||
@@ -467,10 +467,21 @@ void ContractCompiler::appendFunctionSelector(ContractDefinition const& _contrac
|
||||
appendCallValueCheck();
|
||||
|
||||
solAssert(fallback->isFallback(), "");
|
||||
solAssert(FunctionType(*fallback).parameterTypes().empty(), "");
|
||||
solAssert(FunctionType(*fallback).returnParameterTypes().empty(), "");
|
||||
m_context.setStackOffset(0);
|
||||
|
||||
if (!FunctionType(*fallback).parameterTypes().empty())
|
||||
m_context << u256(0) << Instruction::CALLDATASIZE;
|
||||
|
||||
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
|
||||
m_context.appendRevert("Unknown signature and no fallback defined");
|
||||
@@ -479,6 +490,7 @@ void ContractCompiler::appendFunctionSelector(ContractDefinition const& _contrac
|
||||
|
||||
for (auto const& it: interfaceFunctions)
|
||||
{
|
||||
m_context.setStackOffset(1);
|
||||
FunctionTypePointer const& functionType = it.second;
|
||||
solAssert(functionType->hasDeclaration(), "");
|
||||
CompilerContext::LocationSetter locationSetter(m_context, functionType->declaration());
|
||||
@@ -588,7 +600,9 @@ bool ContractCompiler::visit(FunctionDefinition const& _function)
|
||||
// reserve additional slots: [retarg0] ... [retargm]
|
||||
|
||||
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.
|
||||
m_context.adjustStackOffset(static_cast<int>(parametersSize) + 1);
|
||||
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());
|
||||
|
||||
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
|
||||
for (size_t i = 0; i < c_returnValuesSize; ++i)
|
||||
stackLayout.push_back(static_cast<int>(i));
|
||||
@@ -639,7 +654,7 @@ bool ContractCompiler::visit(FunctionDefinition const& _function)
|
||||
errinfo_sourceLocation(_function.location()) <<
|
||||
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)
|
||||
{
|
||||
m_context << Instruction::POP;
|
||||
|
||||
@@ -652,8 +652,16 @@ string IRGenerator::dispatchRoutine(ContractDefinition const& _contract)
|
||||
{
|
||||
string fallbackCode;
|
||||
if (!fallback->isPayable())
|
||||
fallbackCode += callValueCheck();
|
||||
fallbackCode += m_context.enqueueFunctionForCodeGeneration(*fallback) + "() stop()";
|
||||
fallbackCode += callValueCheck() + "\n";
|
||||
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);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user