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("arrow", _out > 0 ? "->" : "");
templ("assignment_op", _out > 0 ? ":=" : ""); templ("assignment_op", _out > 0 ? ":=" : "");
templ("out", suffixedVariableNameList("out_", 0, _out)); 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; vector<map<string, string>> functions;
for (auto const& contract: mostDerivedContract().annotation().linearizedBaseContracts) for (auto const& contract: mostDerivedContract().annotation().linearizedBaseContracts)
for (FunctionDefinition const* function: contract->definedFunctions()) 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) string IRGenerator::generate(ContractDefinition const& _contract)
{ {
solUnimplementedAssert(!_contract.isLibrary(), "Libraries not yet implemented.");
Whiskers t(R"( Whiskers t(R"(
object "<CreationObject>" { object "<CreationObject>" {
code { code {
<memoryInit> <memoryInit>
<callValueCheck> <callValueCheck>
<?notLibrary>
<?constructorHasParams> let <constructorParams> := <copyConstructorArguments>() </constructorHasParams> <?constructorHasParams> let <constructorParams> := <copyConstructorArguments>() </constructorHasParams>
<implicitConstructor>(<constructorParams>) <implicitConstructor>(<constructorParams>)
</notLibrary>
<deploy> <deploy>
<functions> <functions>
} }
@ -101,6 +101,7 @@ string IRGenerator::generate(ContractDefinition const& _contract)
t("CreationObject", creationObjectName(_contract)); t("CreationObject", creationObjectName(_contract));
t("memoryInit", memoryInit()); t("memoryInit", memoryInit());
t("notLibrary", !_contract.isLibrary());
FunctionDefinition const* constructor = _contract.constructor(); FunctionDefinition const* constructor = _contract.constructor();
t("callValueCheck", !constructor || !constructor->isPayable() ? callValueCheck() : ""); 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)]); 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(), ""); solUnimplementedAssert(!functionType->bound(), "");
switch (functionType->kind()) switch (functionType->kind())
{ {
@ -579,32 +587,60 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall)
else else
args.emplace_back(convert(*arguments[i], *parameterTypes[i]).commaSeparatedList()); 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(), ""); solUnimplementedAssert(!functionType->bound(), "Internal calls to bound functions are not yet implemented for libraries and not allowed for contracts");
if (auto functionDef = dynamic_cast<FunctionDefinition const*>(identifier->annotation().referencedDeclaration))
functionDef = dynamic_cast<FunctionDefinition const*>(memberAccess->annotation().referencedDeclaration);
if (functionDef.value() != nullptr)
solAssert(functionType->declaration() == *memberAccess->annotation().referencedDeclaration, "");
else
{ {
define(_functionCall) << solAssert(dynamic_cast<VariableDeclaration const*>(memberAccess->annotation().referencedDeclaration), "");
m_context.enqueueFunctionForCodeGeneration( solAssert(!functionType->hasDeclaration(), "");
functionDef->resolveVirtual(m_context.mostDerivedContract())
) <<
"(" <<
joinHumanReadable(args) <<
")\n";
return;
} }
} }
else if (auto identifier = dynamic_cast<Identifier const*>(&_functionCall.expression()))
{
solAssert(!functionType->bound(), "");
define(_functionCall) << if (auto unresolvedFunctionDef = dynamic_cast<FunctionDefinition const*>(identifier->annotation().referencedDeclaration))
// NOTE: internalDispatch() takes care of adding the function to function generation queue {
m_context.internalDispatch( functionDef = &unresolvedFunctionDef->resolveVirtual(m_context.mostDerivedContract());
TupleType(functionType->parameterTypes()).sizeOnStack(), solAssert(functionType->declaration() == *identifier->annotation().referencedDeclaration, "");
TupleType(functionType->returnParameterTypes()).sizeOnStack() }
) << else
"(" << {
IRVariable(_functionCall.expression()).part("functionIdentifier").name() << functionDef = nullptr;
joinHumanReadablePrefixed(args) << solAssert(dynamic_cast<VariableDeclaration const*>(identifier->annotation().referencedDeclaration), "");
")\n"; 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; break;
} }
case FunctionType::Kind::External: 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"; define(_identifier) << to_string(functionDef->resolveVirtual(m_context.mostDerivedContract()).id()) << "\n";
else if (VariableDeclaration const* varDecl = dynamic_cast<VariableDeclaration const*>(declaration)) else if (VariableDeclaration const* varDecl = dynamic_cast<VariableDeclaration const*>(declaration))
handleVariableReference(*varDecl, _identifier); 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)) 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 * 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 * of a specific Solidity type. If the Solidity type occupies a single stack slot, the IRVariable refers to a single yul variable.
* olidity 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()``. * 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 * 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 * ``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); BaseBase.init(c, d);
} }
} }
// ====
// compileViaYul: also
// ---- // ----
// x() -> 0 // x() -> 0
// y() -> 0 // y() -> 0

View File

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

View File

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

View File

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

View File

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

View File

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