This commit is contained in:
Daniel Kirchner 2023-06-20 04:08:23 +02:00
parent 4357b0316b
commit bd0e0fcdbe
15 changed files with 586 additions and 156 deletions

View File

@ -268,8 +268,10 @@ namespace solidity::langutil
/* Yul-specific tokens, but not keywords. */ \
T(Leave, "leave", 0) \
\
T(NonExperimentalEnd, nullptr, 0) /* used as non-experimental enum end marker */ \
/* Experimental Solidity specific keywords. */ \
K(Word, "word", 0) \
K(Void, "void", 0) \
K(StaticAssert, "static_assert", 0) \
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); }
// 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 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 ||
@ -331,11 +336,11 @@ namespace TokenTraits
{
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::Word && tok < Token::ExperimentalEnd);
(tok > Token::NonExperimentalEnd && tok < Token::ExperimentalEnd);
}
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);

View File

@ -98,6 +98,7 @@ set(sources
codegen/ReturnInfo.cpp
codegen/YulUtilFunctions.h
codegen/YulUtilFunctions.cpp
codegen/experimental/IRGenerationContext.h
codegen/experimental/IRGenerator.cpp
codegen/experimental/IRGenerator.h
codegen/experimental/IRGeneratorForStatements.cpp

View File

@ -36,7 +36,7 @@ bool Analysis::check(vector<shared_ptr<SourceUnit const>> const& _sourceUnits)
for (auto source: _sourceUnits)
if (!syntaxRestrictor.check(*source))
return false;
TypeInference typeInference{m_errorReporter};
TypeInference typeInference{*this};
for (auto source: _sourceUnits)
if (!typeInference.analyze(*source))
return false;

View File

@ -38,7 +38,11 @@ class Analysis
{
public:
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);
langutil::ErrorReporter& errorReporter() { return m_errorReporter; }
uint64_t maxAstId() const { return m_maxAstId; }
private:
langutil::ErrorReporter& m_errorReporter;
uint64_t m_maxAstId = 0;

View File

@ -43,6 +43,7 @@ private:
bool visit(ContractDefinition const& _contractDefinition) override;
bool visit(FunctionDefinition const& _functionDefinition) override;
bool visit(ExpressionStatement const&) override { return true; }
bool visit(FunctionCall const&) override { return true; }
bool visit(Assignment const&) override { return true; }
bool visit(Block const&) override { return true; }
bool visit(InlineAssembly const&) override { return true; }

View File

@ -18,17 +18,39 @@
#include <libsolidity/analysis/experimental/TypeInference.h>
#include <libsolidity/analysis/experimental/Analysis.h>
#include <liblangutil/Exceptions.h>
#include <libyul/AsmAnalysis.h>
#include <libyul/AsmAnalysisInfo.h>
#include <libyul/AST.h>
#include <range/v3/view/transform.hpp>
using namespace std;
using namespace solidity::frontend;
using namespace solidity::frontend::experimental;
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)
{
_sourceUnit.accept(*this);
@ -37,12 +59,36 @@ bool TypeInference::analyze(SourceUnit const& _sourceUnit)
bool TypeInference::visit(FunctionDefinition const& _functionDefinition)
{
ScopedSaveAndRestore env{m_env, {}};
_functionDefinition.parameterList().accept(*this);
if (_functionDefinition.returnParameterList())
_functionDefinition.returnParameterList()->accept(*this);
auto& functionAnnotation = annotation(_functionDefinition);
if (functionAnnotation.type)
return false;
_functionDefinition.body().accept(*this);
Type functionType;
{
_functionDefinition.parameterList().accept(*this);
if (_functionDefinition.returnParameterList())
_functionDefinition.returnParameterList()->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;
}
@ -52,6 +98,20 @@ bool TypeInference::visit(ParameterList const&)
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)
{
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())
{
case Token::Word:
return WordType{};
return m_wordType;
case Token::Void:
return m_voidType;
default:
m_errorReporter.typeError(0000_error, _typeName.location(), "Only elementary types are supported.");
break;
@ -73,7 +135,8 @@ experimental::Type TypeInference::fromTypeName(TypeName const& _typeName)
}
else
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;
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;
return true;
};
@ -120,19 +185,83 @@ bool TypeInference::visit(InlineAssembly const& _inlineAssembly)
bool TypeInference::visit(VariableDeclaration const& _variableDeclaration)
{
Type type = _variableDeclaration.hasTypeName() ? fromTypeName(_variableDeclaration.typeName()) : m_typeSystem.freshTypeVariable();
m_env.assignType(m_typeSystem, &_variableDeclaration, type);
solAssert(!_variableDeclaration.value());
auto& variableAnnotation = annotation(_variableDeclaration);
solAssert(!variableAnnotation.type);
variableAnnotation.type = [&] {
if (_variableDeclaration.hasTypeName())
return fromTypeName(_variableDeclaration.typeName());
else
return m_typeSystem.freshTypeVariable();
}();
return false;
}
bool TypeInference::visit(Assignment const& _assignment)
bool TypeInference::visit(Assignment const&)
{
(void)_assignment;
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)
{
(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;
}
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))));
}

View File

@ -27,10 +27,12 @@
namespace solidity::frontend::experimental
{
class Analysis;
class TypeInference: public ASTConstVisitor
{
public:
TypeInference(langutil::ErrorReporter& _errorReporter): m_errorReporter(_errorReporter) {}
TypeInference(Analysis& _analysis);
bool analyze(SourceUnit const& _sourceUnit);
private:
@ -40,6 +42,7 @@ private:
bool visit(FunctionDefinition const& _functionDefinition) override;
bool visit(ParameterList const& _parameterList) override;
void endVisit(ParameterList const& _parameterList) override;
bool visit(SourceUnit const&) override { return true; }
bool visit(ContractDefinition const&) override { return true; }
bool visit(InlineAssembly const& _inlineAssembly) override;
@ -47,14 +50,29 @@ private:
bool visit(ExpressionStatement const&) override { return true; }
bool visit(Assignment const&) override;
void endVisit(Assignment 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;
Type fromTypeName(TypeName const& _typeName);
TypeSystem m_typeSystem;
Analysis& m_analysis;
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;
};
}

View File

@ -18,47 +18,225 @@
#include <libsolidity/ast/experimental/TypeSystem.h>
#include <libsolidity/ast/AST.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 solidity::frontend;
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)));
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))
return m_types[_declaration];
Type result = _typeSystem.freshTypeVariable();
m_types.emplace(std::piecewise_construct, std::forward_as_tuple(_declaration), std::forward_as_tuple(result));
return std::nullopt;
}
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;
}
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);
_b = _context.resolve(_b);
if (auto* varA = get_if<TypeVariable>(&_a))
_context.instantiate(*varA, _b);
else if (holds_alternative<WordType>(_a))
{
if (holds_alternative<WordType>(_b))
return;
else
solAssert(false, "unification failed");
}
solAssert(false, fmt::format("cannot unify {} and {}", typeToString(_a), typeToString(_b)));
auto [constructor, arguments] = destAtomicType(_functionType);
auto const* builtinType = get_if<BuiltinType>(&constructor);
solAssert(builtinType && *builtinType == BuiltinType::Function);
solAssert(arguments.size() == 2);
return make_tuple(arguments.front(), arguments.back());
}

View File

@ -19,8 +19,6 @@
#include <liblangutil/Exceptions.h>
#include <fmt/format.h>
#include <optional>
#include <string>
#include <variant>
@ -35,99 +33,64 @@ namespace solidity::frontend::experimental
{
class TypeSystem;
class TypeEnvironment;
struct SumType;
struct TupleType;
struct FunctionType;
struct WordType;
struct UserDefinedType;
struct TypeVariable;
struct FreeType;
struct AtomicType;
struct GenericTypeVariable;
struct FreeTypeVariable;
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;
std::string toString() const
{
return "sum";
}
Void,
Function,
Unit,
Pair,
Word
};
struct TupleType
struct AtomicType
{
std::vector<Type const*> components;
std::string toString() const
{
return "tuple";
}
using Constructor = std::variant<BuiltinType, Declaration const*>;
Constructor constructor;
std::vector<Type> arguments;
};
struct FunctionType
struct FreeTypeVariable
{
Type const* codomain = nullptr;
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:
TypeSystem const& parent() const { return *m_parent; }
uint64_t index() const { return m_index; }
private:
friend class TypeSystem;
TypeSystem const* m_parent = nullptr;
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;
std::string toString() const
{
return fmt::format("free<{}>", index);
}
TypeSystem const& parent() const { return *m_parent; }
uint64_t index() const { return m_index; }
private:
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
{
public:
void assignType(TypeSystem& _typeSystem, Declaration const* _declaration, Type _typeAssignment);
Type lookup(TypeSystem& _typeSystem, Declaration const* _declaration);
Type freshFreeType();
void unify(TypeSystem& _typeSystem, Type _a, Type _b);
TypeEnvironment(TypeSystem& _parent): m_parent(_parent) {}
TypeEnvironment(TypeEnvironment const& _env) = delete;
TypeEnvironment& operator=(TypeEnvironment const& _env) = delete;
std::unique_ptr<TypeEnvironment> fresh() const;
void assignType(Declaration const* _declaration, Type _typeAssignment);
std::optional<Type> lookup(Declaration const* _declaration);
private:
uint64_t m_numFreeTypes = 0;
TypeSystem& m_parent;
std::map<Declaration const*, Type> m_types;
};
@ -138,30 +101,38 @@ public:
TypeSystem() {}
TypeSystem(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();
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;
return m_builtinTypes.at(_builtinType).name;
}
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:
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;
};
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;
};
}

View 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);
}
};
}

View File

@ -18,6 +18,7 @@
#include <libsolidity/codegen/experimental/IRGenerator.h>
#include <libsolidity/codegen/experimental/IRGenerationContext.h>
#include <libsolidity/codegen/experimental/IRGeneratorForStatements.h>
#include <libsolidity/codegen/ir/Common.h>
@ -31,6 +32,8 @@
#include <libsolutil/Whiskers.h>
#include <range/v3/view/drop_last.hpp>
#include <variant>
using namespace std;
@ -43,7 +46,7 @@ string IRGenerator::run(
ContractDefinition const& _contract,
bytes const& /*_cborMetadata*/,
map<ContractDefinition const*, string_view const> const& /*_otherYulSources*/
) const
)
{
Whiskers t(R"(
@ -66,30 +69,52 @@ string IRGenerator::run(
return t.render();
}
string IRGenerator::generate(ContractDefinition const& _contract) const
string IRGenerator::generate(ContractDefinition const& _contract)
{
std::stringstream code;
code << "{\n";
if (_contract.fallbackFunction())
{
code << IRNames::function(*_contract.fallbackFunction()) << "()\n";
m_context.functionQueue.emplace_front(_contract.fallbackFunction());
}
code << "revert(0,0)\n";
code << "}\n";
for (FunctionDefinition const* f: _contract.definedFunctions())
code << generate(*f);
while (!m_context.functionQueue.empty())
{
FunctionDefinition const* function = m_context.functionQueue.front();
m_context.functionQueue.pop_front();
m_context.generatedFunctions.insert(function);
code << generate(*function);
}
return code.str();
}
string IRGenerator::generate(FunctionDefinition const& _function) const
string IRGenerator::generate(FunctionDefinition const& _function)
{
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())
{
IRGeneratorForStatements statementGenerator{m_analysis};
IRGeneratorForStatements statementGenerator{m_context};
code << statementGenerator.generate(*_statement);
}
code << "}\n";

View File

@ -18,6 +18,7 @@
#pragma once
#include <libsolidity/codegen/experimental/IRGenerationContext.h>
#include <libsolidity/interface/DebugSettings.h>
#include <libsolidity/interface/OptimiserSettings.h>
#include <libsolidity/ast/ASTForward.h>
@ -53,24 +54,24 @@ public:
m_eofVersion(_eofVersion),
m_debugInfoSelection(_debugInfoSelection),
m_soliditySourceProvider(_soliditySourceProvider),
m_analysis(_analysis)
m_context{_analysis, {}, {}}
{}
std::string run(
ContractDefinition const& _contract,
bytes const& _cborMetadata,
std::map<ContractDefinition const*, std::string_view const> const& _otherYulSources
) const;
);
std::string generate(ContractDefinition const& _contract) const;
std::string generate(FunctionDefinition const& _function) const;
std::string generate(ContractDefinition const& _contract);
std::string generate(FunctionDefinition const& _function);
private:
langutil::EVMVersion const m_evmVersion;
std::optional<uint8_t> const m_eofVersion;
OptimiserSettings const m_optimiserSettings;
langutil::DebugInfoSelection m_debugInfoSelection = {};
langutil::CharStreamProvider const* m_soliditySourceProvider = nullptr;
Analysis const& m_analysis;
IRGenerationContext m_context;
};
}

View File

@ -25,6 +25,8 @@
#include <libsolidity/codegen/ir/Common.h>
#include <range/v3/view/drop_last.hpp>
using namespace std;
using namespace solidity;
using namespace solidity::util;
@ -104,7 +106,7 @@ bool IRGeneratorForStatements::visit(InlineAssembly const& _assembly)
CopyTranslate bodyCopier{_assembly.dialect(), _assembly.annotation().externalReferences};
yul::Statement modified = bodyCopier(_assembly.operations());
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;
}
@ -119,6 +121,55 @@ bool IRGeneratorForStatements::visit(VariableDeclarationStatement const& _variab
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&)
{
solAssert(false, "Unsupported AST node during statement code generation.");

View File

@ -18,6 +18,7 @@
#pragma once
#include <libsolidity/codegen/experimental/IRGenerationContext.h>
#include <libsolidity/ast/ASTVisitor.h>
#include <functional>
@ -30,15 +31,19 @@ class Analysis;
class IRGeneratorForStatements: public ASTConstVisitor
{
public:
IRGeneratorForStatements(Analysis const& _analysis): m_analysis(_analysis) {}
IRGeneratorForStatements(IRGenerationContext& _context): m_context(_context) {}
std::string generate(ASTNode const& _node);
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(VariableDeclarationStatement const& _variableDeclarationStatement) override;
/// Default visit will reject all AST nodes that are not explicitly supported.
bool visitNode(ASTNode const& _node) override;
Analysis const& m_analysis;
IRGenerationContext& m_context;
std::stringstream m_code;
};

View File

@ -1,9 +1,7 @@
pragma experimental solidity;
function f(a:word) -> (b:word) {
assembly {
b := a
}
function f(a) -> (b) {
b = a;
}
contract C {
@ -13,7 +11,7 @@ contract C {
assembly {
x := 0x42
}
y = x;
y = f(x);
assembly {
mstore(0, y)
return(0, 32)
@ -23,4 +21,4 @@ contract C {
// ====
// compileViaYul: true
// ----
// () -> 0x42
// () -> 21