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