mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
tmp
This commit is contained in:
parent
4357b0316b
commit
bd0e0fcdbe
@ -268,8 +268,10 @@ namespace solidity::langutil
|
|||||||
/* Yul-specific tokens, but not keywords. */ \
|
/* Yul-specific tokens, but not keywords. */ \
|
||||||
T(Leave, "leave", 0) \
|
T(Leave, "leave", 0) \
|
||||||
\
|
\
|
||||||
|
T(NonExperimentalEnd, nullptr, 0) /* used as non-experimental enum end marker */ \
|
||||||
/* Experimental Solidity specific keywords. */ \
|
/* Experimental Solidity specific keywords. */ \
|
||||||
K(Word, "word", 0) \
|
K(Word, "word", 0) \
|
||||||
|
K(Void, "void", 0) \
|
||||||
K(StaticAssert, "static_assert", 0) \
|
K(StaticAssert, "static_assert", 0) \
|
||||||
T(ExperimentalEnd, nullptr, 0) /* used as experimental enum end marker */ \
|
T(ExperimentalEnd, nullptr, 0) /* used as experimental enum end marker */ \
|
||||||
\
|
\
|
||||||
@ -295,7 +297,10 @@ namespace TokenTraits
|
|||||||
constexpr size_t count() { return static_cast<size_t>(Token::NUM_TOKENS); }
|
constexpr size_t count() { return static_cast<size_t>(Token::NUM_TOKENS); }
|
||||||
|
|
||||||
// Predicates
|
// Predicates
|
||||||
constexpr bool isElementaryTypeName(Token tok) { return (Token::Int <= tok && tok < Token::TypesEnd) || tok == Token::Word; }
|
constexpr bool isElementaryTypeName(Token tok)
|
||||||
|
{
|
||||||
|
return (Token::Int <= tok && tok < Token::TypesEnd) || tok == Token::Word || tok == Token::Void;
|
||||||
|
}
|
||||||
constexpr bool isAssignmentOp(Token tok) { return Token::Assign <= tok && tok <= Token::AssignMod; }
|
constexpr bool isAssignmentOp(Token tok) { return Token::Assign <= tok && tok <= Token::AssignMod; }
|
||||||
constexpr bool isBinaryOp(Token op) { return Token::Comma <= op && op <= Token::Exp; }
|
constexpr bool isBinaryOp(Token op) { return Token::Comma <= op && op <= Token::Exp; }
|
||||||
constexpr bool isCommutativeOp(Token op) { return op == Token::BitOr || op == Token::BitXor || op == Token::BitAnd ||
|
constexpr bool isCommutativeOp(Token op) { return op == Token::BitOr || op == Token::BitXor || op == Token::BitAnd ||
|
||||||
@ -331,11 +336,11 @@ namespace TokenTraits
|
|||||||
{
|
{
|
||||||
return tok == Token::Assembly || tok == Token::Contract || tok == Token::External || tok == Token::Fallback ||
|
return tok == Token::Assembly || tok == Token::Contract || tok == Token::External || tok == Token::Fallback ||
|
||||||
tok == Token::Pragma || tok == Token::Import || tok == Token::As || tok == Token::Function || tok == Token::Let ||
|
tok == Token::Pragma || tok == Token::Import || tok == Token::As || tok == Token::Function || tok == Token::Let ||
|
||||||
(tok >= Token::Word && tok < Token::ExperimentalEnd);
|
(tok > Token::NonExperimentalEnd && tok < Token::ExperimentalEnd);
|
||||||
}
|
}
|
||||||
constexpr bool isExperimentalSolidityOnlyKeyword(Token tok)
|
constexpr bool isExperimentalSolidityOnlyKeyword(Token tok)
|
||||||
{
|
{
|
||||||
return tok >= Token::Word && tok < Token::ExperimentalEnd;
|
return tok > Token::NonExperimentalEnd && tok < Token::ExperimentalEnd;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isYulKeyword(std::string const& _literal);
|
bool isYulKeyword(std::string const& _literal);
|
||||||
|
@ -98,6 +98,7 @@ set(sources
|
|||||||
codegen/ReturnInfo.cpp
|
codegen/ReturnInfo.cpp
|
||||||
codegen/YulUtilFunctions.h
|
codegen/YulUtilFunctions.h
|
||||||
codegen/YulUtilFunctions.cpp
|
codegen/YulUtilFunctions.cpp
|
||||||
|
codegen/experimental/IRGenerationContext.h
|
||||||
codegen/experimental/IRGenerator.cpp
|
codegen/experimental/IRGenerator.cpp
|
||||||
codegen/experimental/IRGenerator.h
|
codegen/experimental/IRGenerator.h
|
||||||
codegen/experimental/IRGeneratorForStatements.cpp
|
codegen/experimental/IRGeneratorForStatements.cpp
|
||||||
|
@ -36,7 +36,7 @@ bool Analysis::check(vector<shared_ptr<SourceUnit const>> const& _sourceUnits)
|
|||||||
for (auto source: _sourceUnits)
|
for (auto source: _sourceUnits)
|
||||||
if (!syntaxRestrictor.check(*source))
|
if (!syntaxRestrictor.check(*source))
|
||||||
return false;
|
return false;
|
||||||
TypeInference typeInference{m_errorReporter};
|
TypeInference typeInference{*this};
|
||||||
for (auto source: _sourceUnits)
|
for (auto source: _sourceUnits)
|
||||||
if (!typeInference.analyze(*source))
|
if (!typeInference.analyze(*source))
|
||||||
return false;
|
return false;
|
||||||
|
@ -38,7 +38,11 @@ class Analysis
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
Analysis(langutil::ErrorReporter& _errorReporter, uint64_t _maxAstId);
|
Analysis(langutil::ErrorReporter& _errorReporter, uint64_t _maxAstId);
|
||||||
|
Analysis(Analysis const&) = delete;
|
||||||
|
Analysis const& operator=(Analysis const&) = delete;
|
||||||
bool check(std::vector<std::shared_ptr<SourceUnit const>> const& _sourceUnits);
|
bool check(std::vector<std::shared_ptr<SourceUnit const>> const& _sourceUnits);
|
||||||
|
langutil::ErrorReporter& errorReporter() { return m_errorReporter; }
|
||||||
|
uint64_t maxAstId() const { return m_maxAstId; }
|
||||||
private:
|
private:
|
||||||
langutil::ErrorReporter& m_errorReporter;
|
langutil::ErrorReporter& m_errorReporter;
|
||||||
uint64_t m_maxAstId = 0;
|
uint64_t m_maxAstId = 0;
|
||||||
|
@ -43,6 +43,7 @@ private:
|
|||||||
bool visit(ContractDefinition const& _contractDefinition) override;
|
bool visit(ContractDefinition const& _contractDefinition) override;
|
||||||
bool visit(FunctionDefinition const& _functionDefinition) override;
|
bool visit(FunctionDefinition const& _functionDefinition) override;
|
||||||
bool visit(ExpressionStatement const&) override { return true; }
|
bool visit(ExpressionStatement const&) override { return true; }
|
||||||
|
bool visit(FunctionCall const&) override { return true; }
|
||||||
bool visit(Assignment const&) override { return true; }
|
bool visit(Assignment const&) override { return true; }
|
||||||
bool visit(Block const&) override { return true; }
|
bool visit(Block const&) override { return true; }
|
||||||
bool visit(InlineAssembly const&) override { return true; }
|
bool visit(InlineAssembly const&) override { return true; }
|
||||||
|
@ -18,17 +18,39 @@
|
|||||||
|
|
||||||
|
|
||||||
#include <libsolidity/analysis/experimental/TypeInference.h>
|
#include <libsolidity/analysis/experimental/TypeInference.h>
|
||||||
|
#include <libsolidity/analysis/experimental/Analysis.h>
|
||||||
#include <liblangutil/Exceptions.h>
|
#include <liblangutil/Exceptions.h>
|
||||||
|
|
||||||
#include <libyul/AsmAnalysis.h>
|
#include <libyul/AsmAnalysis.h>
|
||||||
#include <libyul/AsmAnalysisInfo.h>
|
#include <libyul/AsmAnalysisInfo.h>
|
||||||
#include <libyul/AST.h>
|
#include <libyul/AST.h>
|
||||||
|
|
||||||
|
#include <range/v3/view/transform.hpp>
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
using namespace solidity::frontend;
|
using namespace solidity::frontend;
|
||||||
using namespace solidity::frontend::experimental;
|
using namespace solidity::frontend::experimental;
|
||||||
using namespace solidity::langutil;
|
using namespace solidity::langutil;
|
||||||
|
|
||||||
|
TypeInference::TypeInference(Analysis& _analysis):
|
||||||
|
m_analysis(_analysis),
|
||||||
|
m_errorReporter(_analysis.errorReporter())
|
||||||
|
{
|
||||||
|
for (auto [type, name, arity]: std::initializer_list<std::tuple<BuiltinType, const char*, uint64_t>> {
|
||||||
|
{BuiltinType::Void, "void", 0},
|
||||||
|
{BuiltinType::Function, "fun", 2},
|
||||||
|
{BuiltinType::Unit, "unit", 0},
|
||||||
|
{BuiltinType::Pair, "pair", 2},
|
||||||
|
{BuiltinType::Word, "word", 0}
|
||||||
|
})
|
||||||
|
m_typeSystem.declareBuiltinType(type, name, arity);
|
||||||
|
m_voidType = m_typeSystem.builtinType(BuiltinType::Void, {});
|
||||||
|
m_wordType = m_typeSystem.builtinType(BuiltinType::Word, {});
|
||||||
|
m_env = make_unique<TypeEnvironment>(m_typeSystem);
|
||||||
|
|
||||||
|
m_typeAnnotations.resize(_analysis.maxAstId());
|
||||||
|
}
|
||||||
|
|
||||||
bool TypeInference::analyze(SourceUnit const& _sourceUnit)
|
bool TypeInference::analyze(SourceUnit const& _sourceUnit)
|
||||||
{
|
{
|
||||||
_sourceUnit.accept(*this);
|
_sourceUnit.accept(*this);
|
||||||
@ -37,13 +59,37 @@ bool TypeInference::analyze(SourceUnit const& _sourceUnit)
|
|||||||
|
|
||||||
bool TypeInference::visit(FunctionDefinition const& _functionDefinition)
|
bool TypeInference::visit(FunctionDefinition const& _functionDefinition)
|
||||||
{
|
{
|
||||||
ScopedSaveAndRestore env{m_env, {}};
|
auto& functionAnnotation = annotation(_functionDefinition);
|
||||||
|
if (functionAnnotation.type)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
Type functionType;
|
||||||
|
{
|
||||||
_functionDefinition.parameterList().accept(*this);
|
_functionDefinition.parameterList().accept(*this);
|
||||||
if (_functionDefinition.returnParameterList())
|
if (_functionDefinition.returnParameterList())
|
||||||
_functionDefinition.returnParameterList()->accept(*this);
|
_functionDefinition.returnParameterList()->accept(*this);
|
||||||
|
|
||||||
_functionDefinition.body().accept(*this);
|
_functionDefinition.body().accept(*this);
|
||||||
|
|
||||||
|
auto typeFromParameterList = [&](ParameterList const* _list) {
|
||||||
|
if (!_list)
|
||||||
|
return m_typeSystem.builtinType(BuiltinType::Unit, {});
|
||||||
|
return TypeSystemHelpers{m_typeSystem}.tupleType(_list->parameters() | ranges::view::transform([&](auto _param) {
|
||||||
|
auto& argAnnotation = annotation(*_param);
|
||||||
|
solAssert(argAnnotation.type);
|
||||||
|
return *argAnnotation.type;
|
||||||
|
}) | ranges::to<std::vector<Type>>);
|
||||||
|
};
|
||||||
|
|
||||||
|
Type argType = typeFromParameterList(&_functionDefinition.parameterList());
|
||||||
|
Type resultType = typeFromParameterList(_functionDefinition.returnParameterList().get());
|
||||||
|
|
||||||
|
functionType = m_typeSystem.builtinType(BuiltinType::Function, {argType, resultType});
|
||||||
|
}
|
||||||
|
functionAnnotation.type = functionType;
|
||||||
|
|
||||||
|
m_errorReporter.warning(0000_error, _functionDefinition.location(), m_typeSystem.typeToString(m_typeSystem.resolve(functionType)));
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -52,6 +98,20 @@ bool TypeInference::visit(ParameterList const&)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TypeInference::endVisit(ParameterList const& _parameterList)
|
||||||
|
{
|
||||||
|
auto& listAnnotation = annotation(_parameterList);
|
||||||
|
solAssert(!listAnnotation.type);
|
||||||
|
std::vector<Type> argTypes;
|
||||||
|
for(auto arg: _parameterList.parameters())
|
||||||
|
{
|
||||||
|
auto& argAnnotation = annotation(*arg);
|
||||||
|
solAssert(argAnnotation.type);
|
||||||
|
argTypes.emplace_back(*argAnnotation.type);
|
||||||
|
}
|
||||||
|
listAnnotation.type = TypeSystemHelpers{m_typeSystem}.tupleType(argTypes);
|
||||||
|
}
|
||||||
|
|
||||||
bool TypeInference::visitNode(ASTNode const& _node)
|
bool TypeInference::visitNode(ASTNode const& _node)
|
||||||
{
|
{
|
||||||
m_errorReporter.typeError(0000_error, _node.location(), "Unsupported AST node during type inference.");
|
m_errorReporter.typeError(0000_error, _node.location(), "Unsupported AST node during type inference.");
|
||||||
@ -65,7 +125,9 @@ experimental::Type TypeInference::fromTypeName(TypeName const& _typeName)
|
|||||||
switch(elementaryTypeName->typeName().token())
|
switch(elementaryTypeName->typeName().token())
|
||||||
{
|
{
|
||||||
case Token::Word:
|
case Token::Word:
|
||||||
return WordType{};
|
return m_wordType;
|
||||||
|
case Token::Void:
|
||||||
|
return m_voidType;
|
||||||
default:
|
default:
|
||||||
m_errorReporter.typeError(0000_error, _typeName.location(), "Only elementary types are supported.");
|
m_errorReporter.typeError(0000_error, _typeName.location(), "Only elementary types are supported.");
|
||||||
break;
|
break;
|
||||||
@ -73,7 +135,8 @@ experimental::Type TypeInference::fromTypeName(TypeName const& _typeName)
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
m_errorReporter.typeError(0000_error, _typeName.location(), "Only elementary types are supported.");
|
m_errorReporter.typeError(0000_error, _typeName.location(), "Only elementary types are supported.");
|
||||||
return m_env.freshFreeType();
|
// TODO: free type?
|
||||||
|
return m_typeSystem.freshTypeVariable();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -101,7 +164,9 @@ bool TypeInference::visit(InlineAssembly const& _inlineAssembly)
|
|||||||
Declaration const* declaration = identifierInfo.declaration;
|
Declaration const* declaration = identifierInfo.declaration;
|
||||||
solAssert(!!declaration, "");
|
solAssert(!!declaration, "");
|
||||||
|
|
||||||
m_env.assignType(m_typeSystem, declaration, WordType{});
|
auto& declarationAnnotation = annotation(*declaration);
|
||||||
|
solAssert(declarationAnnotation.type);
|
||||||
|
m_typeSystem.unify(*declarationAnnotation.type, m_wordType);
|
||||||
identifierInfo.valueSize = 1;
|
identifierInfo.valueSize = 1;
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
@ -120,19 +185,83 @@ bool TypeInference::visit(InlineAssembly const& _inlineAssembly)
|
|||||||
|
|
||||||
bool TypeInference::visit(VariableDeclaration const& _variableDeclaration)
|
bool TypeInference::visit(VariableDeclaration const& _variableDeclaration)
|
||||||
{
|
{
|
||||||
Type type = _variableDeclaration.hasTypeName() ? fromTypeName(_variableDeclaration.typeName()) : m_typeSystem.freshTypeVariable();
|
solAssert(!_variableDeclaration.value());
|
||||||
m_env.assignType(m_typeSystem, &_variableDeclaration, type);
|
auto& variableAnnotation = annotation(_variableDeclaration);
|
||||||
|
solAssert(!variableAnnotation.type);
|
||||||
|
variableAnnotation.type = [&] {
|
||||||
|
if (_variableDeclaration.hasTypeName())
|
||||||
|
return fromTypeName(_variableDeclaration.typeName());
|
||||||
|
else
|
||||||
|
return m_typeSystem.freshTypeVariable();
|
||||||
|
}();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TypeInference::visit(Assignment const& _assignment)
|
bool TypeInference::visit(Assignment const&)
|
||||||
{
|
{
|
||||||
(void)_assignment;
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TypeInference::endVisit(Assignment const& _assignment)
|
||||||
|
{
|
||||||
|
auto& assignmentAnnotation = annotation(_assignment);
|
||||||
|
solAssert(!assignmentAnnotation.type);
|
||||||
|
|
||||||
|
auto& lhsAnnotation = annotation(_assignment.leftHandSide());
|
||||||
|
solAssert(lhsAnnotation.type);
|
||||||
|
auto& rhsAnnotation = annotation(_assignment.rightHandSide());
|
||||||
|
solAssert(rhsAnnotation.type);
|
||||||
|
m_typeSystem.unify(*lhsAnnotation.type, *rhsAnnotation.type);
|
||||||
|
assignmentAnnotation.type = m_typeSystem.resolve(*lhsAnnotation.type);
|
||||||
|
}
|
||||||
|
|
||||||
|
TypeInference::TypeAnnotation& TypeInference::annotation(ASTNode const& _node)
|
||||||
|
{
|
||||||
|
auto& annotation = m_typeAnnotations.at(static_cast<size_t>(_node.id()));
|
||||||
|
if (!annotation)
|
||||||
|
annotation = make_unique<TypeAnnotation>();
|
||||||
|
return *annotation;
|
||||||
|
}
|
||||||
|
|
||||||
bool TypeInference::visit(Identifier const& _identifier)
|
bool TypeInference::visit(Identifier const& _identifier)
|
||||||
{
|
{
|
||||||
(void)_identifier;
|
auto& identifierAnnotation = annotation(_identifier);
|
||||||
|
solAssert(!identifierAnnotation.type);
|
||||||
|
|
||||||
|
auto const* referencedDeclaration = _identifier.annotation().referencedDeclaration;
|
||||||
|
solAssert(referencedDeclaration);
|
||||||
|
|
||||||
|
auto& declarationAnnotation = annotation(*referencedDeclaration);
|
||||||
|
if (!declarationAnnotation.type)
|
||||||
|
referencedDeclaration->accept(*this);
|
||||||
|
|
||||||
|
solAssert(declarationAnnotation.type);
|
||||||
|
identifierAnnotation.type = declarationAnnotation.type;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool TypeInference::visit(FunctionCall const&) { return true; }
|
||||||
|
void TypeInference::endVisit(FunctionCall const& _functionCall)
|
||||||
|
{
|
||||||
|
auto& functionCallAnnotation = annotation(_functionCall);
|
||||||
|
solAssert(!functionCallAnnotation.type);
|
||||||
|
|
||||||
|
auto& expressionAnnotation = annotation(_functionCall.expression());
|
||||||
|
solAssert(expressionAnnotation.type);
|
||||||
|
|
||||||
|
Type functionType = m_typeSystem.fresh(*expressionAnnotation.type);
|
||||||
|
|
||||||
|
std::vector<Type> argTypes;
|
||||||
|
for(auto arg: _functionCall.arguments())
|
||||||
|
{
|
||||||
|
auto& argAnnotation = annotation(*arg);
|
||||||
|
solAssert(argAnnotation.type);
|
||||||
|
argTypes.emplace_back(*argAnnotation.type);
|
||||||
|
}
|
||||||
|
Type argTuple = TypeSystemHelpers{m_typeSystem}.tupleType(argTypes);
|
||||||
|
Type genericFunctionType = TypeSystemHelpers{m_typeSystem}.functionType(argTuple, m_typeSystem.freshTypeVariable());
|
||||||
|
m_typeSystem.unify(genericFunctionType, functionType);
|
||||||
|
|
||||||
|
functionCallAnnotation.type = m_typeSystem.resolve(std::get<1>(TypeSystemHelpers{m_typeSystem}.destFunctionType(m_typeSystem.resolve(genericFunctionType))));
|
||||||
|
}
|
||||||
|
@ -27,10 +27,12 @@
|
|||||||
namespace solidity::frontend::experimental
|
namespace solidity::frontend::experimental
|
||||||
{
|
{
|
||||||
|
|
||||||
|
class Analysis;
|
||||||
|
|
||||||
class TypeInference: public ASTConstVisitor
|
class TypeInference: public ASTConstVisitor
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
TypeInference(langutil::ErrorReporter& _errorReporter): m_errorReporter(_errorReporter) {}
|
TypeInference(Analysis& _analysis);
|
||||||
|
|
||||||
bool analyze(SourceUnit const& _sourceUnit);
|
bool analyze(SourceUnit const& _sourceUnit);
|
||||||
private:
|
private:
|
||||||
@ -40,6 +42,7 @@ private:
|
|||||||
|
|
||||||
bool visit(FunctionDefinition const& _functionDefinition) override;
|
bool visit(FunctionDefinition const& _functionDefinition) override;
|
||||||
bool visit(ParameterList const& _parameterList) override;
|
bool visit(ParameterList const& _parameterList) override;
|
||||||
|
void endVisit(ParameterList const& _parameterList) override;
|
||||||
bool visit(SourceUnit const&) override { return true; }
|
bool visit(SourceUnit const&) override { return true; }
|
||||||
bool visit(ContractDefinition const&) override { return true; }
|
bool visit(ContractDefinition const&) override { return true; }
|
||||||
bool visit(InlineAssembly const& _inlineAssembly) override;
|
bool visit(InlineAssembly const& _inlineAssembly) override;
|
||||||
@ -47,14 +50,29 @@ private:
|
|||||||
|
|
||||||
bool visit(ExpressionStatement const&) override { return true; }
|
bool visit(ExpressionStatement const&) override { return true; }
|
||||||
bool visit(Assignment const&) override;
|
bool visit(Assignment const&) override;
|
||||||
|
void endVisit(Assignment const&) override;
|
||||||
bool visit(Identifier const&) override;
|
bool visit(Identifier const&) override;
|
||||||
|
bool visit(FunctionCall const& _functionCall) override;
|
||||||
|
void endVisit(FunctionCall const& _functionCall) override;
|
||||||
|
|
||||||
bool visitNode(ASTNode const& _node) override;
|
bool visitNode(ASTNode const& _node) override;
|
||||||
|
|
||||||
Type fromTypeName(TypeName const& _typeName);
|
Type fromTypeName(TypeName const& _typeName);
|
||||||
TypeSystem m_typeSystem;
|
Analysis& m_analysis;
|
||||||
langutil::ErrorReporter& m_errorReporter;
|
langutil::ErrorReporter& m_errorReporter;
|
||||||
TypeEnvironment m_env;
|
TypeSystem m_typeSystem;
|
||||||
|
std::unique_ptr<TypeEnvironment> m_env;
|
||||||
|
Type m_voidType;
|
||||||
|
Type m_wordType;
|
||||||
|
|
||||||
|
struct TypeAnnotation
|
||||||
|
{
|
||||||
|
std::optional<Type> type;
|
||||||
|
};
|
||||||
|
|
||||||
|
TypeAnnotation& annotation(ASTNode const& _node);
|
||||||
|
|
||||||
|
std::vector<std::unique_ptr<TypeAnnotation>> m_typeAnnotations;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
@ -18,47 +18,225 @@
|
|||||||
|
|
||||||
|
|
||||||
#include <libsolidity/ast/experimental/TypeSystem.h>
|
#include <libsolidity/ast/experimental/TypeSystem.h>
|
||||||
|
#include <libsolidity/ast/AST.h>
|
||||||
#include <liblangutil/Exceptions.h>
|
#include <liblangutil/Exceptions.h>
|
||||||
|
|
||||||
|
#include <libsolutil/Visitor.h>
|
||||||
|
|
||||||
|
#include <range/v3/to_container.hpp>
|
||||||
|
#include <range/v3/view/drop_exactly.hpp>
|
||||||
|
#include <range/v3/view/drop_last.hpp>
|
||||||
|
#include <range/v3/view/reverse.hpp>
|
||||||
|
#include <range/v3/view/zip.hpp>
|
||||||
|
|
||||||
|
#include <fmt/format.h>
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
using namespace solidity::frontend;
|
using namespace solidity::frontend;
|
||||||
using namespace solidity::frontend::experimental;
|
using namespace solidity::frontend::experimental;
|
||||||
|
|
||||||
void TypeEnvironment::assignType(TypeSystem& _typeSystem, Declaration const* _declaration, Type _typeAssignment)
|
std::string TypeSystem::typeToString(Type const& _type) const
|
||||||
|
{
|
||||||
|
return std::visit(util::GenericVisitor{
|
||||||
|
[&](AtomicType const& _type) {
|
||||||
|
std::stringstream stream;
|
||||||
|
if (!_type.arguments.empty())
|
||||||
|
{
|
||||||
|
stream << "(";
|
||||||
|
for (auto type: _type.arguments | ranges::views::drop_last(1))
|
||||||
|
stream << typeToString(type) << ", ";
|
||||||
|
stream << typeToString(_type.arguments.back());
|
||||||
|
stream << ") ";
|
||||||
|
}
|
||||||
|
stream << std::visit(util::GenericVisitor{
|
||||||
|
[&](Declaration const* _declaration) {
|
||||||
|
return _declaration->name();
|
||||||
|
},
|
||||||
|
[&](BuiltinType _builtinType) -> string {
|
||||||
|
return builtinTypeName(_builtinType);
|
||||||
|
}
|
||||||
|
}, _type.constructor);
|
||||||
|
return stream.str();
|
||||||
|
},
|
||||||
|
[](FreeTypeVariable const& _type) {
|
||||||
|
return fmt::format("free[{}]", _type.index());
|
||||||
|
},
|
||||||
|
[](GenericTypeVariable const& _type) {
|
||||||
|
return fmt::format("var[{}]", _type.index());
|
||||||
|
},
|
||||||
|
}, resolve(_type));
|
||||||
|
}
|
||||||
|
|
||||||
|
void TypeEnvironment::assignType(Declaration const* _declaration, Type _typeAssignment)
|
||||||
{
|
{
|
||||||
auto&& [type, newlyInserted] = m_types.emplace(std::piecewise_construct, std::forward_as_tuple(_declaration), std::forward_as_tuple(std::move(_typeAssignment)));
|
auto&& [type, newlyInserted] = m_types.emplace(std::piecewise_construct, std::forward_as_tuple(_declaration), std::forward_as_tuple(std::move(_typeAssignment)));
|
||||||
if (!newlyInserted)
|
if (!newlyInserted)
|
||||||
{
|
{
|
||||||
unify(_typeSystem, type->second, _typeAssignment);
|
m_parent.unify(type->second, _typeAssignment);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Type TypeEnvironment::lookup(TypeSystem& _typeSystem, Declaration const* _declaration)
|
std::optional<experimental::Type> TypeEnvironment::lookup(Declaration const* _declaration)
|
||||||
{
|
{
|
||||||
if (m_types.count(_declaration))
|
if (m_types.count(_declaration))
|
||||||
return m_types[_declaration];
|
return m_types[_declaration];
|
||||||
Type result = _typeSystem.freshTypeVariable();
|
return std::nullopt;
|
||||||
m_types.emplace(std::piecewise_construct, std::forward_as_tuple(_declaration), std::forward_as_tuple(result));
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void TypeSystem::unify(Type _a, Type _b)
|
||||||
|
{
|
||||||
|
auto unificationFailure = [&]() {
|
||||||
|
solAssert(false, fmt::format("cannot unify {} and {}", typeToString(_a), typeToString(_b)));
|
||||||
|
};
|
||||||
|
_a = resolve(_a);
|
||||||
|
_b = resolve(_b);
|
||||||
|
std::visit(util::GenericVisitor{
|
||||||
|
[&](GenericTypeVariable _left, GenericTypeVariable _right) {
|
||||||
|
validate(_left);
|
||||||
|
validate(_right);
|
||||||
|
if (_left.index() != _right.index())
|
||||||
|
instantiate(_left, _right);
|
||||||
|
},
|
||||||
|
[&](GenericTypeVariable _var, auto) {
|
||||||
|
instantiate(_var, _b);
|
||||||
|
},
|
||||||
|
[&](auto, GenericTypeVariable _var) {
|
||||||
|
instantiate(_var, _a);
|
||||||
|
},
|
||||||
|
[&](AtomicType _atomicLeft, AtomicType _atomicRight) {
|
||||||
|
if(_atomicLeft.constructor != _atomicRight.constructor)
|
||||||
|
unificationFailure();
|
||||||
|
if (_atomicLeft.arguments.size() != _atomicRight.arguments.size())
|
||||||
|
unificationFailure();
|
||||||
|
for (auto&& [left, right]: ranges::zip_view(_atomicLeft.arguments, _atomicRight.arguments))
|
||||||
|
unify(left, right);
|
||||||
|
},
|
||||||
|
[&](auto, auto) {
|
||||||
|
unificationFailure();
|
||||||
|
}
|
||||||
|
}, _a, _b);
|
||||||
|
}
|
||||||
|
|
||||||
|
unique_ptr<TypeEnvironment> TypeEnvironment::fresh() const
|
||||||
|
{
|
||||||
|
auto newEnv = make_unique<TypeEnvironment>(m_parent);
|
||||||
|
for(auto [decl,type]: m_types)
|
||||||
|
newEnv->m_types.emplace(decl, type);
|
||||||
|
return newEnv;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
experimental::Type TypeSystem::freshTypeVariable()
|
||||||
|
{
|
||||||
|
uint64_t index = m_typeVariables.size();
|
||||||
|
m_typeVariables.emplace_back(std::nullopt);
|
||||||
|
return GenericTypeVariable(*this, index);
|
||||||
|
}
|
||||||
|
|
||||||
|
experimental::Type TypeSystem::freshFreeType()
|
||||||
|
{
|
||||||
|
uint64_t index = m_freeTypes.size();
|
||||||
|
m_freeTypes.emplace_back(std::nullopt);
|
||||||
|
return FreeTypeVariable(*this, index);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TypeSystem::instantiate(GenericTypeVariable _variable, Type _type)
|
||||||
|
{
|
||||||
|
validate(_variable);
|
||||||
|
solAssert(!m_typeVariables.at(_variable.index()).has_value());
|
||||||
|
solAssert(_variable.m_parent == this);
|
||||||
|
m_typeVariables[_variable.index()] = _type;
|
||||||
|
}
|
||||||
|
|
||||||
|
experimental::Type TypeSystem::resolve(Type _type) const
|
||||||
|
{
|
||||||
|
Type result = _type;
|
||||||
|
while(auto const* var = std::get_if<GenericTypeVariable>(&result))
|
||||||
|
if (auto value = m_typeVariables.at(var->index()))
|
||||||
|
result = *value;
|
||||||
|
else
|
||||||
|
break;
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
Type TypeEnvironment::freshFreeType()
|
|
||||||
|
void TypeSystem::declareBuiltinType(BuiltinType _builtinType, std::string _name, uint64_t _arity)
|
||||||
{
|
{
|
||||||
return FreeType{m_numFreeTypes++};
|
solAssert(m_builtinTypes.count(_builtinType) == 0, "Builtin type already declared.");
|
||||||
|
m_builtinTypes[_builtinType] = TypeConstructorInfo{
|
||||||
|
_name,
|
||||||
|
_arity
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
experimental::Type TypeSystem::builtinType(BuiltinType _builtinType, std::vector<Type> _arguments) const
|
||||||
|
{
|
||||||
|
auto const& info = m_builtinTypes.at(_builtinType);
|
||||||
|
solAssert(info.arity == _arguments.size(), "Invalid arity.");
|
||||||
|
return AtomicType{_builtinType, _arguments};
|
||||||
|
}
|
||||||
|
|
||||||
|
void TypeSystem::validate(GenericTypeVariable _variable) const
|
||||||
|
{
|
||||||
|
solAssert(_variable.m_parent == this);
|
||||||
|
solAssert(_variable.index() < m_typeVariables.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
experimental::Type TypeSystemHelpers::tupleType(vector<Type> _elements) const
|
||||||
|
{
|
||||||
|
if (_elements.empty())
|
||||||
|
return typeSystem.builtinType(BuiltinType::Unit, {});
|
||||||
|
if (_elements.size() == 1)
|
||||||
|
return _elements.front();
|
||||||
|
Type result = _elements.back();
|
||||||
|
for (Type type: _elements | ranges::view::reverse | ranges::view::drop_exactly(1))
|
||||||
|
result = typeSystem.builtinType(BuiltinType::Pair, {type, result});
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
experimental::Type TypeSystemHelpers::functionType(experimental::Type _argType, experimental::Type _resultType) const
|
||||||
|
{
|
||||||
|
return typeSystem.builtinType(BuiltinType::Function, {_argType, _resultType});
|
||||||
|
}
|
||||||
|
|
||||||
|
experimental::Type TypeSystem::fresh(Type _type)
|
||||||
|
{
|
||||||
|
return std::visit(util::GenericVisitor{
|
||||||
|
[&](AtomicType const& _type) -> Type {
|
||||||
|
return AtomicType{
|
||||||
|
_type.constructor,
|
||||||
|
_type.arguments | ranges::view::transform([&](Type _argType) {
|
||||||
|
return fresh(_argType);
|
||||||
|
}) | ranges::to<vector<Type>>
|
||||||
|
};
|
||||||
|
},
|
||||||
|
[&](FreeTypeVariable const&) {
|
||||||
|
return _type;
|
||||||
|
},
|
||||||
|
[&](GenericTypeVariable const&) {
|
||||||
|
return freshTypeVariable();
|
||||||
|
},
|
||||||
|
}, resolve(_type));
|
||||||
|
}
|
||||||
|
|
||||||
|
tuple<AtomicType::Constructor, vector<experimental::Type>> TypeSystemHelpers::destAtomicType(Type _functionType) const
|
||||||
|
{
|
||||||
|
using ResultType = tuple<AtomicType::Constructor, vector<Type>>;
|
||||||
|
return std::visit(util::GenericVisitor{
|
||||||
|
[&](AtomicType const& _type) -> ResultType {
|
||||||
|
return std::make_tuple(_type.constructor, _type.arguments);
|
||||||
|
},
|
||||||
|
[](auto) -> ResultType {
|
||||||
|
solAssert(false);
|
||||||
|
}
|
||||||
|
}, _functionType);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void TypeEnvironment::unify(TypeSystem& _context, Type _a, Type _b)
|
tuple<experimental::Type, experimental::Type> TypeSystemHelpers::destFunctionType(Type _functionType) const
|
||||||
{
|
{
|
||||||
_a = _context.resolve(_a);
|
auto [constructor, arguments] = destAtomicType(_functionType);
|
||||||
_b = _context.resolve(_b);
|
auto const* builtinType = get_if<BuiltinType>(&constructor);
|
||||||
if (auto* varA = get_if<TypeVariable>(&_a))
|
solAssert(builtinType && *builtinType == BuiltinType::Function);
|
||||||
_context.instantiate(*varA, _b);
|
solAssert(arguments.size() == 2);
|
||||||
else if (holds_alternative<WordType>(_a))
|
return make_tuple(arguments.front(), arguments.back());
|
||||||
{
|
|
||||||
if (holds_alternative<WordType>(_b))
|
|
||||||
return;
|
|
||||||
else
|
|
||||||
solAssert(false, "unification failed");
|
|
||||||
}
|
|
||||||
|
|
||||||
solAssert(false, fmt::format("cannot unify {} and {}", typeToString(_a), typeToString(_b)));
|
|
||||||
}
|
}
|
@ -19,8 +19,6 @@
|
|||||||
|
|
||||||
#include <liblangutil/Exceptions.h>
|
#include <liblangutil/Exceptions.h>
|
||||||
|
|
||||||
#include <fmt/format.h>
|
|
||||||
|
|
||||||
#include <optional>
|
#include <optional>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <variant>
|
#include <variant>
|
||||||
@ -35,99 +33,64 @@ namespace solidity::frontend::experimental
|
|||||||
{
|
{
|
||||||
|
|
||||||
class TypeSystem;
|
class TypeSystem;
|
||||||
|
class TypeEnvironment;
|
||||||
|
|
||||||
struct SumType;
|
struct AtomicType;
|
||||||
struct TupleType;
|
struct GenericTypeVariable;
|
||||||
struct FunctionType;
|
struct FreeTypeVariable;
|
||||||
struct WordType;
|
|
||||||
struct UserDefinedType;
|
|
||||||
struct TypeVariable;
|
|
||||||
struct FreeType;
|
|
||||||
|
|
||||||
using Type = std::variant<SumType, TupleType, FunctionType, WordType, UserDefinedType, TypeVariable, FreeType>;
|
using Type = std::variant<AtomicType, GenericTypeVariable, FreeTypeVariable>;
|
||||||
|
|
||||||
struct SumType
|
enum class BuiltinType
|
||||||
{
|
{
|
||||||
std::vector<Type const*> alternatives;
|
Void,
|
||||||
std::string toString() const
|
Function,
|
||||||
{
|
Unit,
|
||||||
return "sum";
|
Pair,
|
||||||
}
|
Word
|
||||||
};
|
};
|
||||||
|
|
||||||
struct TupleType
|
struct AtomicType
|
||||||
{
|
{
|
||||||
std::vector<Type const*> components;
|
using Constructor = std::variant<BuiltinType, Declaration const*>;
|
||||||
std::string toString() const
|
Constructor constructor;
|
||||||
{
|
std::vector<Type> arguments;
|
||||||
return "tuple";
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct FunctionType
|
struct FreeTypeVariable
|
||||||
{
|
{
|
||||||
Type const* codomain = nullptr;
|
TypeSystem const& parent() const { return *m_parent; }
|
||||||
Type const* domain = nullptr;
|
|
||||||
std::string toString() const
|
|
||||||
{
|
|
||||||
return "fun";
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct WordType
|
|
||||||
{
|
|
||||||
std::string toString() const
|
|
||||||
{
|
|
||||||
return "word";
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct UserDefinedType
|
|
||||||
{
|
|
||||||
Declaration const* declaration = nullptr;
|
|
||||||
std::vector<Type const*> arguments;
|
|
||||||
std::string toString() const
|
|
||||||
{
|
|
||||||
return "user_defined_type";
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct TypeVariable
|
|
||||||
{
|
|
||||||
std::string toString() const
|
|
||||||
{
|
|
||||||
return fmt::format("var<{}>", m_index);
|
|
||||||
}
|
|
||||||
private:
|
|
||||||
uint64_t index() const { return m_index; }
|
uint64_t index() const { return m_index; }
|
||||||
|
private:
|
||||||
friend class TypeSystem;
|
friend class TypeSystem;
|
||||||
|
TypeSystem const* m_parent = nullptr;
|
||||||
uint64_t m_index = 0;
|
uint64_t m_index = 0;
|
||||||
TypeVariable(uint64_t _index): m_index(_index) {}
|
FreeTypeVariable(TypeSystem const& _parent, uint64_t _index): m_parent(&_parent), m_index(_index) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct FreeType
|
struct GenericTypeVariable
|
||||||
{
|
{
|
||||||
uint64_t index = 0;
|
TypeSystem const& parent() const { return *m_parent; }
|
||||||
std::string toString() const
|
uint64_t index() const { return m_index; }
|
||||||
{
|
private:
|
||||||
return fmt::format("free<{}>", index);
|
friend class TypeSystem;
|
||||||
}
|
TypeSystem const* m_parent = nullptr;
|
||||||
|
uint64_t m_index = 0;
|
||||||
|
GenericTypeVariable(TypeSystem const& _parent, uint64_t _index): m_parent(&_parent), m_index(_index) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
inline std::string typeToString(Type const& _type)
|
|
||||||
{
|
|
||||||
return std::visit([](auto _type) { return _type.toString(); }, _type);
|
|
||||||
}
|
|
||||||
|
|
||||||
class TypeEnvironment
|
class TypeEnvironment
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
void assignType(TypeSystem& _typeSystem, Declaration const* _declaration, Type _typeAssignment);
|
TypeEnvironment(TypeSystem& _parent): m_parent(_parent) {}
|
||||||
Type lookup(TypeSystem& _typeSystem, Declaration const* _declaration);
|
TypeEnvironment(TypeEnvironment const& _env) = delete;
|
||||||
Type freshFreeType();
|
TypeEnvironment& operator=(TypeEnvironment const& _env) = delete;
|
||||||
void unify(TypeSystem& _typeSystem, Type _a, Type _b);
|
std::unique_ptr<TypeEnvironment> fresh() const;
|
||||||
|
void assignType(Declaration const* _declaration, Type _typeAssignment);
|
||||||
|
std::optional<Type> lookup(Declaration const* _declaration);
|
||||||
private:
|
private:
|
||||||
uint64_t m_numFreeTypes = 0;
|
TypeSystem& m_parent;
|
||||||
std::map<Declaration const*, Type> m_types;
|
std::map<Declaration const*, Type> m_types;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -138,30 +101,38 @@ public:
|
|||||||
TypeSystem() {}
|
TypeSystem() {}
|
||||||
TypeSystem(TypeSystem const&) = delete;
|
TypeSystem(TypeSystem const&) = delete;
|
||||||
TypeSystem const& operator=(TypeSystem const&) = delete;
|
TypeSystem const& operator=(TypeSystem const&) = delete;
|
||||||
Type freshTypeVariable()
|
void declareBuiltinType(BuiltinType _builtinType, std::string _name, uint64_t _arity);
|
||||||
|
Type builtinType(BuiltinType _builtinType, std::vector<Type> _arguments) const;
|
||||||
|
std::string builtinTypeName(BuiltinType _builtinType) const
|
||||||
{
|
{
|
||||||
uint64_t index = m_typeVariables.size();
|
return m_builtinTypes.at(_builtinType).name;
|
||||||
m_typeVariables.emplace_back(std::nullopt);
|
|
||||||
return TypeVariable(index);
|
|
||||||
}
|
|
||||||
void instantiate(TypeVariable _variable, Type _type)
|
|
||||||
{
|
|
||||||
solAssert(_variable.index() < m_typeVariables.size());
|
|
||||||
solAssert(!m_typeVariables.at(_variable.index()).has_value());
|
|
||||||
m_typeVariables[_variable.index()] = _type;
|
|
||||||
}
|
|
||||||
Type resolve(Type _type)
|
|
||||||
{
|
|
||||||
Type result = _type;
|
|
||||||
while(auto const* var = std::get_if<TypeVariable>(&result))
|
|
||||||
if (auto value = m_typeVariables.at(var->index()))
|
|
||||||
result = *value;
|
|
||||||
else
|
|
||||||
break;
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
Type freshFreeType();
|
||||||
|
void instantiate(GenericTypeVariable _variable, Type _type);
|
||||||
|
void validate(GenericTypeVariable _variable) const;
|
||||||
|
Type resolve(Type _type) const;
|
||||||
|
std::string typeToString(Type const& _type) const;
|
||||||
|
Type freshTypeVariable();
|
||||||
|
Type fresh(Type _type);
|
||||||
|
void unify(Type _a, Type _b);
|
||||||
private:
|
private:
|
||||||
|
std::vector<std::optional<Type>> m_freeTypes;
|
||||||
|
struct TypeConstructorInfo
|
||||||
|
{
|
||||||
|
std::string name;
|
||||||
|
uint64_t arity = 0;
|
||||||
|
};
|
||||||
|
std::map<BuiltinType, TypeConstructorInfo> m_builtinTypes;
|
||||||
std::vector<std::optional<Type>> m_typeVariables;
|
std::vector<std::optional<Type>> m_typeVariables;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct TypeSystemHelpers
|
||||||
|
{
|
||||||
|
TypeSystem& typeSystem;
|
||||||
|
Type tupleType(std::vector<Type> _elements) const;
|
||||||
|
Type functionType(Type _argType, Type _resultType) const;
|
||||||
|
std::tuple<AtomicType::Constructor, std::vector<Type>> destAtomicType(Type _functionType) const;
|
||||||
|
std::tuple<Type, Type> destFunctionType(Type _functionType) const;
|
||||||
|
};
|
||||||
|
|
||||||
}
|
}
|
43
libsolidity/codegen/experimental/IRGenerationContext.h
Normal file
43
libsolidity/codegen/experimental/IRGenerationContext.h
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
/*
|
||||||
|
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/>.
|
||||||
|
*/
|
||||||
|
// SPDX-License-Identifier: GPL-3.0
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <libsolidity/ast/ASTForward.h>
|
||||||
|
|
||||||
|
#include <list>
|
||||||
|
#include <set>
|
||||||
|
|
||||||
|
namespace solidity::frontend::experimental
|
||||||
|
{
|
||||||
|
|
||||||
|
class Analysis;
|
||||||
|
|
||||||
|
struct IRGenerationContext
|
||||||
|
{
|
||||||
|
Analysis const& analysis;
|
||||||
|
std::list<FunctionDefinition const*> functionQueue;
|
||||||
|
std::set<FunctionDefinition const*> generatedFunctions;
|
||||||
|
void enqueueFunctionDefinition(FunctionDefinition const* _functionDefinition)
|
||||||
|
{
|
||||||
|
if (!generatedFunctions.count(_functionDefinition))
|
||||||
|
functionQueue.emplace_back(_functionDefinition);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
@ -18,6 +18,7 @@
|
|||||||
|
|
||||||
#include <libsolidity/codegen/experimental/IRGenerator.h>
|
#include <libsolidity/codegen/experimental/IRGenerator.h>
|
||||||
|
|
||||||
|
#include <libsolidity/codegen/experimental/IRGenerationContext.h>
|
||||||
#include <libsolidity/codegen/experimental/IRGeneratorForStatements.h>
|
#include <libsolidity/codegen/experimental/IRGeneratorForStatements.h>
|
||||||
|
|
||||||
#include <libsolidity/codegen/ir/Common.h>
|
#include <libsolidity/codegen/ir/Common.h>
|
||||||
@ -31,6 +32,8 @@
|
|||||||
|
|
||||||
#include <libsolutil/Whiskers.h>
|
#include <libsolutil/Whiskers.h>
|
||||||
|
|
||||||
|
#include <range/v3/view/drop_last.hpp>
|
||||||
|
|
||||||
#include <variant>
|
#include <variant>
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
@ -43,7 +46,7 @@ string IRGenerator::run(
|
|||||||
ContractDefinition const& _contract,
|
ContractDefinition const& _contract,
|
||||||
bytes const& /*_cborMetadata*/,
|
bytes const& /*_cborMetadata*/,
|
||||||
map<ContractDefinition const*, string_view const> const& /*_otherYulSources*/
|
map<ContractDefinition const*, string_view const> const& /*_otherYulSources*/
|
||||||
) const
|
)
|
||||||
{
|
{
|
||||||
|
|
||||||
Whiskers t(R"(
|
Whiskers t(R"(
|
||||||
@ -66,30 +69,52 @@ string IRGenerator::run(
|
|||||||
return t.render();
|
return t.render();
|
||||||
}
|
}
|
||||||
|
|
||||||
string IRGenerator::generate(ContractDefinition const& _contract) const
|
string IRGenerator::generate(ContractDefinition const& _contract)
|
||||||
{
|
{
|
||||||
std::stringstream code;
|
std::stringstream code;
|
||||||
code << "{\n";
|
code << "{\n";
|
||||||
if (_contract.fallbackFunction())
|
if (_contract.fallbackFunction())
|
||||||
{
|
{
|
||||||
code << IRNames::function(*_contract.fallbackFunction()) << "()\n";
|
code << IRNames::function(*_contract.fallbackFunction()) << "()\n";
|
||||||
|
m_context.functionQueue.emplace_front(_contract.fallbackFunction());
|
||||||
}
|
}
|
||||||
code << "revert(0,0)\n";
|
code << "revert(0,0)\n";
|
||||||
code << "}\n";
|
code << "}\n";
|
||||||
|
|
||||||
for (FunctionDefinition const* f: _contract.definedFunctions())
|
while (!m_context.functionQueue.empty())
|
||||||
code << generate(*f);
|
{
|
||||||
|
FunctionDefinition const* function = m_context.functionQueue.front();
|
||||||
|
m_context.functionQueue.pop_front();
|
||||||
|
m_context.generatedFunctions.insert(function);
|
||||||
|
code << generate(*function);
|
||||||
|
}
|
||||||
|
|
||||||
return code.str();
|
return code.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
string IRGenerator::generate(FunctionDefinition const& _function) const
|
string IRGenerator::generate(FunctionDefinition const& _function)
|
||||||
{
|
{
|
||||||
std::stringstream code;
|
std::stringstream code;
|
||||||
code << "function " << IRNames::function(_function) << "() {\n";
|
code << "function " << IRNames::function(_function) << "(";
|
||||||
|
if (_function.parameters().size() > 1)
|
||||||
|
for (auto const& arg: _function.parameters() | ranges::view::drop_last(1))
|
||||||
|
code << IRNames::localVariable(*arg) << ", ";
|
||||||
|
if (!_function.parameters().empty())
|
||||||
|
code << IRNames::localVariable(*_function.parameters().back());
|
||||||
|
code << ")";
|
||||||
|
if (_function.returnParameterList() && !_function.returnParameters().empty())
|
||||||
|
{
|
||||||
|
code << " -> ";
|
||||||
|
if (_function.returnParameters().size() > 1)
|
||||||
|
for (auto const& arg: _function.returnParameters() | ranges::view::drop_last(1))
|
||||||
|
code << IRNames::localVariable(*arg) << ", ";
|
||||||
|
if (!_function.returnParameters().empty())
|
||||||
|
code << IRNames::localVariable(*_function.returnParameters().back());
|
||||||
|
}
|
||||||
|
code << "{\n";
|
||||||
for (auto _statement: _function.body().statements())
|
for (auto _statement: _function.body().statements())
|
||||||
{
|
{
|
||||||
IRGeneratorForStatements statementGenerator{m_analysis};
|
IRGeneratorForStatements statementGenerator{m_context};
|
||||||
code << statementGenerator.generate(*_statement);
|
code << statementGenerator.generate(*_statement);
|
||||||
}
|
}
|
||||||
code << "}\n";
|
code << "}\n";
|
||||||
|
@ -18,6 +18,7 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <libsolidity/codegen/experimental/IRGenerationContext.h>
|
||||||
#include <libsolidity/interface/DebugSettings.h>
|
#include <libsolidity/interface/DebugSettings.h>
|
||||||
#include <libsolidity/interface/OptimiserSettings.h>
|
#include <libsolidity/interface/OptimiserSettings.h>
|
||||||
#include <libsolidity/ast/ASTForward.h>
|
#include <libsolidity/ast/ASTForward.h>
|
||||||
@ -53,24 +54,24 @@ public:
|
|||||||
m_eofVersion(_eofVersion),
|
m_eofVersion(_eofVersion),
|
||||||
m_debugInfoSelection(_debugInfoSelection),
|
m_debugInfoSelection(_debugInfoSelection),
|
||||||
m_soliditySourceProvider(_soliditySourceProvider),
|
m_soliditySourceProvider(_soliditySourceProvider),
|
||||||
m_analysis(_analysis)
|
m_context{_analysis, {}, {}}
|
||||||
{}
|
{}
|
||||||
|
|
||||||
std::string run(
|
std::string run(
|
||||||
ContractDefinition const& _contract,
|
ContractDefinition const& _contract,
|
||||||
bytes const& _cborMetadata,
|
bytes const& _cborMetadata,
|
||||||
std::map<ContractDefinition const*, std::string_view const> const& _otherYulSources
|
std::map<ContractDefinition const*, std::string_view const> const& _otherYulSources
|
||||||
) const;
|
);
|
||||||
|
|
||||||
std::string generate(ContractDefinition const& _contract) const;
|
std::string generate(ContractDefinition const& _contract);
|
||||||
std::string generate(FunctionDefinition const& _function) const;
|
std::string generate(FunctionDefinition const& _function);
|
||||||
private:
|
private:
|
||||||
langutil::EVMVersion const m_evmVersion;
|
langutil::EVMVersion const m_evmVersion;
|
||||||
std::optional<uint8_t> const m_eofVersion;
|
std::optional<uint8_t> const m_eofVersion;
|
||||||
OptimiserSettings const m_optimiserSettings;
|
OptimiserSettings const m_optimiserSettings;
|
||||||
langutil::DebugInfoSelection m_debugInfoSelection = {};
|
langutil::DebugInfoSelection m_debugInfoSelection = {};
|
||||||
langutil::CharStreamProvider const* m_soliditySourceProvider = nullptr;
|
langutil::CharStreamProvider const* m_soliditySourceProvider = nullptr;
|
||||||
Analysis const& m_analysis;
|
IRGenerationContext m_context;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -25,6 +25,8 @@
|
|||||||
|
|
||||||
#include <libsolidity/codegen/ir/Common.h>
|
#include <libsolidity/codegen/ir/Common.h>
|
||||||
|
|
||||||
|
#include <range/v3/view/drop_last.hpp>
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
using namespace solidity;
|
using namespace solidity;
|
||||||
using namespace solidity::util;
|
using namespace solidity::util;
|
||||||
@ -104,7 +106,7 @@ bool IRGeneratorForStatements::visit(InlineAssembly const& _assembly)
|
|||||||
CopyTranslate bodyCopier{_assembly.dialect(), _assembly.annotation().externalReferences};
|
CopyTranslate bodyCopier{_assembly.dialect(), _assembly.annotation().externalReferences};
|
||||||
yul::Statement modified = bodyCopier(_assembly.operations());
|
yul::Statement modified = bodyCopier(_assembly.operations());
|
||||||
solAssert(holds_alternative<yul::Block>(modified));
|
solAssert(holds_alternative<yul::Block>(modified));
|
||||||
m_code << yul::AsmPrinter()(std::get<yul::Block>(modified));
|
m_code << yul::AsmPrinter()(std::get<yul::Block>(modified)) << "\n";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -119,6 +121,55 @@ bool IRGeneratorForStatements::visit(VariableDeclarationStatement const& _variab
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool IRGeneratorForStatements::visit(ExpressionStatement const&)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IRGeneratorForStatements::visit(Identifier const& _identifier)
|
||||||
|
{
|
||||||
|
auto const* rhsVar = dynamic_cast<VariableDeclaration const*>(_identifier.annotation().referencedDeclaration);
|
||||||
|
solAssert(rhsVar, "Can only reference identifiers referring to variables.");
|
||||||
|
m_code << "let " << IRNames::localVariable(_identifier) << " := " << IRNames::localVariable(*rhsVar) << "\n";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IRGeneratorForStatements::visit(FunctionCall const& _functionCall)
|
||||||
|
{
|
||||||
|
for(auto arg: _functionCall.arguments())
|
||||||
|
arg->accept(*this);
|
||||||
|
|
||||||
|
auto const* identifier = dynamic_cast<Identifier const*>(&_functionCall.expression());
|
||||||
|
solAssert(identifier, "Complex function call expressions not supported.");
|
||||||
|
auto const* functionDefinition = dynamic_cast<FunctionDefinition const*>(identifier->annotation().referencedDeclaration);
|
||||||
|
solAssert(functionDefinition, "Function call expression must refer to a function definition.");
|
||||||
|
m_context.enqueueFunctionDefinition(functionDefinition);
|
||||||
|
|
||||||
|
m_code << "let " << IRNames::localVariable(_functionCall) << " := " << IRNames::function(*functionDefinition) << "(";
|
||||||
|
auto const& arguments = _functionCall.arguments();
|
||||||
|
if (arguments.size() > 1)
|
||||||
|
for (auto arg: arguments | ranges::view::drop_last(1))
|
||||||
|
m_code << IRNames::localVariable(*arg) << ", ";
|
||||||
|
if (!arguments.empty())
|
||||||
|
m_code << IRNames::localVariable(*arguments.back());
|
||||||
|
m_code << ")\n";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IRGeneratorForStatements::visit(Assignment const& _assignment)
|
||||||
|
{
|
||||||
|
_assignment.rightHandSide().accept(*this);
|
||||||
|
auto const* lhs = dynamic_cast<Identifier const*>(&_assignment.leftHandSide());
|
||||||
|
solAssert(lhs, "Can only assign to identifiers.");
|
||||||
|
auto const* lhsVar = dynamic_cast<VariableDeclaration const*>(lhs->annotation().referencedDeclaration);
|
||||||
|
solAssert(lhsVar, "Can only assign to identifiers referring to variables.");
|
||||||
|
m_code << IRNames::localVariable(*lhsVar) << " := " << IRNames::localVariable(_assignment.rightHandSide()) << "\n";
|
||||||
|
|
||||||
|
m_code << "let " << IRNames::localVariable(_assignment) << " := " << IRNames::localVariable(*lhsVar) << "\n";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
bool IRGeneratorForStatements::visitNode(ASTNode const&)
|
bool IRGeneratorForStatements::visitNode(ASTNode const&)
|
||||||
{
|
{
|
||||||
solAssert(false, "Unsupported AST node during statement code generation.");
|
solAssert(false, "Unsupported AST node during statement code generation.");
|
||||||
|
@ -18,6 +18,7 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <libsolidity/codegen/experimental/IRGenerationContext.h>
|
||||||
#include <libsolidity/ast/ASTVisitor.h>
|
#include <libsolidity/ast/ASTVisitor.h>
|
||||||
|
|
||||||
#include <functional>
|
#include <functional>
|
||||||
@ -30,15 +31,19 @@ class Analysis;
|
|||||||
class IRGeneratorForStatements: public ASTConstVisitor
|
class IRGeneratorForStatements: public ASTConstVisitor
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
IRGeneratorForStatements(Analysis const& _analysis): m_analysis(_analysis) {}
|
IRGeneratorForStatements(IRGenerationContext& _context): m_context(_context) {}
|
||||||
|
|
||||||
std::string generate(ASTNode const& _node);
|
std::string generate(ASTNode const& _node);
|
||||||
private:
|
private:
|
||||||
|
bool visit(ExpressionStatement const& _expressionStatement) override;
|
||||||
|
bool visit(Assignment const& _assignment) override;
|
||||||
|
bool visit(Identifier const& _identifier) override;
|
||||||
|
bool visit(FunctionCall const&) override;
|
||||||
bool visit(InlineAssembly const& _inlineAssembly) override;
|
bool visit(InlineAssembly const& _inlineAssembly) override;
|
||||||
bool visit(VariableDeclarationStatement const& _variableDeclarationStatement) override;
|
bool visit(VariableDeclarationStatement const& _variableDeclarationStatement) override;
|
||||||
/// Default visit will reject all AST nodes that are not explicitly supported.
|
/// Default visit will reject all AST nodes that are not explicitly supported.
|
||||||
bool visitNode(ASTNode const& _node) override;
|
bool visitNode(ASTNode const& _node) override;
|
||||||
Analysis const& m_analysis;
|
IRGenerationContext& m_context;
|
||||||
std::stringstream m_code;
|
std::stringstream m_code;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1,9 +1,7 @@
|
|||||||
pragma experimental solidity;
|
pragma experimental solidity;
|
||||||
|
|
||||||
function f(a:word) -> (b:word) {
|
function f(a) -> (b) {
|
||||||
assembly {
|
b = a;
|
||||||
b := a
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
contract C {
|
contract C {
|
||||||
@ -13,7 +11,7 @@ contract C {
|
|||||||
assembly {
|
assembly {
|
||||||
x := 0x42
|
x := 0x42
|
||||||
}
|
}
|
||||||
y = x;
|
y = f(x);
|
||||||
assembly {
|
assembly {
|
||||||
mstore(0, y)
|
mstore(0, y)
|
||||||
return(0, 32)
|
return(0, 32)
|
||||||
@ -23,4 +21,4 @@ contract C {
|
|||||||
// ====
|
// ====
|
||||||
// compileViaYul: true
|
// compileViaYul: true
|
||||||
// ----
|
// ----
|
||||||
// () -> 0x42
|
// () -> 21
|
||||||
|
Loading…
Reference in New Issue
Block a user