mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Merge pull request #6306 from ethereum/solidity-yul2
More Solidity -> yul codegen
This commit is contained in:
commit
31abeb99a8
@ -69,6 +69,8 @@ set(sources
|
|||||||
codegen/YulUtilFunctions.cpp
|
codegen/YulUtilFunctions.cpp
|
||||||
codegen/ir/IRGenerator.cpp
|
codegen/ir/IRGenerator.cpp
|
||||||
codegen/ir/IRGenerator.h
|
codegen/ir/IRGenerator.h
|
||||||
|
codegen/ir/IRGeneratorForStatements.cpp
|
||||||
|
codegen/ir/IRGeneratorForStatements.h
|
||||||
codegen/ir/IRGenerationContext.cpp
|
codegen/ir/IRGenerationContext.cpp
|
||||||
codegen/ir/IRGenerationContext.h
|
codegen/ir/IRGenerationContext.h
|
||||||
formal/SMTChecker.cpp
|
formal/SMTChecker.cpp
|
||||||
|
@ -252,6 +252,36 @@ string YulUtilFunctions::roundUpFunction()
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
string YulUtilFunctions::overflowCheckedUIntAddFunction(size_t _bits)
|
||||||
|
{
|
||||||
|
solAssert(0 < _bits && _bits <= 256 && _bits % 8 == 0, "");
|
||||||
|
string functionName = "checked_add_uint_" + to_string(_bits);
|
||||||
|
return m_functionCollector->createFunction(functionName, [&]() {
|
||||||
|
if (_bits < 256)
|
||||||
|
return
|
||||||
|
Whiskers(R"(
|
||||||
|
function <functionName>(x, y) -> sum {
|
||||||
|
let mask := <mask>
|
||||||
|
sum := add(and(x, mask), and(y, mask))
|
||||||
|
if and(sum, not(mask)) { revert(0, 0) }
|
||||||
|
}
|
||||||
|
)")
|
||||||
|
("functionName", functionName)
|
||||||
|
("mask", toCompactHexWithPrefix((u256(1) << _bits) - 1))
|
||||||
|
.render();
|
||||||
|
else
|
||||||
|
return
|
||||||
|
Whiskers(R"(
|
||||||
|
function <functionName>(x, y) -> sum {
|
||||||
|
sum := add(x, y)
|
||||||
|
if lt(sum, x) { revert(0, 0) }
|
||||||
|
}
|
||||||
|
)")
|
||||||
|
("functionName", functionName)
|
||||||
|
.render();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
string YulUtilFunctions::arrayLengthFunction(ArrayType const& _type)
|
string YulUtilFunctions::arrayLengthFunction(ArrayType const& _type)
|
||||||
{
|
{
|
||||||
string functionName = "array_length_" + _type.identifier();
|
string functionName = "array_length_" + _type.identifier();
|
||||||
|
@ -73,6 +73,8 @@ public:
|
|||||||
/// of 32 or the input if it is a multiple of 32.
|
/// of 32 or the input if it is a multiple of 32.
|
||||||
std::string roundUpFunction();
|
std::string roundUpFunction();
|
||||||
|
|
||||||
|
std::string overflowCheckedUIntAddFunction(size_t _bits);
|
||||||
|
|
||||||
std::string arrayLengthFunction(ArrayType const& _type);
|
std::string arrayLengthFunction(ArrayType const& _type);
|
||||||
/// @returns the name of a function that computes the number of bytes required
|
/// @returns the name of a function that computes the number of bytes required
|
||||||
/// to store an array in memory given its length (internally encoded, not ABI encoded).
|
/// to store an array in memory given its length (internally encoded, not ABI encoded).
|
||||||
|
@ -20,8 +20,11 @@
|
|||||||
|
|
||||||
#include <libsolidity/codegen/ir/IRGenerationContext.h>
|
#include <libsolidity/codegen/ir/IRGenerationContext.h>
|
||||||
|
|
||||||
|
#include <libsolidity/codegen/YulUtilFunctions.h>
|
||||||
#include <libsolidity/ast/AST.h>
|
#include <libsolidity/ast/AST.h>
|
||||||
|
|
||||||
|
#include <libdevcore/Whiskers.h>
|
||||||
|
|
||||||
using namespace dev;
|
using namespace dev;
|
||||||
using namespace dev::solidity;
|
using namespace dev::solidity;
|
||||||
using namespace std;
|
using namespace std;
|
||||||
@ -35,3 +38,93 @@ string IRGenerationContext::addLocalVariable(VariableDeclaration const& _varDecl
|
|||||||
|
|
||||||
return m_localVariables[&_varDecl] = "vloc_" + _varDecl.name() + "_" + to_string(_varDecl.id());
|
return m_localVariables[&_varDecl] = "vloc_" + _varDecl.name() + "_" + to_string(_varDecl.id());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
string IRGenerationContext::variableName(VariableDeclaration const& _varDecl)
|
||||||
|
{
|
||||||
|
solAssert(
|
||||||
|
m_localVariables.count(&_varDecl),
|
||||||
|
"Unknown variable: " + _varDecl.name()
|
||||||
|
);
|
||||||
|
return m_localVariables[&_varDecl];
|
||||||
|
}
|
||||||
|
|
||||||
|
string IRGenerationContext::functionName(FunctionDefinition const& _function)
|
||||||
|
{
|
||||||
|
// @TODO previously, we had to distinguish creation context and runtime context,
|
||||||
|
// but since we do not work with jump positions anymore, this should not be a problem, right?
|
||||||
|
return "fun_" + _function.name() + "_" + to_string(_function.id());
|
||||||
|
}
|
||||||
|
|
||||||
|
FunctionDefinition const& IRGenerationContext::virtualFunction(FunctionDefinition const& _function)
|
||||||
|
{
|
||||||
|
// @TODO previously, we had to distinguish creation context and runtime context,
|
||||||
|
// but since we do not work with jump positions anymore, this should not be a problem, right?
|
||||||
|
string name = _function.name();
|
||||||
|
FunctionType functionType(_function);
|
||||||
|
for (auto const& contract: m_inheritanceHierarchy)
|
||||||
|
for (FunctionDefinition const* function: contract->definedFunctions())
|
||||||
|
if (
|
||||||
|
function->name() == name &&
|
||||||
|
!function->isConstructor() &&
|
||||||
|
FunctionType(*function).asCallableFunction(false)->hasEqualParameterTypes(functionType)
|
||||||
|
)
|
||||||
|
return *function;
|
||||||
|
solAssert(false, "Super function " + name + " not found.");
|
||||||
|
}
|
||||||
|
|
||||||
|
string IRGenerationContext::virtualFunctionName(FunctionDefinition const& _functionDeclaration)
|
||||||
|
{
|
||||||
|
return functionName(virtualFunction(_functionDeclaration));
|
||||||
|
}
|
||||||
|
|
||||||
|
string IRGenerationContext::newYulVariable()
|
||||||
|
{
|
||||||
|
return "_" + to_string(++m_varCounter);
|
||||||
|
}
|
||||||
|
|
||||||
|
string IRGenerationContext::variable(Expression const& _expression)
|
||||||
|
{
|
||||||
|
unsigned size = _expression.annotation().type->sizeOnStack();
|
||||||
|
solUnimplementedAssert(size == 1, "");
|
||||||
|
return "expr_" + to_string(_expression.id());
|
||||||
|
}
|
||||||
|
|
||||||
|
string IRGenerationContext::internalDispatch(size_t _in, size_t _out)
|
||||||
|
{
|
||||||
|
// TODO can we limit the generated functions to only those visited
|
||||||
|
// in the expression context? What about creation / runtime context?
|
||||||
|
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> {
|
||||||
|
switch fun
|
||||||
|
<#cases>
|
||||||
|
case <funID>
|
||||||
|
{
|
||||||
|
<out> := <name>(<in>)
|
||||||
|
}
|
||||||
|
</cases>
|
||||||
|
default { invalid() }
|
||||||
|
}
|
||||||
|
)");
|
||||||
|
templ("functionName", funName);
|
||||||
|
templ("comma", _in > 0 ? "," : "");
|
||||||
|
YulUtilFunctions utils(m_evmVersion, m_functions);
|
||||||
|
templ("in", utils.suffixedVariableNameList("in_", 0, _in));
|
||||||
|
templ("out", utils.suffixedVariableNameList("out_", 0, _out));
|
||||||
|
vector<map<string, string>> functions;
|
||||||
|
for (auto const& contract: m_inheritanceHierarchy)
|
||||||
|
for (FunctionDefinition const* function: contract->definedFunctions())
|
||||||
|
if (
|
||||||
|
!function->isConstructor() &&
|
||||||
|
function->parameters().size() == _in &&
|
||||||
|
function->returnParameters().size() == _out
|
||||||
|
)
|
||||||
|
functions.emplace_back(map<string, string> {
|
||||||
|
{ "funID", to_string(function->id()) },
|
||||||
|
{ "name", functionName(*function)}
|
||||||
|
});
|
||||||
|
templ("cases", move(functions));
|
||||||
|
return templ.render();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
@ -28,13 +28,17 @@
|
|||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
namespace dev
|
namespace dev
|
||||||
{
|
{
|
||||||
namespace solidity
|
namespace solidity
|
||||||
{
|
{
|
||||||
|
|
||||||
|
class ContractDefinition;
|
||||||
class VariableDeclaration;
|
class VariableDeclaration;
|
||||||
|
class FunctionDefinition;
|
||||||
|
class Expression;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class that contains contextual information during IR generation.
|
* Class that contains contextual information during IR generation.
|
||||||
@ -50,13 +54,33 @@ public:
|
|||||||
|
|
||||||
std::shared_ptr<MultiUseYulFunctionCollector> functionCollector() const { return m_functions; }
|
std::shared_ptr<MultiUseYulFunctionCollector> functionCollector() const { return m_functions; }
|
||||||
|
|
||||||
|
/// Sets the current inheritance hierarchy from derived to base.
|
||||||
|
void setInheritanceHierarchy(std::vector<ContractDefinition const*> _hierarchy)
|
||||||
|
{
|
||||||
|
m_inheritanceHierarchy = std::move(_hierarchy);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
std::string addLocalVariable(VariableDeclaration const& _varDecl);
|
std::string addLocalVariable(VariableDeclaration const& _varDecl);
|
||||||
|
std::string variableName(VariableDeclaration const& _varDecl);
|
||||||
|
std::string functionName(FunctionDefinition const& _function);
|
||||||
|
FunctionDefinition const& virtualFunction(FunctionDefinition const& _functionDeclaration);
|
||||||
|
std::string virtualFunctionName(FunctionDefinition const& _functionDeclaration);
|
||||||
|
|
||||||
|
std::string newYulVariable();
|
||||||
|
/// @returns the variable (or comma-separated list of variables) that contain
|
||||||
|
/// the value of the given expression.
|
||||||
|
std::string variable(Expression const& _expression);
|
||||||
|
|
||||||
|
std::string internalDispatch(size_t _in, size_t _out);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
langutil::EVMVersion m_evmVersion;
|
langutil::EVMVersion m_evmVersion;
|
||||||
OptimiserSettings m_optimiserSettings;
|
OptimiserSettings m_optimiserSettings;
|
||||||
|
std::vector<ContractDefinition const*> m_inheritanceHierarchy;
|
||||||
std::map<VariableDeclaration const*, std::string> m_localVariables;
|
std::map<VariableDeclaration const*, std::string> m_localVariables;
|
||||||
std::shared_ptr<MultiUseYulFunctionCollector> m_functions;
|
std::shared_ptr<MultiUseYulFunctionCollector> m_functions;
|
||||||
|
size_t m_varCounter = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -22,7 +22,10 @@
|
|||||||
|
|
||||||
#include <libsolidity/codegen/ir/IRGenerator.h>
|
#include <libsolidity/codegen/ir/IRGenerator.h>
|
||||||
|
|
||||||
|
#include <libsolidity/codegen/ir/IRGeneratorForStatements.h>
|
||||||
|
|
||||||
#include <libsolidity/ast/AST.h>
|
#include <libsolidity/ast/AST.h>
|
||||||
|
#include <libsolidity/ast/ASTVisitor.h>
|
||||||
#include <libsolidity/codegen/ABIFunctions.h>
|
#include <libsolidity/codegen/ABIFunctions.h>
|
||||||
#include <libsolidity/codegen/CompilerUtils.h>
|
#include <libsolidity/codegen/CompilerUtils.h>
|
||||||
|
|
||||||
@ -43,7 +46,7 @@ using namespace dev::solidity;
|
|||||||
pair<string, string> IRGenerator::run(ContractDefinition const& _contract)
|
pair<string, string> IRGenerator::run(ContractDefinition const& _contract)
|
||||||
{
|
{
|
||||||
// TODO Would be nice to pretty-print this while retaining comments.
|
// TODO Would be nice to pretty-print this while retaining comments.
|
||||||
string ir = generateIR(_contract);
|
string ir = generate(_contract);
|
||||||
|
|
||||||
yul::AssemblyStack asmStack(m_evmVersion, yul::AssemblyStack::Language::StrictAssembly, m_optimiserSettings);
|
yul::AssemblyStack asmStack(m_evmVersion, yul::AssemblyStack::Language::StrictAssembly, m_optimiserSettings);
|
||||||
if (!asmStack.parseAndAnalyze("", ir))
|
if (!asmStack.parseAndAnalyze("", ir))
|
||||||
@ -69,7 +72,7 @@ pair<string, string> IRGenerator::run(ContractDefinition const& _contract)
|
|||||||
return {warning + ir, warning + asmStack.print()};
|
return {warning + ir, warning + asmStack.print()};
|
||||||
}
|
}
|
||||||
|
|
||||||
string IRGenerator::generateIR(ContractDefinition const& _contract)
|
string IRGenerator::generate(ContractDefinition const& _contract)
|
||||||
{
|
{
|
||||||
Whiskers t(R"(
|
Whiskers t(R"(
|
||||||
object "<CreationObject>" {
|
object "<CreationObject>" {
|
||||||
@ -90,6 +93,7 @@ string IRGenerator::generateIR(ContractDefinition const& _contract)
|
|||||||
)");
|
)");
|
||||||
|
|
||||||
resetContext();
|
resetContext();
|
||||||
|
m_context.setInheritanceHierarchy(_contract.annotation().linearizedBaseContracts);
|
||||||
t("CreationObject", creationObjectName(_contract));
|
t("CreationObject", creationObjectName(_contract));
|
||||||
t("memoryInit", memoryInit());
|
t("memoryInit", memoryInit());
|
||||||
t("constructor", _contract.constructor() ? constructorCode(*_contract.constructor()) : "");
|
t("constructor", _contract.constructor() ? constructorCode(*_contract.constructor()) : "");
|
||||||
@ -97,17 +101,25 @@ string IRGenerator::generateIR(ContractDefinition const& _contract)
|
|||||||
t("functions", m_context.functionCollector()->requestedFunctions());
|
t("functions", m_context.functionCollector()->requestedFunctions());
|
||||||
|
|
||||||
resetContext();
|
resetContext();
|
||||||
|
m_context.setInheritanceHierarchy(_contract.annotation().linearizedBaseContracts);
|
||||||
t("RuntimeObject", runtimeObjectName(_contract));
|
t("RuntimeObject", runtimeObjectName(_contract));
|
||||||
t("dispatch", dispatchRoutine(_contract));
|
t("dispatch", dispatchRoutine(_contract));
|
||||||
t("runtimeFunctions", m_context.functionCollector()->requestedFunctions());
|
t("runtimeFunctions", m_context.functionCollector()->requestedFunctions());
|
||||||
return t.render();
|
return t.render();
|
||||||
}
|
}
|
||||||
|
|
||||||
string IRGenerator::generateIRFunction(FunctionDefinition const& _function)
|
string IRGenerator::generate(Block const& _block)
|
||||||
{
|
{
|
||||||
string functionName = "fun_" + to_string(_function.id()) + "_" + _function.name();
|
IRGeneratorForStatements generator(m_context, m_utils);
|
||||||
|
_block.accept(generator);
|
||||||
|
return generator.code();
|
||||||
|
}
|
||||||
|
|
||||||
|
string IRGenerator::generateFunction(FunctionDefinition const& _function)
|
||||||
|
{
|
||||||
|
string functionName = m_context.functionName(_function);
|
||||||
return m_context.functionCollector()->createFunction(functionName, [&]() {
|
return m_context.functionCollector()->createFunction(functionName, [&]() {
|
||||||
Whiskers t("function <functionName>(<params>) <returns> {}");
|
Whiskers t("\nfunction <functionName>(<params>) <returns> {\n<body>\n}\n");
|
||||||
t("functionName", functionName);
|
t("functionName", functionName);
|
||||||
string params;
|
string params;
|
||||||
for (auto const& varDecl: _function.parameters())
|
for (auto const& varDecl: _function.parameters())
|
||||||
@ -117,6 +129,7 @@ string IRGenerator::generateIRFunction(FunctionDefinition const& _function)
|
|||||||
for (auto const& varDecl: _function.returnParameters())
|
for (auto const& varDecl: _function.returnParameters())
|
||||||
retParams += (retParams.empty() ? "" : ", ") + m_context.addLocalVariable(*varDecl);
|
retParams += (retParams.empty() ? "" : ", ") + m_context.addLocalVariable(*varDecl);
|
||||||
t("returns", retParams.empty() ? "" : " -> " + retParams);
|
t("returns", retParams.empty() ? "" : " -> " + retParams);
|
||||||
|
t("body", generate(_function.body()));
|
||||||
return t.render();
|
return t.render();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -200,7 +213,7 @@ string IRGenerator::dispatchRoutine(ContractDefinition const& _contract)
|
|||||||
templ["abiDecode"] = abiFunctions.tupleDecoder(type->parameterTypes());
|
templ["abiDecode"] = abiFunctions.tupleDecoder(type->parameterTypes());
|
||||||
templ["params"] = m_utils.suffixedVariableNameList("param_", 0, paramVars);
|
templ["params"] = m_utils.suffixedVariableNameList("param_", 0, paramVars);
|
||||||
templ["retParams"] = m_utils.suffixedVariableNameList("ret_", retVars, 0);
|
templ["retParams"] = m_utils.suffixedVariableNameList("ret_", retVars, 0);
|
||||||
templ["function"] = generateIRFunction(dynamic_cast<FunctionDefinition const&>(type->declaration()));
|
templ["function"] = generateFunction(dynamic_cast<FunctionDefinition const&>(type->declaration()));
|
||||||
templ["allocate"] = m_utils.allocationFunction();
|
templ["allocate"] = m_utils.allocationFunction();
|
||||||
templ["abiEncode"] = abiFunctions.tupleEncoder(type->returnParameterTypes(), type->returnParameterTypes(), false);
|
templ["abiEncode"] = abiFunctions.tupleEncoder(type->returnParameterTypes(), type->returnParameterTypes(), false);
|
||||||
templ["comma"] = retVars == 0 ? "" : ", ";
|
templ["comma"] = retVars == 0 ? "" : ", ";
|
||||||
@ -211,7 +224,7 @@ string IRGenerator::dispatchRoutine(ContractDefinition const& _contract)
|
|||||||
string fallbackCode;
|
string fallbackCode;
|
||||||
if (!fallback->isPayable())
|
if (!fallback->isPayable())
|
||||||
fallbackCode += callValueCheck();
|
fallbackCode += callValueCheck();
|
||||||
fallbackCode += generateIRFunction(*fallback) + "() stop()";
|
fallbackCode += generateFunction(*fallback) + "() stop()";
|
||||||
|
|
||||||
t("fallback", fallbackCode);
|
t("fallback", fallbackCode);
|
||||||
}
|
}
|
||||||
|
@ -51,8 +51,11 @@ public:
|
|||||||
std::pair<std::string, std::string> run(ContractDefinition const& _contract);
|
std::pair<std::string, std::string> run(ContractDefinition const& _contract);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::string generateIR(ContractDefinition const& _contract);
|
std::string generate(ContractDefinition const& _contract);
|
||||||
std::string generateIRFunction(FunctionDefinition const& _function);
|
std::string generate(Block const& _block);
|
||||||
|
|
||||||
|
/// Generates code for and returns the name of the function.
|
||||||
|
std::string generateFunction(FunctionDefinition const& _function);
|
||||||
|
|
||||||
std::string constructorCode(FunctionDefinition const& _constructor);
|
std::string constructorCode(FunctionDefinition const& _constructor);
|
||||||
std::string deployCode(ContractDefinition const& _contract);
|
std::string deployCode(ContractDefinition const& _contract);
|
||||||
|
214
libsolidity/codegen/ir/IRGeneratorForStatements.cpp
Normal file
214
libsolidity/codegen/ir/IRGeneratorForStatements.cpp
Normal file
@ -0,0 +1,214 @@
|
|||||||
|
/*
|
||||||
|
This file is part of solidity.
|
||||||
|
|
||||||
|
solidity is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
solidity is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with solidity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
/**
|
||||||
|
* Component that translates Solidity code into Yul at statement level and below.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <libsolidity/codegen/ir/IRGeneratorForStatements.h>
|
||||||
|
|
||||||
|
#include <libsolidity/codegen/ir/IRGenerationContext.h>
|
||||||
|
#include <libsolidity/codegen/YulUtilFunctions.h>
|
||||||
|
|
||||||
|
#include <libdevcore/StringUtils.h>
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
using namespace dev;
|
||||||
|
using namespace dev::solidity;
|
||||||
|
|
||||||
|
bool IRGeneratorForStatements::visit(VariableDeclarationStatement const& _varDeclStatement)
|
||||||
|
{
|
||||||
|
for (auto const& decl: _varDeclStatement.declarations())
|
||||||
|
if (decl)
|
||||||
|
m_context.addLocalVariable(*decl);
|
||||||
|
|
||||||
|
if (Expression const* expression = _varDeclStatement.initialValue())
|
||||||
|
{
|
||||||
|
solUnimplementedAssert(_varDeclStatement.declarations().size() == 1, "");
|
||||||
|
|
||||||
|
expression->accept(*this);
|
||||||
|
|
||||||
|
solUnimplementedAssert(
|
||||||
|
*expression->annotation().type == *_varDeclStatement.declarations().front()->type(),
|
||||||
|
"Type conversion not yet implemented"
|
||||||
|
);
|
||||||
|
m_code <<
|
||||||
|
"let " <<
|
||||||
|
m_context.variableName(*_varDeclStatement.declarations().front()) <<
|
||||||
|
" := " <<
|
||||||
|
m_context.variable(*expression) <<
|
||||||
|
"\n";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
for (auto const& decl: _varDeclStatement.declarations())
|
||||||
|
if (decl)
|
||||||
|
m_code << "let " << m_context.variableName(*decl) << "\n";
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IRGeneratorForStatements::visit(Assignment const& _assignment)
|
||||||
|
{
|
||||||
|
solUnimplementedAssert(_assignment.assignmentOperator() == Token::Assign, "");
|
||||||
|
|
||||||
|
_assignment.rightHandSide().accept(*this);
|
||||||
|
|
||||||
|
// solUnimplementedAssert(
|
||||||
|
// *_assignment.rightHandSide().annotation().type == *_assignment.leftHandSide().annotation().type,
|
||||||
|
// "Type conversion not yet implemented"
|
||||||
|
// );
|
||||||
|
// TODO proper lvalue handling
|
||||||
|
auto const& identifier = dynamic_cast<Identifier const&>(_assignment.leftHandSide());
|
||||||
|
string varName = m_context.variableName(dynamic_cast<VariableDeclaration const&>(*identifier.annotation().referencedDeclaration));
|
||||||
|
m_code << varName << " := " << m_context.variable(_assignment.rightHandSide()) << "\n";
|
||||||
|
m_code << "let " << m_context.variable(_assignment) << " := " << varName << "\n";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void IRGeneratorForStatements::endVisit(BinaryOperation const& _binOp)
|
||||||
|
{
|
||||||
|
solUnimplementedAssert(_binOp.getOperator() == Token::Add, "");
|
||||||
|
solUnimplementedAssert(*_binOp.leftExpression().annotation().type == *_binOp.rightExpression().annotation().type, "");
|
||||||
|
if (IntegerType const* type = dynamic_cast<IntegerType const*>(_binOp.annotation().commonType.get()))
|
||||||
|
{
|
||||||
|
solUnimplementedAssert(!type->isSigned(), "");
|
||||||
|
m_code <<
|
||||||
|
"let " <<
|
||||||
|
m_context.variable(_binOp) <<
|
||||||
|
" := " <<
|
||||||
|
m_utils.overflowCheckedUIntAddFunction(type->numBits()) <<
|
||||||
|
"(" <<
|
||||||
|
m_context.variable(_binOp.leftExpression()) <<
|
||||||
|
", " <<
|
||||||
|
m_context.variable(_binOp.rightExpression()) <<
|
||||||
|
")\n";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
solUnimplementedAssert(false, "");
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IRGeneratorForStatements::visit(FunctionCall const& _functionCall)
|
||||||
|
{
|
||||||
|
solUnimplementedAssert(_functionCall.annotation().kind == FunctionCallKind::FunctionCall, "");
|
||||||
|
FunctionTypePointer functionType = dynamic_pointer_cast<FunctionType const>(_functionCall.expression().annotation().type);
|
||||||
|
|
||||||
|
TypePointers parameterTypes = functionType->parameterTypes();
|
||||||
|
vector<ASTPointer<Expression const>> const& callArguments = _functionCall.arguments();
|
||||||
|
vector<ASTPointer<ASTString>> const& callArgumentNames = _functionCall.names();
|
||||||
|
if (!functionType->takesArbitraryParameters())
|
||||||
|
solAssert(callArguments.size() == parameterTypes.size(), "");
|
||||||
|
|
||||||
|
vector<ASTPointer<Expression const>> arguments;
|
||||||
|
if (callArgumentNames.empty())
|
||||||
|
// normal arguments
|
||||||
|
arguments = callArguments;
|
||||||
|
else
|
||||||
|
// named arguments
|
||||||
|
for (auto const& parameterName: functionType->parameterNames())
|
||||||
|
{
|
||||||
|
auto const it = std::find_if(callArgumentNames.cbegin(), callArgumentNames.cend(), [&](ASTPointer<ASTString> const& _argName) {
|
||||||
|
return *_argName == parameterName;
|
||||||
|
});
|
||||||
|
|
||||||
|
solAssert(it != callArgumentNames.cend(), "");
|
||||||
|
arguments.push_back(callArguments[std::distance(callArgumentNames.begin(), it)]);
|
||||||
|
}
|
||||||
|
|
||||||
|
solUnimplementedAssert(!functionType->bound(), "");
|
||||||
|
switch (functionType->kind())
|
||||||
|
{
|
||||||
|
case FunctionType::Kind::Internal:
|
||||||
|
{
|
||||||
|
vector<string> args;
|
||||||
|
for (unsigned i = 0; i < arguments.size(); ++i)
|
||||||
|
{
|
||||||
|
arguments[i]->accept(*this);
|
||||||
|
// TODO convert
|
||||||
|
//utils().convertType(*arguments[i]->annotation().type, *function.parameterTypes()[i]);
|
||||||
|
args.emplace_back(m_context.variable(*arguments[i]));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (auto identifier = dynamic_cast<Identifier const*>(&_functionCall.expression()))
|
||||||
|
{
|
||||||
|
solAssert(!functionType->bound(), "");
|
||||||
|
if (auto functionDef = dynamic_cast<FunctionDefinition const*>(identifier->annotation().referencedDeclaration))
|
||||||
|
{
|
||||||
|
// @TODO The function can very well return multiple vars.
|
||||||
|
m_code <<
|
||||||
|
"let " <<
|
||||||
|
m_context.variable(_functionCall) <<
|
||||||
|
" := " <<
|
||||||
|
m_context.virtualFunctionName(*functionDef) <<
|
||||||
|
"(" <<
|
||||||
|
joinHumanReadable(args) <<
|
||||||
|
")\n";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_functionCall.expression().accept(*this);
|
||||||
|
|
||||||
|
// @TODO The function can very well return multiple vars.
|
||||||
|
args = vector<string>{m_context.variable(_functionCall.expression())} + args;
|
||||||
|
m_code <<
|
||||||
|
"let " <<
|
||||||
|
m_context.variable(_functionCall) <<
|
||||||
|
" := " <<
|
||||||
|
m_context.internalDispatch(functionType->parameterTypes().size(), functionType->returnParameterTypes().size()) <<
|
||||||
|
"(" <<
|
||||||
|
joinHumanReadable(args) <<
|
||||||
|
")\n";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
solUnimplemented("");
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IRGeneratorForStatements::visit(Identifier const& _identifier)
|
||||||
|
{
|
||||||
|
Declaration const* declaration = _identifier.annotation().referencedDeclaration;
|
||||||
|
string value;
|
||||||
|
if (FunctionDefinition const* functionDef = dynamic_cast<FunctionDefinition const*>(declaration))
|
||||||
|
value = to_string(m_context.virtualFunction(*functionDef).id());
|
||||||
|
else if (VariableDeclaration const* varDecl = dynamic_cast<VariableDeclaration const*>(declaration))
|
||||||
|
value = m_context.variableName(*varDecl);
|
||||||
|
else
|
||||||
|
solUnimplemented("");
|
||||||
|
m_code << "let " << m_context.variable(_identifier) << " := " << value << "\n";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IRGeneratorForStatements::visit(Literal const& _literal)
|
||||||
|
{
|
||||||
|
TypePointer type = _literal.annotation().type;
|
||||||
|
|
||||||
|
switch (type->category())
|
||||||
|
{
|
||||||
|
case Type::Category::RationalNumber:
|
||||||
|
case Type::Category::Bool:
|
||||||
|
case Type::Category::Address:
|
||||||
|
m_code << "let " << m_context.variable(_literal) << " := " << toCompactHexWithPrefix(type->literalValue(&_literal)) << "\n";
|
||||||
|
break;
|
||||||
|
case Type::Category::StringLiteral:
|
||||||
|
solUnimplemented("");
|
||||||
|
break; // will be done during conversion
|
||||||
|
default:
|
||||||
|
solUnimplemented("Only integer, boolean and string literals implemented for now.");
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
61
libsolidity/codegen/ir/IRGeneratorForStatements.h
Normal file
61
libsolidity/codegen/ir/IRGeneratorForStatements.h
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
/*
|
||||||
|
This file is part of solidity.
|
||||||
|
|
||||||
|
solidity is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
solidity is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with solidity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
/**
|
||||||
|
* Component that translates Solidity code into Yul at statement level and below.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <libsolidity/ast/ASTVisitor.h>
|
||||||
|
|
||||||
|
namespace dev
|
||||||
|
{
|
||||||
|
namespace solidity
|
||||||
|
{
|
||||||
|
|
||||||
|
class IRGenerationContext;
|
||||||
|
class YulUtilFunctions;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Component that translates Solidity's AST into Yul at statement level and below.
|
||||||
|
* It is an AST visitor that appends to an internal string buffer.
|
||||||
|
*/
|
||||||
|
class IRGeneratorForStatements: public ASTConstVisitor
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
IRGeneratorForStatements(IRGenerationContext& _context, YulUtilFunctions& _utils):
|
||||||
|
m_context(_context),
|
||||||
|
m_utils(_utils)
|
||||||
|
{}
|
||||||
|
|
||||||
|
std::string code() const { return m_code.str(); }
|
||||||
|
|
||||||
|
bool visit(VariableDeclarationStatement const& _variableDeclaration) override;
|
||||||
|
bool visit(Assignment const& _assignment) override;
|
||||||
|
void endVisit(BinaryOperation const& _binOp) override;
|
||||||
|
bool visit(FunctionCall const& _funCall) override;
|
||||||
|
bool visit(Identifier const& _identifier) override;
|
||||||
|
bool visit(Literal const& _literal) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::ostringstream m_code;
|
||||||
|
IRGenerationContext& m_context;
|
||||||
|
YulUtilFunctions& m_utils;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@ -1 +1 @@
|
|||||||
{"contracts":{"A":{"C":{"irOptimized":"/*******************************************************\n * WARNING *\n * Solidity to Yul compilation is still EXPERIMENTAL *\n * It can result in LOSS OF FUNDS or worse *\n * !USE AT YOUR OWN RISK! *\n *******************************************************/\n\nobject \"C_6\" {\n code {\n mstore(64, 128)\n codecopy(0, dataoffset(\"C_6_deployed\"), datasize(\"C_6_deployed\"))\n return(0, datasize(\"C_6_deployed\"))\n }\n object \"C_6_deployed\" {\n code {\n mstore(64, 128)\n if iszero(lt(calldatasize(), 4))\n {\n let selector := shift_right_224_unsigned(calldataload(0))\n switch selector\n case 0x26121ff0 {\n if callvalue()\n {\n revert(0, 0)\n }\n abi_decode_tuple_(4, calldatasize())\n fun_5_f()\n let memPos := allocateMemory(0)\n let memEnd := abi_encode_tuple__to__fromStack(memPos)\n return(memPos, sub(memEnd, memPos))\n }\n default {\n }\n }\n revert(0, 0)\n function abi_decode_tuple_(headStart, dataEnd)\n {\n if slt(sub(dataEnd, headStart), 0)\n {\n revert(0, 0)\n }\n }\n function abi_encode_tuple__to__fromStack(headStart) -> tail\n {\n tail := add(headStart, 0)\n }\n function allocateMemory(size) -> memPtr\n {\n memPtr := mload(64)\n let newFreePtr := add(memPtr, size)\n if or(gt(newFreePtr, 0xffffffffffffffff), lt(newFreePtr, memPtr))\n {\n revert(0, 0)\n }\n mstore(64, newFreePtr)\n }\n function fun_5_f()\n {\n }\n function shift_right_224_unsigned(value) -> newValue\n {\n newValue := shr(224, value)\n }\n }\n }\n}\n"}}},"sources":{"A":{"id":0}}}
|
{"contracts":{"A":{"C":{"irOptimized":"/*******************************************************\n * WARNING *\n * Solidity to Yul compilation is still EXPERIMENTAL *\n * It can result in LOSS OF FUNDS or worse *\n * !USE AT YOUR OWN RISK! *\n *******************************************************/\n\nobject \"C_6\" {\n code {\n mstore(64, 128)\n codecopy(0, dataoffset(\"C_6_deployed\"), datasize(\"C_6_deployed\"))\n return(0, datasize(\"C_6_deployed\"))\n }\n object \"C_6_deployed\" {\n code {\n mstore(64, 128)\n if iszero(lt(calldatasize(), 4))\n {\n let selector := shift_right_224_unsigned(calldataload(0))\n switch selector\n case 0x26121ff0 {\n if callvalue()\n {\n revert(0, 0)\n }\n abi_decode_tuple_(4, calldatasize())\n fun_f_5()\n let memPos := allocateMemory(0)\n let memEnd := abi_encode_tuple__to__fromStack(memPos)\n return(memPos, sub(memEnd, memPos))\n }\n default {\n }\n }\n revert(0, 0)\n function abi_decode_tuple_(headStart, dataEnd)\n {\n if slt(sub(dataEnd, headStart), 0)\n {\n revert(0, 0)\n }\n }\n function abi_encode_tuple__to__fromStack(headStart) -> tail\n {\n tail := add(headStart, 0)\n }\n function allocateMemory(size) -> memPtr\n {\n memPtr := mload(64)\n let newFreePtr := add(memPtr, size)\n if or(gt(newFreePtr, 0xffffffffffffffff), lt(newFreePtr, memPtr))\n {\n revert(0, 0)\n }\n mstore(64, newFreePtr)\n }\n function fun_f_5()\n {\n }\n function shift_right_224_unsigned(value) -> newValue\n {\n newValue := shr(224, value)\n }\n }\n }\n}\n"}}},"sources":{"A":{"id":0}}}
|
||||||
|
@ -1 +1 @@
|
|||||||
{"contracts":{"A":{"C":{"ir":"/*******************************************************\n * WARNING *\n * Solidity to Yul compilation is still EXPERIMENTAL *\n * It can result in LOSS OF FUNDS or worse *\n * !USE AT YOUR OWN RISK! *\n *******************************************************/\n\n\n\t\tobject \"C_6\" {\n\t\t\tcode {\n\t\t\t\tmstore(64, 128)\n\t\t\t\t\n\t\t\t\t\n\t\tcodecopy(0, dataoffset(\"C_6_deployed\"), datasize(\"C_6_deployed\"))\n\t\treturn(0, datasize(\"C_6_deployed\"))\n\t\n\t\t\t\t\n\t\t\t}\n\t\t\tobject \"C_6_deployed\" {\n\t\t\t\tcode {\n\t\t\t\t\tmstore(64, 128)\n\t\t\t\t\t\n\t\tif iszero(lt(calldatasize(), 4))\n\t\t{\n\t\t\tlet selector := shift_right_224_unsigned(calldataload(0))\n\t\t\tswitch selector\n\t\t\t\n\t\t\tcase 0x26121ff0\n\t\t\t{\n\t\t\t\t// f()\n\t\t\t\tif callvalue() { revert(0, 0) }\n\t\t\t\t abi_decode_tuple_(4, calldatasize())\n\t\t\t\t fun_5_f()\n\t\t\t\tlet memPos := allocateMemory(0)\n\t\t\t\tlet memEnd := abi_encode_tuple__to__fromStack(memPos )\n\t\t\t\treturn(memPos, sub(memEnd, memPos))\n\t\t\t}\n\t\t\t\n\t\t\tdefault {}\n\t\t}\n\t\trevert(0, 0)\n\t\n\t\t\t\t\t\n\t\t\tfunction abi_decode_tuple_(headStart, dataEnd) {\n\t\t\t\tif slt(sub(dataEnd, headStart), 0) { revert(0, 0) }\n\t\t\t\t\n\t\t\t}\n\t\t\n\t\t\tfunction abi_encode_tuple__to__fromStack(headStart ) -> tail {\n\t\t\t\ttail := add(headStart, 0)\n\t\t\t\t\n\t\t\t}\n\t\t\n\t\t\tfunction allocateMemory(size) -> memPtr {\n\t\t\t\tmemPtr := mload(64)\n\t\t\t\tlet newFreePtr := add(memPtr, size)\n\t\t\t\t// protect against overflow\n\t\t\t\tif or(gt(newFreePtr, 0xffffffffffffffff), lt(newFreePtr, memPtr)) { revert(0, 0) }\n\t\t\t\tmstore(64, newFreePtr)\n\t\t\t}\n\t\tfunction fun_5_f() {}\n\t\t\t\tfunction shift_right_224_unsigned(value) -> newValue {\n\t\t\t\t\tnewValue := shr(224, value)\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t"}}},"sources":{"A":{"id":0}}}
|
{"contracts":{"A":{"C":{"ir":"/*******************************************************\n * WARNING *\n * Solidity to Yul compilation is still EXPERIMENTAL *\n * It can result in LOSS OF FUNDS or worse *\n * !USE AT YOUR OWN RISK! *\n *******************************************************/\n\n\n\t\tobject \"C_6\" {\n\t\t\tcode {\n\t\t\t\tmstore(64, 128)\n\t\t\t\t\n\t\t\t\t\n\t\tcodecopy(0, dataoffset(\"C_6_deployed\"), datasize(\"C_6_deployed\"))\n\t\treturn(0, datasize(\"C_6_deployed\"))\n\t\n\t\t\t\t\n\t\t\t}\n\t\t\tobject \"C_6_deployed\" {\n\t\t\t\tcode {\n\t\t\t\t\tmstore(64, 128)\n\t\t\t\t\t\n\t\tif iszero(lt(calldatasize(), 4))\n\t\t{\n\t\t\tlet selector := shift_right_224_unsigned(calldataload(0))\n\t\t\tswitch selector\n\t\t\t\n\t\t\tcase 0x26121ff0\n\t\t\t{\n\t\t\t\t// f()\n\t\t\t\tif callvalue() { revert(0, 0) }\n\t\t\t\t abi_decode_tuple_(4, calldatasize())\n\t\t\t\t fun_f_5()\n\t\t\t\tlet memPos := allocateMemory(0)\n\t\t\t\tlet memEnd := abi_encode_tuple__to__fromStack(memPos )\n\t\t\t\treturn(memPos, sub(memEnd, memPos))\n\t\t\t}\n\t\t\t\n\t\t\tdefault {}\n\t\t}\n\t\trevert(0, 0)\n\t\n\t\t\t\t\t\n\t\t\tfunction abi_decode_tuple_(headStart, dataEnd) {\n\t\t\t\tif slt(sub(dataEnd, headStart), 0) { revert(0, 0) }\n\t\t\t\t\n\t\t\t}\n\t\t\n\t\t\tfunction abi_encode_tuple__to__fromStack(headStart ) -> tail {\n\t\t\t\ttail := add(headStart, 0)\n\t\t\t\t\n\t\t\t}\n\t\t\n\t\t\tfunction allocateMemory(size) -> memPtr {\n\t\t\t\tmemPtr := mload(64)\n\t\t\t\tlet newFreePtr := add(memPtr, size)\n\t\t\t\t// protect against overflow\n\t\t\t\tif or(gt(newFreePtr, 0xffffffffffffffff), lt(newFreePtr, memPtr)) { revert(0, 0) }\n\t\t\t\tmstore(64, newFreePtr)\n\t\t\t}\n\t\t\nfunction fun_f_5() {\n\n}\n\n\t\t\t\tfunction shift_right_224_unsigned(value) -> newValue {\n\t\t\t\t\tnewValue := shr(224, value)\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t"}}},"sources":{"A":{"id":0}}}
|
||||||
|
@ -0,0 +1,18 @@
|
|||||||
|
contract C {
|
||||||
|
function f(uint a, uint b) public pure returns (uint x) {
|
||||||
|
x = a + b;
|
||||||
|
}
|
||||||
|
function g(uint8 a, uint8 b) public pure returns (uint8 x) {
|
||||||
|
x = a + b;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ====
|
||||||
|
// compileViaYul: true
|
||||||
|
// ----
|
||||||
|
// f(uint256,uint256): 5, 6 -> 11
|
||||||
|
// f(uint256,uint256): -2, 1 -> -1
|
||||||
|
// f(uint256,uint256): -2, 2 -> FAILURE
|
||||||
|
// f(uint256,uint256): 2, -2 -> FAILURE
|
||||||
|
// g(uint8,uint8): 128, 64 -> 192
|
||||||
|
// g(uint8,uint8): 128, 127 -> 255
|
||||||
|
// g(uint8,uint8): 128, 128 -> FAILURE
|
@ -0,0 +1,10 @@
|
|||||||
|
contract C {
|
||||||
|
function f(address a) public pure returns (address x) {
|
||||||
|
address b = a;
|
||||||
|
x = b;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ====
|
||||||
|
// compileViaYul: true
|
||||||
|
// ----
|
||||||
|
// f(address): 0x1234 -> 0x1234
|
10
test/libsolidity/semanticTests/viaYul/local_assignment.sol
Normal file
10
test/libsolidity/semanticTests/viaYul/local_assignment.sol
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
contract C {
|
||||||
|
function f(uint a) public pure returns (uint x) {
|
||||||
|
uint b = a;
|
||||||
|
x = b;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ====
|
||||||
|
// compileViaYul: true
|
||||||
|
// ----
|
||||||
|
// f(uint256): 6 -> 6
|
@ -0,0 +1,10 @@
|
|||||||
|
contract C {
|
||||||
|
function f(bool a) public pure returns (bool x) {
|
||||||
|
bool b = a;
|
||||||
|
x = b;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ====
|
||||||
|
// compileViaYul: true
|
||||||
|
// ----
|
||||||
|
// f(bool): true -> true
|
10
test/libsolidity/semanticTests/viaYul/simple_assignment.sol
Normal file
10
test/libsolidity/semanticTests/viaYul/simple_assignment.sol
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
contract C {
|
||||||
|
function f(uint a, uint b) public pure returns (uint x, uint y) {
|
||||||
|
x = a;
|
||||||
|
y = b;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ====
|
||||||
|
// compileViaYul: true
|
||||||
|
// ----
|
||||||
|
// f(uint256,uint256): 5, 6 -> 5, 6
|
32
test/libsolidity/semanticTests/viaYul/virtual_functions.sol
Normal file
32
test/libsolidity/semanticTests/viaYul/virtual_functions.sol
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
contract X {
|
||||||
|
function f() public returns (uint x) {
|
||||||
|
x = g();
|
||||||
|
}
|
||||||
|
function g() public returns (uint x) {
|
||||||
|
x = 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
contract C is X {
|
||||||
|
function f1() public returns (uint x) {
|
||||||
|
// direct call
|
||||||
|
x = g();
|
||||||
|
}
|
||||||
|
function f2() public returns (uint x) {
|
||||||
|
// call via base
|
||||||
|
x = f();
|
||||||
|
}
|
||||||
|
function f3() public returns (uint x) {
|
||||||
|
// explicit call via base
|
||||||
|
//x = super.g();
|
||||||
|
}
|
||||||
|
function g() public returns (uint x) {
|
||||||
|
x = 3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ===
|
||||||
|
// compileViaYul: true
|
||||||
|
// ----
|
||||||
|
// f() -> 3
|
||||||
|
// f1() -> 3
|
||||||
|
// f2() -> 3
|
||||||
|
// g() -> 3
|
Loading…
Reference in New Issue
Block a user