Merge pull request #6814 from ethereum/yul-delete-operation

[Sol -> Yul] Implement delete for function pointers
This commit is contained in:
chriseth 2019-05-23 16:27:14 +02:00 committed by GitHub
commit 195a7ff61a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 114 additions and 3 deletions

View File

@ -1138,6 +1138,25 @@ string YulUtilFunctions::negateNumberCheckedFunction(Type const& _type)
});
}
string YulUtilFunctions::zeroValueFunction(Type const& _type)
{
solUnimplementedAssert(_type.sizeOnStack() == 1, "Stacksize not yet implemented!");
solUnimplementedAssert(_type.isValueType(), "Zero value for non-value types not yet implemented");
string const functionName = "zero_value_for_" + _type.identifier();
return m_functionCollector->createFunction(functionName, [&]() {
return Whiskers(R"(
function <functionName>() -> ret {
<body>
}
)")
("functionName", functionName)
("body", "ret := 0x0")
.render();
});
}
string YulUtilFunctions::conversionFunctionSpecial(Type const& _from, Type const& _to)
{
string functionName =

View File

@ -183,6 +183,9 @@ public:
std::string negateNumberCheckedFunction(Type const& _type);
/// @returns the name of a function that returns the zero value for the
/// provided type
std::string zeroValueFunction(Type const& _type);
private:
/// Special case of conversionFunction - handles everything that does not
/// use exactly one variable to hold the value.

View File

@ -114,7 +114,7 @@ string IRGenerationContext::internalDispatch(size_t _in, size_t _out)
string funName = "dispatch_internal_in_" + to_string(_in) + "_out_" + to_string(_out);
return m_functions->createFunction(funName, [&]() {
Whiskers templ(R"(
function <functionName>(fun <comma> <in>) -> <out> {
function <functionName>(fun <comma> <in>) <arrow> <out> {
switch fun
<#cases>
case <funID>
@ -129,6 +129,7 @@ string IRGenerationContext::internalDispatch(size_t _in, size_t _out)
templ("comma", _in > 0 ? "," : "");
YulUtilFunctions utils(m_evmVersion, m_functions);
templ("in", utils.suffixedVariableNameList("in_", 0, _in));
templ("arrow", _out > 0 ? "->" : "");
templ("out", utils.suffixedVariableNameList("out_", 0, _out));
vector<map<string, string>> functions;
for (auto const& contract: m_inheritanceHierarchy)
@ -138,10 +139,15 @@ string IRGenerationContext::internalDispatch(size_t _in, size_t _out)
function->parameters().size() == _in &&
function->returnParameters().size() == _out
)
{
// 0 is reserved for uninitialized function pointers
solAssert(function->id() != 0, "Unexpected function ID: 0");
functions.emplace_back(map<string, string> {
{ "funID", to_string(function->id()) },
{ "name", functionName(*function)}
});
}
templ("cases", move(functions));
return templ.render();
});

View File

@ -246,7 +246,13 @@ void IRGeneratorForStatements::endVisit(UnaryOperation const& _unaryOperation)
Type const& resultType = type(_unaryOperation);
Token const op = _unaryOperation.getOperator();
if (resultType.category() == Type::Category::RationalNumber)
if (op == Token::Delete)
{
solAssert(!!m_currentLValue, "LValue not retrieved.");
m_code << m_currentLValue->setToZero();
m_currentLValue.reset();
}
else if (resultType.category() == Type::Category::RationalNumber)
{
defineExpression(_unaryOperation) <<
formatNumber(resultType.literalValue(nullptr)) <<
@ -356,7 +362,12 @@ bool IRGeneratorForStatements::visit(BinaryOperation const& _binOp)
"\n";
else if (TokenTraits::isCompareOp(op))
{
solUnimplementedAssert(commonType->category() != Type::Category::Function, "");
if (auto type = dynamic_cast<FunctionType const*>(commonType))
{
solAssert(op == Token::Equal || op == Token::NotEqual, "Invalid function pointer comparison!");
solAssert(type->kind() != FunctionType::Kind::External, "External function comparison not allowed!");
}
solAssert(commonType->isValueType(), "");
bool isSigned = false;
if (auto type = dynamic_cast<IntegerType const*>(commonType))

View File

@ -22,6 +22,7 @@
#include <libsolidity/codegen/ir/IRGenerationContext.h>
#include <libsolidity/codegen/YulUtilFunctions.h>
#include <libsolidity/codegen/CompilerUtils.h>
#include <libsolidity/ast/AST.h>
#include <libdevcore/Whiskers.h>
@ -45,6 +46,11 @@ string IRLocalVariable::storeValue(string const& _value, Type const& _type) cons
return m_variableName + " := " + _value + "\n";
}
string IRLocalVariable::setToZero() const
{
return storeValue(m_context.utils().zeroValueFunction(*m_type) + "()", *m_type);
}
IRStorageItem::IRStorageItem(
IRGenerationContext& _context,
VariableDeclaration const& _varDecl
@ -110,3 +116,7 @@ string IRStorageItem::storeValue(string const& _value, Type const& _sourceType)
}
}
string IRStorageItem::setToZero() const
{
solUnimplemented("Delete for storage location not yet implemented");
}

View File

@ -51,6 +51,8 @@ public:
/// of type @a _type in the lvalue. Might perform type conversion.
virtual std::string storeValue(std::string const& _value, Type const& _type) const = 0;
/// Returns code that will reset the stored value to zero
virtual std::string setToZero() const = 0;
protected:
IRGenerationContext& m_context;
Type const* m_type;
@ -66,6 +68,7 @@ public:
std::string retrieveValue() const override { return m_variableName; }
std::string storeValue(std::string const& _value, Type const& _type) const override;
std::string setToZero() const override;
private:
std::string m_variableName;
};
@ -86,6 +89,7 @@ public:
std::string retrieveValue() const override;
std::string storeValue(std::string const& _value, Type const& _type) const override;
std::string setToZero() const override;
private:
std::string m_slot;
unsigned m_offset;

View File

@ -0,0 +1,29 @@
contract C {
function internal1() internal pure returns (bool) {
return true;
}
function internal2() internal pure returns (bool) {
return true;
}
function equal() public pure returns (bool same, bool diff, bool inv) {
function() internal pure returns (bool) invalid;
delete invalid;
same = internal1 == internal1;
diff = internal1 == internal2;
inv = internal1 == invalid;
}
function unequal() public pure returns (bool same, bool diff, bool inv) {
function() internal pure returns (bool) invalid;
delete invalid;
same = internal1 != internal1;
diff = internal1 != internal2;
inv = internal1 != invalid;
}
}
// ====
// compileViaYul: true
// ----
// equal() -> true, false, false
// unequal() -> false, true, true

View File

@ -0,0 +1,29 @@
contract C {
function internal_func() internal pure returns (int8)
{
return 1;
}
function call_internal_func() public pure returns (bool ret)
{
function() internal pure returns(int8) func = internal_func;
return func() == internal_func();
}
function call_deleted_internal_func() public pure returns (bool ret)
{
function() internal pure returns(int8) func = internal_func;
delete func;
return func() == internal_func();
}
function external_func() external pure returns (int8)
{
return 1;
}
}
// ====
// compileViaYul: true
// ----
// call_deleted_internal_func() -> FAILURE
// call_internal_func() -> true