Merge pull request #8721 from ethereum/sol-yul-internal-library-calls

[Sol->Yul] Calls to internal library functions
This commit is contained in:
chriseth 2020-04-24 15:34:41 +02:00 committed by GitHub
commit a85736d874
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 80 additions and 27 deletions

View File

@ -135,6 +135,11 @@ string IRGenerationContext::internalDispatch(size_t _in, size_t _out)
templ("arrow", _out > 0 ? "->" : "");
templ("assignment_op", _out > 0 ? ":=" : "");
templ("out", suffixedVariableNameList("out_", 0, _out));
// UNIMPLEMENTED: Internal library calls via pointers are not implemented yet.
// We're not generating code for internal library functions here even though it's possible
// to call them via pointers. Right now such calls end up triggering the `default` case in
// the switch above.
vector<map<string, string>> functions;
for (auto const& contract: mostDerivedContract().annotation().linearizedBaseContracts)
for (FunctionDefinition const* function: contract->definedFunctions())

View File

@ -75,15 +75,15 @@ pair<string, string> IRGenerator::run(ContractDefinition const& _contract)
string IRGenerator::generate(ContractDefinition const& _contract)
{
solUnimplementedAssert(!_contract.isLibrary(), "Libraries not yet implemented.");
Whiskers t(R"(
object "<CreationObject>" {
code {
<memoryInit>
<callValueCheck>
<?notLibrary>
<?constructorHasParams> let <constructorParams> := <copyConstructorArguments>() </constructorHasParams>
<implicitConstructor>(<constructorParams>)
</notLibrary>
<deploy>
<functions>
}
@ -101,6 +101,7 @@ string IRGenerator::generate(ContractDefinition const& _contract)
t("CreationObject", creationObjectName(_contract));
t("memoryInit", memoryInit());
t("notLibrary", !_contract.isLibrary());
FunctionDefinition const* constructor = _contract.constructor();
t("callValueCheck", !constructor || !constructor->isPayable() ? callValueCheck() : "");

View File

@ -567,6 +567,14 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall)
arguments.push_back(callArguments[std::distance(callArgumentNames.begin(), it)]);
}
if (auto memberAccess = dynamic_cast<MemberAccess const*>(&_functionCall.expression()))
if (auto expressionType = dynamic_cast<TypeType const*>(memberAccess->expression().annotation().type))
if (auto contractType = dynamic_cast<ContractType const*>(expressionType->actualType()))
solUnimplementedAssert(
!contractType->contractDefinition().isLibrary() || functionType->kind() == FunctionType::Kind::Internal,
"Only internal function calls implemented for libraries"
);
solUnimplementedAssert(!functionType->bound(), "");
switch (functionType->kind())
{
@ -579,32 +587,60 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall)
else
args.emplace_back(convert(*arguments[i], *parameterTypes[i]).commaSeparatedList());
if (auto identifier = dynamic_cast<Identifier const*>(&_functionCall.expression()))
optional<FunctionDefinition const*> functionDef;
if (auto memberAccess = dynamic_cast<MemberAccess const*>(&_functionCall.expression()))
{
solAssert(!functionType->bound(), "");
if (auto functionDef = dynamic_cast<FunctionDefinition const*>(identifier->annotation().referencedDeclaration))
solUnimplementedAssert(!functionType->bound(), "Internal calls to bound functions are not yet implemented for libraries and not allowed for contracts");
functionDef = dynamic_cast<FunctionDefinition const*>(memberAccess->annotation().referencedDeclaration);
if (functionDef.value() != nullptr)
solAssert(functionType->declaration() == *memberAccess->annotation().referencedDeclaration, "");
else
{
define(_functionCall) <<
m_context.enqueueFunctionForCodeGeneration(
functionDef->resolveVirtual(m_context.mostDerivedContract())
) <<
"(" <<
joinHumanReadable(args) <<
")\n";
return;
solAssert(dynamic_cast<VariableDeclaration const*>(memberAccess->annotation().referencedDeclaration), "");
solAssert(!functionType->hasDeclaration(), "");
}
}
else if (auto identifier = dynamic_cast<Identifier const*>(&_functionCall.expression()))
{
solAssert(!functionType->bound(), "");
define(_functionCall) <<
// NOTE: internalDispatch() takes care of adding the function to function generation queue
m_context.internalDispatch(
TupleType(functionType->parameterTypes()).sizeOnStack(),
TupleType(functionType->returnParameterTypes()).sizeOnStack()
) <<
"(" <<
IRVariable(_functionCall.expression()).part("functionIdentifier").name() <<
joinHumanReadablePrefixed(args) <<
")\n";
if (auto unresolvedFunctionDef = dynamic_cast<FunctionDefinition const*>(identifier->annotation().referencedDeclaration))
{
functionDef = &unresolvedFunctionDef->resolveVirtual(m_context.mostDerivedContract());
solAssert(functionType->declaration() == *identifier->annotation().referencedDeclaration, "");
}
else
{
functionDef = nullptr;
solAssert(dynamic_cast<VariableDeclaration const*>(identifier->annotation().referencedDeclaration), "");
solAssert(!functionType->hasDeclaration(), "");
}
}
else
// Not a simple expression like x or A.x
functionDef = nullptr;
solAssert(functionDef.has_value(), "");
solAssert(functionDef.value() == nullptr || functionDef.value()->isImplemented(), "");
if (functionDef.value() != nullptr)
define(_functionCall) <<
m_context.enqueueFunctionForCodeGeneration(*functionDef.value()) <<
"(" <<
joinHumanReadable(args) <<
")\n";
else
define(_functionCall) <<
// NOTE: internalDispatch() takes care of adding the function to function generation queue
m_context.internalDispatch(
TupleType(functionType->parameterTypes()).sizeOnStack(),
TupleType(functionType->returnParameterTypes()).sizeOnStack()
) <<
"(" <<
IRVariable(_functionCall.expression()).part("functionIdentifier").name() <<
joinHumanReadablePrefixed(args) <<
")\n";
break;
}
case FunctionType::Kind::External:
@ -1297,9 +1333,9 @@ void IRGeneratorForStatements::endVisit(Identifier const& _identifier)
define(_identifier) << to_string(functionDef->resolveVirtual(m_context.mostDerivedContract()).id()) << "\n";
else if (VariableDeclaration const* varDecl = dynamic_cast<VariableDeclaration const*>(declaration))
handleVariableReference(*varDecl, _identifier);
else if (auto contract = dynamic_cast<ContractDefinition const*>(declaration))
else if (dynamic_cast<ContractDefinition const*>(declaration))
{
solUnimplementedAssert(!contract->isLibrary(), "Libraries not yet supported.");
// no-op
}
else if (dynamic_cast<EventDefinition const*>(declaration))
{

View File

@ -29,8 +29,7 @@ class Expression;
/**
* An IRVariable refers to a set of yul variables that correspond to the stack layout of a Solidity variable or expression
* of a specific S
* olidity type. If the Solidity type occupies a single stack slot, the IRVariable refers to a single yul variable.
* of a specific Solidity type. If the Solidity type occupies a single stack slot, the IRVariable refers to a single yul variable.
* Otherwise the set of yul variables it refers to is (recursively) determined by @see ``Type::stackItems()``.
* For example, an IRVariable referring to a dynamically sized calldata array will consist of two parts named
* ``offset`` and ``length``, whereas an IRVariable referring to a statically sized calldata type, a storage reference

View File

@ -34,6 +34,8 @@ contract Child is Base {
BaseBase.init(c, d);
}
}
// ====
// compileViaYul: also
// ----
// x() -> 0
// y() -> 0

View File

@ -18,6 +18,8 @@ contract Child is Base {
Base.init(c, d);
}
}
// ====
// compileViaYul: also
// ----
// x() -> 0
// y() -> 0

View File

@ -15,5 +15,7 @@ contract B {
}
}
// ====
// compileViaYul: also
// ----
// g() -> 1

View File

@ -17,5 +17,7 @@ contract C {
}
}
// ====
// compileViaYul: also
// ----
// f() -> 2

View File

@ -22,5 +22,7 @@ contract C {
}
}
// ====
// compileViaYul: also
// ----
// f() -> 2

View File

@ -6,6 +6,8 @@ contract C {
return L.f(v);
}
}
// ====
// compileViaYul: also
// ----
// g(uint256): 1 -> 1
// g(uint256): 2 -> 4