This commit is contained in:
Daniel Kirchner
2023-06-23 18:37:58 +02:00
parent 14a34ae088
commit 315270f3bb
15 changed files with 318 additions and 65 deletions
+70 -9
View File
@@ -19,26 +19,87 @@
#include <libsolidity/analysis/experimental/SyntaxRestrictor.h>
#include <libsolidity/analysis/experimental/TypeInference.h>
#include <libsolidity/analysis/experimental/TypeRegistration.h>
using namespace std;
using namespace solidity::langutil;
using namespace solidity::frontend::experimental;
// TODO: creating all of them for all nodes up front may be wasteful, we should improve the mechanism.
struct Analysis::AnnotationContainer
{
TypeRegistration::Annotation typeRegistrationAnnotation;
TypeInference::Annotation typeInferenceAnnotation;
};
template<>
TypeRegistration::Annotation& solidity::frontend::experimental::detail::AnnotationFetcher<TypeRegistration>::get(ASTNode const& _node)
{
return analysis.annotationContainer(_node).typeRegistrationAnnotation;
}
template<>
TypeInference::Annotation& solidity::frontend::experimental::detail::AnnotationFetcher<TypeInference>::get(ASTNode const& _node)
{
return analysis.annotationContainer(_node).typeInferenceAnnotation;
}
Analysis::AnnotationContainer& Analysis::annotationContainer(ASTNode const& _node)
{
solAssert(_node.id() > 0);
size_t id = static_cast<size_t>(_node.id());
solAssert(id < m_maxAstId);
return m_annotations[id];
}
Analysis::Analysis(langutil::ErrorReporter& _errorReporter, uint64_t _maxAstId):
m_errorReporter(_errorReporter),
m_maxAstId(_maxAstId)
m_maxAstId(_maxAstId),
m_annotations(std::make_unique<AnnotationContainer[]>(static_cast<size_t>(_maxAstId)))
{
}
Analysis::~Analysis()
{}
template<size_t... Is>
std::tuple<std::integral_constant<size_t, Is>...> makeIndexTuple(std::index_sequence<Is...>) {
return std::make_tuple( std::integral_constant<size_t, Is>{}...);
}
bool Analysis::check(vector<shared_ptr<SourceUnit const>> const& _sourceUnits)
{
SyntaxRestrictor syntaxRestrictor{m_errorReporter};
for (auto source: _sourceUnits)
if (!syntaxRestrictor.check(*source))
return false;
TypeInference typeInference{*this};
for (auto source: _sourceUnits)
if (!typeInference.analyze(*source))
return false;
using AnalysisSteps = std::tuple<SyntaxRestrictor, TypeRegistration, TypeInference>;
return std::apply([&](auto... _indexTuple) {
return ([&](auto&& _step) {
for (auto source: _sourceUnits)
if (!_step.analyze(*source))
return false;
return true;
}(std::tuple_element_t<decltype(_indexTuple)::value, AnalysisSteps>{*this}) && ...);
}, makeIndexTuple(std::make_index_sequence<std::tuple_size_v<AnalysisSteps>>{}));
/*
{
SyntaxRestrictor syntaxRestrictor{*this};
for (auto source: _sourceUnits)
if (!syntaxRestrictor.analyze(*source))
return false;
}
{
TypeRegistration typeRegistration{*this};
for (auto source: _sourceUnits)
if (!typeRegistration.analyze(*source))
return false;
}
{
TypeInference typeInference{*this};
for (auto source: _sourceUnits)
if (!typeInference.analyze(*source))
return false;
}
return true;
*/
}
@@ -17,6 +17,8 @@
// SPDX-License-Identifier: GPL-3.0
#pragma once
#include <libsolidity/ast/experimental/TypeSystem.h>
#include <cstdint>
#include <memory>
#include <vector>
@@ -24,6 +26,7 @@
namespace solidity::frontend
{
class SourceUnit;
class ASTNode;
}
namespace solidity::langutil
@@ -33,19 +36,43 @@ class ErrorReporter;
namespace solidity::frontend::experimental
{
class TypeSystem;
class Analysis;
namespace detail
{
template<typename Step>
struct AnnotationFetcher
{
Analysis& analysis;
typename Step::Annotation& get(ASTNode const& _node);
};
}
class Analysis
{
struct AnnotationContainer;
public:
Analysis(langutil::ErrorReporter& _errorReporter, uint64_t _maxAstId);
Analysis(Analysis const&) = delete;
~Analysis();
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; }
TypeSystem& typeSystem() { return m_typeSystem; }
template<typename Step>
typename Step::Annotation& annotation(ASTNode const& _node)
{
return detail::AnnotationFetcher<Step>{*this}.get(_node);
}
AnnotationContainer& annotationContainer(ASTNode const& _node);
private:
langutil::ErrorReporter& m_errorReporter;
TypeSystem m_typeSystem;
uint64_t m_maxAstId = 0;
std::unique_ptr<AnnotationContainer[]> m_annotations;
};
}
@@ -18,13 +18,18 @@
#include <libsolidity/analysis/experimental/SyntaxRestrictor.h>
#include <libsolidity/analysis/experimental/Analysis.h>
#include <liblangutil/Exceptions.h>
using namespace solidity::frontend;
using namespace solidity::frontend::experimental;
using namespace solidity::langutil;
bool SyntaxRestrictor::check(ASTNode const& _astRoot)
SyntaxRestrictor::SyntaxRestrictor(Analysis& _analysis): m_errorReporter(_analysis.errorReporter())
{}
bool SyntaxRestrictor::analyze(ASTNode const& _astRoot)
{
_astRoot.accept(*this);
return !Error::containsErrors(m_errorReporter.errors());
@@ -106,4 +111,4 @@ bool SyntaxRestrictor::visit(VariableDeclaration const& _variableDeclaration)
if (_variableDeclaration.referenceLocation() != VariableDeclaration::Location::Unspecified)
m_errorReporter.syntaxError(0000_error, _variableDeclaration.location(), "Variables with reference location not supported.");
return true;
}
}
@@ -24,14 +24,14 @@
namespace solidity::frontend::experimental
{
class Analysis;
class SyntaxRestrictor: public ASTConstVisitor
{
public:
/// @param _errorReporter provides the error logging functionality.
explicit SyntaxRestrictor(langutil::ErrorReporter& _errorReporter): m_errorReporter(_errorReporter) {}
SyntaxRestrictor(Analysis& _analysis);
bool check(ASTNode const& _astRoot);
bool analyze(ASTNode const& _astRoot);
private:
/// Default visit will reject all AST nodes that are not explicitly allowed.
@@ -34,22 +34,12 @@ using namespace solidity::langutil;
TypeInference::TypeInference(Analysis& _analysis):
m_analysis(_analysis),
m_errorReporter(_analysis.errorReporter())
m_errorReporter(_analysis.errorReporter()),
m_typeSystem(_analysis.typeSystem())
{
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},
{BuiltinType::Integer, "integer", 0}
})
m_typeSystem.declareBuiltinType(type, name, arity);
m_voidType = m_typeSystem.builtinType(BuiltinType::Void, {});
m_wordType = m_typeSystem.builtinType(BuiltinType::Word, {});
m_integerType = m_typeSystem.builtinType(BuiltinType::Integer, {});
m_typeAnnotations.resize(_analysis.maxAstId());
}
bool TypeInference::analyze(SourceUnit const& _sourceUnit)
@@ -72,7 +62,7 @@ bool TypeInference::visit(FunctionDefinition const& _functionDefinition)
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) {
return TypeSystemHelpers{m_typeSystem}.tupleType(_list->parameters() | ranges::views::transform([&](auto _param) {
auto& argAnnotation = annotation(*_param);
solAssert(argAnnotation.type);
return *argAnnotation.type;
@@ -173,7 +163,6 @@ experimental::Type TypeInference::fromTypeName(TypeName const& _typeName)
else
m_errorReporter.typeError(0000_error, _typeName.location(), "Unsupported type name.");
return m_typeSystem.freshTypeVariable(false);
}
void TypeInference::unify(Type _a, Type _b)
{
@@ -256,12 +245,9 @@ void TypeInference::endVisit(Assignment const& _assignment)
assignmentAnnotation.type = m_typeSystem.resolve(*lhsAnnotation.type);
}
TypeInference::TypeAnnotation& TypeInference::annotation(ASTNode const& _node)
TypeInference::Annotation& TypeInference::annotation(ASTNode const& _node)
{
auto& annotation = m_typeAnnotations.at(static_cast<size_t>(_node.id()));
if (!annotation)
annotation = make_unique<TypeAnnotation>();
return *annotation;
return m_analysis.annotation<TypeInference>(_node);
}
bool TypeInference::visit(Identifier const& _identifier)
@@ -22,8 +22,6 @@
#include <liblangutil/ErrorReporter.h>
#include <range/v3/span.hpp>
namespace solidity::frontend::experimental
{
@@ -35,6 +33,12 @@ public:
TypeInference(Analysis& _analysis);
bool analyze(SourceUnit const& _sourceUnit);
struct Annotation
{
std::optional<Type> type;
};
private:
bool visit(Block const&) override { return true; }
bool visit(VariableDeclarationStatement const&) override { return true; }
@@ -67,22 +71,15 @@ private:
Type fromTypeName(TypeName const& _typeName);
Analysis& m_analysis;
langutil::ErrorReporter& m_errorReporter;
TypeSystem m_typeSystem;
TypeSystem& m_typeSystem;
Type m_voidType;
Type m_wordType;
Type m_integerType;
std::optional<Type> m_currentFunctionType;
struct TypeAnnotation
{
std::optional<Type> type;
};
TypeAnnotation& annotation(ASTNode const& _node);
Annotation& annotation(ASTNode const& _node);
void unify(Type _a, Type _b);
std::vector<std::unique_ptr<TypeAnnotation>> m_typeAnnotations;
};
}
}
@@ -0,0 +1,102 @@
/*
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
#include <libsolidity/analysis/experimental/TypeRegistration.h>
#include <libsolidity/analysis/experimental/Analysis.h>
#include <liblangutil/Exceptions.h>
#include <libyul/AsmAnalysis.h>
#include <libyul/AsmAnalysisInfo.h>
#include <libyul/AST.h>
using namespace std;
using namespace solidity::frontend;
using namespace solidity::frontend::experimental;
using namespace solidity::langutil;
TypeRegistration::TypeRegistration(Analysis& _analysis):
m_analysis(_analysis),
m_errorReporter(_analysis.errorReporter()),
m_typeSystem(_analysis.typeSystem())
{
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},
{BuiltinType::Integer, "integer", 0}
})
m_typeSystem.declareBuiltinType(type, name, arity);
}
bool TypeRegistration::analyze(SourceUnit const& _sourceUnit)
{
_sourceUnit.accept(*this);
return !m_errorReporter.hasErrors();
}
bool TypeRegistration::visit(TypeClassDefinition const& _typeClassDefinition)
{
if (!m_visitedClasses.insert(_typeClassDefinition.id()).second)
return false;
return false;
}
bool TypeRegistration::visit(TypeClassInstantiation const& _typeClassInstantiation)
{
auto const* classDefintion = dynamic_cast<TypeClassDefinition const*>(_typeClassInstantiation.sort().annotation().referencedDeclaration);
if (!classDefintion)
m_errorReporter.fatalTypeError(0000_error, _typeClassInstantiation.sort().location(), "Expected a type class.");
classDefintion->accept(*this);
// TypeClass typeClass{classDefintion};
auto fromTypeName = [&](TypeName const& _typeName) -> Type {
if (auto const* elementaryTypeName = dynamic_cast<ElementaryTypeName const*>(&_typeName))
{
switch(elementaryTypeName->typeName().token())
{
case Token::Word:
return m_typeSystem.builtinType(BuiltinType::Word, {});
case Token::Void:
return m_typeSystem.builtinType(BuiltinType::Void, {});
case Token::Integer:
return m_typeSystem.builtinType(BuiltinType::Integer, {});
default:
m_errorReporter.typeError(0000_error, _typeName.location(), "Only elementary types are supported.");
break;
}
}
else
m_errorReporter.typeError(0000_error, _typeName.location(), "Unsupported type name.");
return m_typeSystem.freshTypeVariable(false);
};
auto type = fromTypeName(_typeClassInstantiation.typeConstructor());
_typeClassInstantiation.argumentSorts();
// m_typeSystem.instantiateClass();
return false;
}
TypeRegistration::Annotation& TypeRegistration::annotation(ASTNode const& _node)
{
return m_analysis.annotation<TypeRegistration>(_node);
}
@@ -0,0 +1,51 @@
/*
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/ASTVisitor.h>
#include <libsolidity/ast/experimental/TypeSystem.h>
#include <liblangutil/ErrorReporter.h>
namespace solidity::frontend::experimental
{
class Analysis;
class TypeRegistration: public ASTConstVisitor
{
public:
struct Annotation
{
Type type;
};
TypeRegistration(Analysis& _analysis);
bool analyze(SourceUnit const& _sourceUnit);
private:
bool visit(TypeClassDefinition const& _typeClassDefinition) override;
bool visit(TypeClassInstantiation const& _typeClassInstantiation) override;
Annotation& annotation(ASTNode const& _node);
Analysis& m_analysis;
langutil::ErrorReporter& m_errorReporter;
TypeSystem& m_typeSystem;
std::set<int64_t> m_visitedClasses;
};
}