mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Separate approach of AST transform.
This commit is contained in:
parent
f9b424418c
commit
5adc255b3c
@ -186,6 +186,10 @@ set(sources
|
||||
experimental/analysis/Analysis.h
|
||||
experimental/analysis/DebugWarner.cpp
|
||||
experimental/analysis/DebugWarner.h
|
||||
experimental/analysis/ASTTransform.cpp
|
||||
experimental/analysis/ASTTransform.h
|
||||
experimental/analysis/TypeCheck.cpp
|
||||
experimental/analysis/TypeCheck.h
|
||||
experimental/analysis/TypeInference.cpp
|
||||
experimental/analysis/TypeInference.h
|
||||
experimental/analysis/TypeRegistration.cpp
|
||||
|
460
libsolidity/experimental/analysis/ASTTransform.cpp
Normal file
460
libsolidity/experimental/analysis/ASTTransform.cpp
Normal file
@ -0,0 +1,460 @@
|
||||
/*
|
||||
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/ast/AST.h>
|
||||
#include <libsolidity/experimental/analysis/ASTTransform.h>
|
||||
#include <libsolidity/experimental/analysis/Analysis.h>
|
||||
|
||||
#include <liblangutil/ErrorReporter.h>
|
||||
#include <liblangutil/Exceptions.h>
|
||||
|
||||
#include <libyul/AsmAnalysis.h>
|
||||
#include <libyul/AsmAnalysisInfo.h>
|
||||
#include <libyul/AST.h>
|
||||
|
||||
#include <range/v3/algorithm/fold_right.hpp>
|
||||
#include <range/v3/view/move.hpp>
|
||||
#include <range/v3/view/drop.hpp>
|
||||
|
||||
namespace legacy = solidity::frontend;
|
||||
using namespace std;
|
||||
using namespace solidity::langutil;
|
||||
using namespace solidity::frontend::experimental;
|
||||
|
||||
ASTTransform::ASTTransform(Analysis& _analysis): m_analysis(_analysis), m_errorReporter(_analysis.errorReporter()), m_ast(make_unique<AST>())
|
||||
{
|
||||
}
|
||||
|
||||
bool ASTTransform::visit(legacy::ContractDefinition const& _contractDefinition)
|
||||
{
|
||||
SetNode setNode(*this, _contractDefinition);
|
||||
auto [it, newlyInserted] = m_ast->contracts.emplace(&_contractDefinition, AST::ContractInfo{});
|
||||
solAssert(newlyInserted);
|
||||
AST::ContractInfo& contractInfo = it->second;
|
||||
for (auto const& node: _contractDefinition.subNodes())
|
||||
if (auto const* function = dynamic_cast<legacy::FunctionDefinition const*>(node.get()))
|
||||
solAssert(contractInfo.functions.emplace(string{}, functionDefinition(*function)).second);
|
||||
else
|
||||
m_errorReporter.typeError(0000_error, node->location(), "Unsupported contract element.");
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ASTTransform::visit(legacy::FunctionDefinition const& _functionDefinition)
|
||||
{
|
||||
SetNode setNode(*this, _functionDefinition);
|
||||
solAssert(m_ast->functions.emplace(&_functionDefinition, functionDefinition(_functionDefinition)).second);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ASTTransform::visit(legacy::TypeClassDefinition const& _typeClassDefinition)
|
||||
{
|
||||
SetNode setNode(*this, _typeClassDefinition);
|
||||
auto [it, newlyInserted] = m_ast->typeClasses.emplace(&_typeClassDefinition, AST::TypeClassInformation{});
|
||||
solAssert(newlyInserted);
|
||||
auto& info = it->second;
|
||||
info.typeVariable = term(_typeClassDefinition.typeVariable());
|
||||
info.declaration = reference(_typeClassDefinition);
|
||||
declare(_typeClassDefinition, *info.declaration);
|
||||
map<std::string, AST::FunctionInfo>& functions = info.functions;
|
||||
for (auto subNode: _typeClassDefinition.subNodes())
|
||||
{
|
||||
auto const *function = dynamic_cast<FunctionDefinition const*>(subNode.get());
|
||||
solAssert(function);
|
||||
solAssert(functions.emplace(function->name(), functionDefinition(*function)).second);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ASTTransform::visit(legacy::TypeClassInstantiation const& _typeClassInstantiation)
|
||||
{
|
||||
SetNode setNode(*this, _typeClassInstantiation);
|
||||
auto [it, newlyInserted] = m_ast->typeClassInstantiations.emplace(&_typeClassInstantiation, AST::TypeClassInstantiationInformation{});
|
||||
solAssert(newlyInserted);
|
||||
auto& info = it->second;
|
||||
info.typeClass = std::visit(util::GenericVisitor{
|
||||
[&](Token _token) -> unique_ptr<Term> { return builtinTypeClass(_token); },
|
||||
[&](ASTPointer<legacy::IdentifierPath> _identifierPath) -> unique_ptr<Term> {
|
||||
solAssert(_identifierPath->annotation().referencedDeclaration);
|
||||
return reference(*_identifierPath->annotation().referencedDeclaration);
|
||||
}
|
||||
}, _typeClassInstantiation.typeClass().name());
|
||||
info.typeConstructor = term(_typeClassInstantiation.typeConstructor());
|
||||
info.argumentSorts = termOrConstant(_typeClassInstantiation.argumentSorts(), BuiltinConstant::Unit);
|
||||
map<std::string, AST::FunctionInfo>& functions = info.functions;
|
||||
for (auto subNode: _typeClassInstantiation.subNodes())
|
||||
{
|
||||
auto const *function = dynamic_cast<FunctionDefinition const*>(subNode.get());
|
||||
solAssert(function);
|
||||
solAssert(functions.emplace(function->name(), functionDefinition(*function)).second);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ASTTransform::visit(legacy::TypeDefinition const& _typeDefinition)
|
||||
{
|
||||
SetNode setNode(*this, _typeDefinition);
|
||||
auto [it, newlyInserted] = m_ast->typeDefinitions.emplace(&_typeDefinition, AST::TypeInformation{});
|
||||
solAssert(newlyInserted);
|
||||
auto& info = it->second;
|
||||
info.declaration = makeTerm<VariableDeclaration>(reference(_typeDefinition), nullptr);
|
||||
declare(_typeDefinition, *info.declaration);
|
||||
if (_typeDefinition.arguments())
|
||||
info.arguments = tuple(_typeDefinition.arguments()->parameters() | ranges::view::transform([&](auto argument){
|
||||
solAssert(!argument->typeExpression()); // TODO: error handling
|
||||
return term(*argument);
|
||||
}) | ranges::view::move | ranges::to<list<unique_ptr<Term>>>);
|
||||
if (_typeDefinition.typeExpression())
|
||||
info.value = term(*_typeDefinition.typeExpression());
|
||||
return false;
|
||||
}
|
||||
|
||||
unique_ptr<Term> ASTTransform::term(legacy::VariableDeclarationStatement const& _declaration)
|
||||
{
|
||||
SetNode setNode(*this, _declaration);
|
||||
solAssert(_declaration.declarations().size() == 1);
|
||||
unique_ptr<Term> value;
|
||||
if (_declaration.initialValue())
|
||||
value = term(*_declaration.initialValue());
|
||||
return term(*_declaration.declarations().front(), std::move(value));
|
||||
}
|
||||
|
||||
unique_ptr<Term> ASTTransform::term(legacy::Assignment const& _assignment)
|
||||
{
|
||||
SetNode setNode(*this, _assignment);
|
||||
if (_assignment.assignmentOperator() == Token::Assign)
|
||||
return application(BuiltinConstant::Assign, _assignment.leftHandSide(), _assignment.rightHandSide());
|
||||
else
|
||||
solAssert(false);
|
||||
}
|
||||
|
||||
unique_ptr<Term> ASTTransform::term(TypeName const& _name)
|
||||
{
|
||||
SetNode setNode(*this, _name);
|
||||
if (auto const* elementaryTypeName = dynamic_cast<ElementaryTypeName const*>(&_name))
|
||||
{
|
||||
switch (elementaryTypeName->typeName().token())
|
||||
{
|
||||
case Token::Void:
|
||||
return constant(BuiltinConstant::Void);
|
||||
case Token::Fun:
|
||||
return constant(BuiltinConstant::Fun);
|
||||
case Token::Unit:
|
||||
return constant(BuiltinConstant::Unit);
|
||||
case Token::Pair:
|
||||
return constant(BuiltinConstant::Pair);
|
||||
case Token::Word:
|
||||
return constant(BuiltinConstant::Word);
|
||||
case Token::Integer:
|
||||
return constant(BuiltinConstant::Integer);
|
||||
case Token::Bool:
|
||||
return constant(BuiltinConstant::Bool);
|
||||
default:
|
||||
m_analysis.errorReporter().typeError(0000_error, m_currentLocation, "Unsupported type.");
|
||||
return constant(BuiltinConstant::Undefined);
|
||||
}
|
||||
}
|
||||
else if (auto const* userDefinedTypeName = dynamic_cast<UserDefinedTypeName const*>(&_name))
|
||||
{
|
||||
auto const* declaration = userDefinedTypeName->pathNode().annotation().referencedDeclaration;
|
||||
solAssert(declaration);
|
||||
return reference(*declaration);
|
||||
}
|
||||
else
|
||||
solAssert(false);
|
||||
}
|
||||
|
||||
unique_ptr<Term> ASTTransform::term(legacy::Statement const& _statement)
|
||||
{
|
||||
SetNode setNode(*this, _statement);
|
||||
if (auto const* assembly = dynamic_cast<legacy::InlineAssembly const*>(&_statement))
|
||||
return term(*assembly);
|
||||
else if (auto const* declaration = dynamic_cast<legacy::VariableDeclarationStatement const*>(&_statement))
|
||||
return term(*declaration);
|
||||
else if (auto const* assign = dynamic_cast<legacy::Assignment const*>(&_statement))
|
||||
return term(*assign);
|
||||
else if (auto const* expressionStatement = dynamic_cast<legacy::ExpressionStatement const*>(&_statement))
|
||||
return term(expressionStatement->expression());
|
||||
else if (auto const* returnStatement = dynamic_cast<legacy::Return const*>(&_statement))
|
||||
return application(BuiltinConstant::Return, termOrConstant(returnStatement->expression(), BuiltinConstant::Unit));
|
||||
else
|
||||
{
|
||||
m_analysis.errorReporter().fatalTypeError(0000_error, _statement.location(), "Unsupported statement.");
|
||||
solAssert(false);
|
||||
}
|
||||
}
|
||||
|
||||
unique_ptr<Term> ASTTransform::term(legacy::Block const& _block)
|
||||
{
|
||||
SetNode setNode(*this, _block);
|
||||
if (_block.statements().empty())
|
||||
return application(BuiltinConstant::Block, constant(BuiltinConstant::Unit));
|
||||
auto makeStatement = [&](auto _stmt) {
|
||||
return application(BuiltinConstant::Statement, *_stmt);
|
||||
};
|
||||
|
||||
return application(
|
||||
BuiltinConstant::Block,
|
||||
ranges::fold_right(
|
||||
_block.statements() | ranges::view::drop(1),
|
||||
makeStatement(_block.statements().front()),
|
||||
[&](auto stmt, auto acc) {
|
||||
return application(BuiltinConstant::ChainStatements, std::move(acc), makeStatement(stmt));
|
||||
}
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
AST::FunctionInfo ASTTransform::functionDefinition(legacy::FunctionDefinition const& _functionDefinition)
|
||||
{
|
||||
SetNode setNode(*this, _functionDefinition);
|
||||
std::unique_ptr<Term> body = nullptr;
|
||||
unique_ptr<Term> argumentExpression = term(_functionDefinition.parameterList());
|
||||
if (_functionDefinition.isImplemented())
|
||||
body = term(_functionDefinition.body());
|
||||
unique_ptr<Term> returnType = termOrConstant(_functionDefinition.experimentalReturnExpression(), BuiltinConstant::Unit);
|
||||
unique_ptr<Term> name = reference(_functionDefinition);
|
||||
unique_ptr<Term> function = makeTerm<VariableDeclaration>(std::move(name), std::move(body));
|
||||
declare(_functionDefinition, *function);
|
||||
return AST::FunctionInfo{
|
||||
std::move(function),
|
||||
std::move(argumentExpression),
|
||||
std::move(returnType)
|
||||
};
|
||||
}
|
||||
|
||||
unique_ptr<Term> ASTTransform::term(legacy::ParameterList const& _parameterList)
|
||||
{
|
||||
SetNode setNode(*this, _parameterList);
|
||||
return tuple(_parameterList.parameters() | ranges::view::transform([&](auto parameter) {
|
||||
return term(*parameter);
|
||||
}) | ranges::view::move | ranges::to<list<unique_ptr<Term>>>);
|
||||
}
|
||||
|
||||
unique_ptr<Term> ASTTransform::term(legacy::VariableDeclaration const& _variableDeclaration, std::unique_ptr<Term> _initialValue)
|
||||
{
|
||||
SetNode setNode(*this, _variableDeclaration);
|
||||
solAssert(!_variableDeclaration.value());
|
||||
unique_ptr<Term> name = reference(_variableDeclaration);
|
||||
if (_variableDeclaration.typeExpression())
|
||||
name = constrain(std::move(name), term(*_variableDeclaration.typeExpression()));
|
||||
unique_ptr<Term> declaration = makeTerm<VariableDeclaration>(std::move(name), std::move(_initialValue));
|
||||
declare(_variableDeclaration, *declaration);
|
||||
return declaration;
|
||||
}
|
||||
|
||||
unique_ptr<Term> ASTTransform::term(legacy::InlineAssembly const& _inlineAssembly)
|
||||
{
|
||||
SetNode setNode(*this, _inlineAssembly);
|
||||
std::map<yul::Identifier const*, unique_ptr<Term>> references;
|
||||
// External references have already been resolved in a prior stage and stored in the annotation.
|
||||
// We run the resolve step again regardless.
|
||||
yul::ExternalIdentifierAccess::Resolver identifierAccess = [&](
|
||||
yul::Identifier const& _identifier,
|
||||
yul::IdentifierContext _context,
|
||||
bool
|
||||
) -> bool
|
||||
{
|
||||
if (_context == yul::IdentifierContext::NonExternal)
|
||||
{
|
||||
// TODO: do we need this?
|
||||
// Hack until we can disallow any shadowing: If we found an internal reference,
|
||||
// clear the external references, so that codegen does not use it.
|
||||
_inlineAssembly.annotation().externalReferences.erase(& _identifier);
|
||||
return false;
|
||||
}
|
||||
InlineAssemblyAnnotation::ExternalIdentifierInfo* identifierInfo = util::valueOrNullptr(_inlineAssembly.annotation().externalReferences, &_identifier);
|
||||
if (!identifierInfo)
|
||||
return false;
|
||||
Declaration const* declaration = identifierInfo->declaration;
|
||||
solAssert(!!declaration, "");
|
||||
solAssert(identifierInfo->suffix == "", "");
|
||||
|
||||
SetNode setNode(*this, originLocationOf(_identifier));
|
||||
references.emplace(&_identifier, reference(*declaration));
|
||||
identifierInfo->valueSize = 1;
|
||||
return true;
|
||||
};
|
||||
solAssert(!_inlineAssembly.annotation().analysisInfo, "");
|
||||
_inlineAssembly.annotation().analysisInfo = make_shared<yul::AsmAnalysisInfo>();
|
||||
yul::AsmAnalyzer analyzer(
|
||||
*_inlineAssembly.annotation().analysisInfo,
|
||||
m_analysis.errorReporter(),
|
||||
_inlineAssembly.dialect(),
|
||||
identifierAccess
|
||||
);
|
||||
if (!analyzer.analyze(_inlineAssembly.operations()))
|
||||
solAssert(m_analysis.errorReporter().hasErrors());
|
||||
|
||||
return makeTerm<InlineAssembly>(_inlineAssembly.operations(), std::move(references));
|
||||
}
|
||||
|
||||
|
||||
unique_ptr<Term> ASTTransform::term(legacy::Expression const& _expression)
|
||||
{
|
||||
SetNode setNode(*this, _expression);
|
||||
if (auto const* id = dynamic_cast<legacy::Identifier const*>(&_expression))
|
||||
{
|
||||
solAssert(id->annotation().referencedDeclaration);
|
||||
return reference(*id->annotation().referencedDeclaration);
|
||||
}
|
||||
else if (auto const* call = dynamic_cast<legacy::FunctionCall const*>(&_expression))
|
||||
{
|
||||
list<unique_ptr<Term>> arguments;
|
||||
for (auto argument: call->arguments())
|
||||
arguments.emplace_back(term(*argument));
|
||||
return application(term(call->expression()), std::move(arguments));
|
||||
}
|
||||
else if (auto const* assign = dynamic_cast<legacy::Assignment const*>(&_expression))
|
||||
return term(*assign);
|
||||
else if (auto const* memberAccess = dynamic_cast<legacy::MemberAccess const*>(&_expression))
|
||||
{
|
||||
unique_ptr<Term> memberNameConstant;
|
||||
{
|
||||
SetNode setMemberLocation(*this, memberAccess->memberLocation());
|
||||
memberNameConstant = constant(memberAccess->memberName());
|
||||
}
|
||||
return application(BuiltinConstant::MemberAccess, term(memberAccess->expression()), std::move(memberNameConstant));
|
||||
}
|
||||
else if (auto const* operation = dynamic_cast<legacy::BinaryOperation const*>(&_expression))
|
||||
{
|
||||
unique_ptr<Term> left = term(operation->leftExpression());
|
||||
unique_ptr<Term> right = term(operation->rightExpression());
|
||||
return binaryOperation(operation->getOperator(), std::move(left), std::move(right));
|
||||
}
|
||||
else if (auto const* typeNameExpression = dynamic_cast<legacy::ElementaryTypeNameExpression const*>(&_expression))
|
||||
return term(typeNameExpression->type());
|
||||
else if (auto const* tupleExpression = dynamic_cast<legacy::TupleExpression const*>(&_expression))
|
||||
return tuple(tupleExpression->components() | ranges::view::transform([&](auto component) {
|
||||
return term(*component);
|
||||
}) | ranges::view::move | ranges::to<list<unique_ptr<Term>>>);
|
||||
else
|
||||
{
|
||||
m_analysis.errorReporter().fatalTypeError(0000_error, _expression.location(), "Unsupported expression.");
|
||||
return tuple({});
|
||||
}
|
||||
}
|
||||
|
||||
unique_ptr<Term> ASTTransform::binaryOperation(
|
||||
Token _operator,
|
||||
unique_ptr<Term> _leftHandSide,
|
||||
unique_ptr<Term> _rightHandSide
|
||||
)
|
||||
{
|
||||
return application(builtinBinaryOperator(_operator), std::move(_leftHandSide), std::move(_rightHandSide));
|
||||
}
|
||||
|
||||
unique_ptr<Term> ASTTransform::reference(legacy::Declaration const& _declaration)
|
||||
{
|
||||
auto [it, newlyInserted] = m_declarationIndices.emplace(&_declaration, m_ast->declarations.size());
|
||||
if (newlyInserted)
|
||||
m_ast->declarations.emplace_back(AST::DeclarationInfo{nullptr, {}});
|
||||
return makeTerm<Reference>(it->second);
|
||||
}
|
||||
|
||||
size_t ASTTransform::declare(legacy::Declaration const& _declaration, Term& _term)
|
||||
{
|
||||
auto [it, newlyInserted] = m_declarationIndices.emplace(&_declaration, m_ast->declarations.size());
|
||||
if (newlyInserted)
|
||||
m_ast->declarations.emplace_back(AST::DeclarationInfo{&_term, _declaration.name()});
|
||||
else
|
||||
{
|
||||
auto& info = m_ast->declarations.at(it->second);
|
||||
solAssert(!info.target);
|
||||
info.target = &_term;
|
||||
info.name = _declaration.name();
|
||||
}
|
||||
termBase(_term).declaration = it->second;
|
||||
return it->second;
|
||||
}
|
||||
|
||||
TermBase ASTTransform::makeTermBase()
|
||||
{
|
||||
return TermBase{
|
||||
m_currentLocation,
|
||||
m_currentNode ? make_optional(m_currentNode->id()) : nullopt,
|
||||
std::monostate{},
|
||||
nullopt
|
||||
};
|
||||
}
|
||||
|
||||
unique_ptr<Term> ASTTransform::constrain(unique_ptr<Term> _value, unique_ptr<Term> _constraint)
|
||||
{
|
||||
return application(BuiltinConstant::Constrain, std::move(_value), std::move(_constraint));
|
||||
}
|
||||
|
||||
unique_ptr<Term> ASTTransform::builtinTypeClass(langutil::Token _token)
|
||||
{
|
||||
switch (_token)
|
||||
{
|
||||
case Token::Mul:
|
||||
return constant(BuiltinConstant::Mul);
|
||||
case Token::Add:
|
||||
return constant(BuiltinConstant::Add);
|
||||
case Token::Integer:
|
||||
return constant(BuiltinConstant::Integer);
|
||||
case Token::Equal:
|
||||
return constant(BuiltinConstant::Equal);
|
||||
default:
|
||||
m_analysis.errorReporter().typeError(0000_error, m_currentLocation, "Invalid type class.");
|
||||
return constant(BuiltinConstant::Undefined);
|
||||
}
|
||||
}
|
||||
|
||||
unique_ptr<Term> ASTTransform::builtinBinaryOperator(Token _token)
|
||||
{
|
||||
switch (_token)
|
||||
{
|
||||
case Token::Colon:
|
||||
return constant(BuiltinConstant::Constrain);
|
||||
case Token::RightArrow:
|
||||
return constant(BuiltinConstant::Fun);
|
||||
case Token::Mul:
|
||||
return constant(BuiltinConstant::Mul);
|
||||
case Token::Add:
|
||||
return constant(BuiltinConstant::Add);
|
||||
case Token::Equal:
|
||||
return constant(BuiltinConstant::Equal);
|
||||
default:
|
||||
m_analysis.errorReporter().typeError(0000_error, m_currentLocation, "Unsupported operator.");
|
||||
return constant(BuiltinConstant::Undefined);
|
||||
}
|
||||
}
|
||||
|
||||
unique_ptr<Term> ASTTransform::pair(unique_ptr<Term> _first, unique_ptr<Term> _second)
|
||||
{
|
||||
return application(
|
||||
application(
|
||||
BuiltinConstant::Pair,
|
||||
std::move(_first)
|
||||
),
|
||||
std::move(_second)
|
||||
);
|
||||
}
|
||||
|
||||
unique_ptr<Term> ASTTransform::tuple(list<unique_ptr<Term>> _components)
|
||||
{
|
||||
if (auto term = ranges::fold_right_last(_components | ranges::view::move, [&](auto a, auto b) { return pair(std::move(a), std::move(b)); }))
|
||||
return std::move(*term);
|
||||
else
|
||||
return constant(BuiltinConstant::Unit);
|
||||
}
|
||||
|
||||
unique_ptr<Term> ASTTransform::application(unique_ptr<Term> _function, std::list<unique_ptr<Term>> _arguments)
|
||||
{
|
||||
return makeTerm<Application>(std::move(_function), tuple(std::move(_arguments)));
|
||||
}
|
168
libsolidity/experimental/analysis/ASTTransform.h
Normal file
168
libsolidity/experimental/analysis/ASTTransform.h
Normal file
@ -0,0 +1,168 @@
|
||||
/*
|
||||
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/experimental/ast/AST.h>
|
||||
#include <libsolidity/ast/ASTVisitor.h>
|
||||
|
||||
namespace solidity::langutil
|
||||
{
|
||||
class ErrorReporter;
|
||||
}
|
||||
|
||||
namespace solidity::frontend::experimental
|
||||
{
|
||||
namespace legacy = solidity::frontend;
|
||||
|
||||
class Analysis;
|
||||
|
||||
class ASTTransform: public ASTConstVisitor
|
||||
{
|
||||
public:
|
||||
ASTTransform(Analysis& _analysis);
|
||||
|
||||
std::unique_ptr<AST> ast()
|
||||
{
|
||||
return std::move(m_ast);
|
||||
}
|
||||
|
||||
private:
|
||||
bool visit(legacy::SourceUnit const&) override { return true; }
|
||||
bool visit(legacy::PragmaDirective const&) override { return true; }
|
||||
bool visit(legacy::ImportDirective const&) override { return true; }
|
||||
bool visit(legacy::ContractDefinition const& _contractDefinition) override;
|
||||
bool visit(legacy::FunctionDefinition const& _functionDefinition) override;
|
||||
bool visit(legacy::TypeClassDefinition const& _typeClassDefinition) override;
|
||||
bool visit(legacy::TypeClassInstantiation const& _typeClassInstantiation) override;
|
||||
bool visit(legacy::TypeDefinition const& _typeDefinition) override;
|
||||
|
||||
AST::FunctionInfo functionDefinition(legacy::FunctionDefinition const& _functionDefinition);
|
||||
std::unique_ptr<Term> term(legacy::ParameterList const& _parameterList);
|
||||
std::unique_ptr<Term> term(legacy::VariableDeclaration const& _variableDeclaration, std::unique_ptr<Term> _initialValue = nullptr);
|
||||
std::unique_ptr<Term> term(legacy::Block const& _block);
|
||||
std::unique_ptr<Term> term(std::vector<ASTPointer<legacy::Statement>> const& _statements);
|
||||
std::unique_ptr<Term> term(legacy::InlineAssembly const& _assembly);
|
||||
std::unique_ptr<Term> term(legacy::VariableDeclarationStatement const& _declaration);
|
||||
std::unique_ptr<Term> term(legacy::Statement const& _statements);
|
||||
std::unique_ptr<Term> term(legacy::Expression const& _expression);
|
||||
std::unique_ptr<Term> term(legacy::Assignment const& _assignment);
|
||||
std::unique_ptr<Term> term(legacy::TypeName const& _name);
|
||||
|
||||
std::unique_ptr<Term> binaryOperation(
|
||||
langutil::Token _operator,
|
||||
std::unique_ptr<Term> _leftHandSide,
|
||||
std::unique_ptr<Term> _rightHandSide
|
||||
);
|
||||
|
||||
std::unique_ptr<Term> constant(BuiltinConstant _constant)
|
||||
{
|
||||
return makeTerm<Constant>(_constant);
|
||||
}
|
||||
std::unique_ptr<Term> constant(std::string _name)
|
||||
{
|
||||
return makeTerm<Constant>(_name);
|
||||
}
|
||||
|
||||
// Allows for easy uniform treatment in the variadic templates below.
|
||||
std::unique_ptr<Term> term(std::unique_ptr<Term> _term) { return _term; }
|
||||
|
||||
std::unique_ptr<Term> tuple(std::list<std::unique_ptr<Term>> _components);
|
||||
template<typename... Args>
|
||||
std::unique_ptr<Term> tuple(Args&&... _args)
|
||||
{
|
||||
std::list<std::unique_ptr<Term>> components;
|
||||
(components.emplace_back(term(std::forward<Args>(_args))), ...);
|
||||
return tuple(std::move(components));
|
||||
}
|
||||
std::unique_ptr<Term> pair(std::unique_ptr<Term> _first, std::unique_ptr<Term> _second);
|
||||
std::unique_ptr<Term> application(std::unique_ptr<Term> _function, std::list<std::unique_ptr<Term>> _argument);
|
||||
template<typename... Args>
|
||||
std::unique_ptr<Term> application(std::unique_ptr<Term> _function, Args&&... _args)
|
||||
{
|
||||
std::list<std::unique_ptr<Term>> components;
|
||||
(components.emplace_back(term(std::forward<Args>(_args))), ...);
|
||||
return application(std::move(_function), std::move(components));
|
||||
}
|
||||
|
||||
template<typename... Args>
|
||||
std::unique_ptr<Term> application(BuiltinConstant _function, Args&&... _args)
|
||||
{
|
||||
std::list<std::unique_ptr<Term>> components;
|
||||
(components.emplace_back(term(std::forward<Args>(_args))), ...);
|
||||
return application(constant(_function), std::move(components));
|
||||
}
|
||||
|
||||
std::unique_ptr<Term> constrain(std::unique_ptr<Term> _value, std::unique_ptr<Term> _constraint);
|
||||
std::unique_ptr<Term> builtinBinaryOperator(langutil::Token);
|
||||
std::unique_ptr<Term> builtinTypeClass(langutil::Token);
|
||||
std::unique_ptr<Term> reference(legacy::Declaration const& _declaration);
|
||||
size_t declare(legacy::Declaration const& _declaration, Term& _term);
|
||||
|
||||
struct SetNode {
|
||||
SetNode(ASTTransform& _parent, ASTNode const& _node):
|
||||
m_parent(_parent),
|
||||
m_previousNode(_parent.m_currentNode),
|
||||
m_previousLocation(_parent.m_currentLocation)
|
||||
{
|
||||
_parent.m_currentNode = &_node;
|
||||
_parent.m_currentLocation = _node.location();
|
||||
}
|
||||
SetNode(ASTTransform& _parent, langutil::SourceLocation const& _location):
|
||||
m_parent(_parent),
|
||||
m_previousNode(_parent.m_currentNode),
|
||||
m_previousLocation(_parent.m_currentLocation)
|
||||
{
|
||||
_parent.m_currentNode = nullptr;
|
||||
_parent.m_currentLocation = _location;
|
||||
}
|
||||
~SetNode() {
|
||||
m_parent.m_currentNode = m_previousNode;
|
||||
m_parent.m_currentLocation = m_previousLocation;
|
||||
}
|
||||
private:
|
||||
ASTTransform& m_parent;
|
||||
ASTNode const* m_previousNode = nullptr;
|
||||
langutil::SourceLocation m_previousLocation;
|
||||
};
|
||||
TermBase makeTermBase();
|
||||
template<typename TermKind, typename... Args>
|
||||
std::unique_ptr<Term> makeTerm(Args&&... _args)
|
||||
{
|
||||
return std::make_unique<Term>(TermKind{
|
||||
makeTermBase(),
|
||||
std::forward<Args>(_args)...
|
||||
});
|
||||
}
|
||||
template<typename T>
|
||||
std::unique_ptr<Term> termOrConstant(T const* _node, BuiltinConstant _constant)
|
||||
{
|
||||
if (_node)
|
||||
return term(*_node);
|
||||
else
|
||||
return constant(_constant);
|
||||
}
|
||||
|
||||
Analysis& m_analysis;
|
||||
langutil::ErrorReporter& m_errorReporter;
|
||||
ASTNode const* m_currentNode = nullptr;
|
||||
langutil::SourceLocation m_currentLocation;
|
||||
std::unique_ptr<AST> m_ast;
|
||||
|
||||
std::map<frontend::Declaration const*, size_t, ASTCompareByID<frontend::Declaration>> m_declarationIndices;
|
||||
};
|
||||
|
||||
}
|
@ -21,6 +21,9 @@
|
||||
#include <libsolidity/experimental/analysis/SyntaxRestrictor.h>
|
||||
#include <libsolidity/experimental/analysis/TypeInference.h>
|
||||
#include <libsolidity/experimental/analysis/TypeRegistration.h>
|
||||
#include <libsolidity/experimental/analysis/ASTTransform.h>
|
||||
#include <libsolidity/experimental/analysis/TypeCheck.h>
|
||||
|
||||
|
||||
using namespace std;
|
||||
using namespace solidity::langutil;
|
||||
@ -121,9 +124,21 @@ std::tuple<std::integral_constant<size_t, Is>...> makeIndexTuple(std::index_sequ
|
||||
return std::make_tuple( std::integral_constant<size_t, Is>{}...);
|
||||
}
|
||||
|
||||
bool Analysis::check(vector<shared_ptr<SourceUnit const>> const& _sourceUnits)
|
||||
bool Analysis::check(vector<shared_ptr<legacy::SourceUnit const>> const& _sourceUnits)
|
||||
{
|
||||
using AnalysisSteps = std::tuple<SyntaxRestrictor, TypeRegistration, TypeInference, DebugWarner>;
|
||||
std::unique_ptr<AST> ast;
|
||||
{
|
||||
ASTTransform astTransform(*this);
|
||||
for (auto source: _sourceUnits)
|
||||
source->accept(astTransform);
|
||||
ast = astTransform.ast();
|
||||
}
|
||||
{
|
||||
TypeCheck typeChecker(*this);
|
||||
typeChecker(*ast);
|
||||
}
|
||||
|
||||
using AnalysisSteps = std::tuple<SyntaxRestrictor, TypeRegistration, TypeInference/*, DebugWarner*/>;
|
||||
|
||||
return std::apply([&](auto... _indexTuple) {
|
||||
return ([&](auto&& _step) {
|
||||
|
19
libsolidity/experimental/analysis/ExpressionEvaluator.cpp
Normal file
19
libsolidity/experimental/analysis/ExpressionEvaluator.cpp
Normal file
@ -0,0 +1,19 @@
|
||||
/*
|
||||
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/ExpressionEvaluator.h>
|
||||
|
87
libsolidity/experimental/analysis/ExpressionEvaluator.h
Normal file
87
libsolidity/experimental/analysis/ExpressionEvaluator.h
Normal file
@ -0,0 +1,87 @@
|
||||
/*
|
||||
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 ExpressionEvaluator: public ASTConstVisitor
|
||||
{
|
||||
public:
|
||||
ExpressionEvaluator(Analysis& _analysis);
|
||||
|
||||
bool analyze(SourceUnit const& _sourceUnit);
|
||||
|
||||
struct Annotation
|
||||
{
|
||||
std::optional<u256> value;
|
||||
};
|
||||
struct GlobalAnnotation
|
||||
{
|
||||
};
|
||||
bool visit(Block const&) override { return true; }
|
||||
bool visit(VariableDeclarationStatement const&) override { return true; }
|
||||
void endVisit(VariableDeclarationStatement const& _variableDeclarationStatement) override;
|
||||
bool visit(VariableDeclaration const& _variableDeclaration) override;
|
||||
|
||||
bool visit(FunctionDefinition const& _functionDefinition) override;
|
||||
bool visit(ParameterList const&) override { return true; }
|
||||
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;
|
||||
bool visit(PragmaDirective const&) override { return false; }
|
||||
|
||||
bool visit(ExpressionStatement const&) override { return true; }
|
||||
bool visit(Assignment const&) override { return true; }
|
||||
void endVisit(Assignment const& _assignment) override;
|
||||
bool visit(Identifier const&) override;
|
||||
bool visit(IdentifierPath const&) override;
|
||||
bool visit(FunctionCall const& _functionCall) override;
|
||||
void endVisit(FunctionCall const& _functionCall) override;
|
||||
bool visit(Return const&) override { return true; }
|
||||
void endVisit(Return const& _return) override;
|
||||
|
||||
bool visit(MemberAccess const& _memberAccess) override;
|
||||
void endVisit(MemberAccess const& _memberAccess) override;
|
||||
bool visit(ElementaryTypeNameExpression const& _expression) override;
|
||||
|
||||
bool visit(TypeClassDefinition const& _typeClassDefinition) override;
|
||||
bool visit(TypeClassInstantiation const& _typeClassInstantiation) override;
|
||||
bool visit(TupleExpression const&) override { return true; }
|
||||
void endVisit(TupleExpression const& _tupleExpression) override;
|
||||
bool visit(TypeDefinition const& _typeDefinition) override;
|
||||
|
||||
bool visitNode(ASTNode const& _node) override;
|
||||
|
||||
bool visit(BinaryOperation const& _operation) override;
|
||||
|
||||
bool visit(Literal const& _literal) override;
|
||||
private:
|
||||
Analysis& m_analysis;
|
||||
langutil::ErrorReporter& m_errorReporter;
|
||||
TypeSystem& m_typeSystem;
|
||||
};
|
||||
|
||||
}
|
522
libsolidity/experimental/analysis/TypeCheck.cpp
Normal file
522
libsolidity/experimental/analysis/TypeCheck.cpp
Normal file
@ -0,0 +1,522 @@
|
||||
/*
|
||||
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/experimental/analysis/TypeCheck.h>
|
||||
#include <libsolidity/experimental/ast/TypeSystemHelper.h>
|
||||
#include <libsolidity/ast/AST.h>
|
||||
#include <libsolutil/CommonIO.h>
|
||||
#include <libsolutil/Visitor.h>
|
||||
#include <range/v3/view/map.hpp>
|
||||
|
||||
#include <liblangutil/ErrorReporter.h>
|
||||
#include <liblangutil/Exceptions.h>
|
||||
|
||||
using namespace std;
|
||||
using namespace solidity;
|
||||
using namespace langutil;
|
||||
using namespace solidity::frontend::experimental;
|
||||
|
||||
namespace
|
||||
{
|
||||
using Term = std::variant<Application, Lambda, InlineAssembly, VariableDeclaration, Reference, Constant>;
|
||||
|
||||
|
||||
optional<pair<reference_wrapper<Term const>, reference_wrapper<Term const>>> destPair(Term const& _term)
|
||||
{
|
||||
if (auto const* app = get_if<Application>(&_term))
|
||||
if (auto* nestedApp = get_if<Application>(app->expression.get()))
|
||||
if (auto* constant = get_if<Constant>(nestedApp->expression.get()))
|
||||
if (constant->name == variant<string, BuiltinConstant>{BuiltinConstant::Pair})
|
||||
return std::make_pair(ref(*nestedApp->argument), ref(*app->argument));
|
||||
return nullopt;
|
||||
}
|
||||
|
||||
void destTuple(Term const& _term, list<reference_wrapper<Term const>>& _components)
|
||||
{
|
||||
list<reference_wrapper<Term const>> components;
|
||||
if (auto const* app = get_if<Application>(&_term))
|
||||
if (auto* nestedApp = get_if<Application>(app->expression.get()))
|
||||
if (auto* constant = get_if<Constant>(nestedApp->expression.get()))
|
||||
if (constant->name == variant<string, BuiltinConstant>{BuiltinConstant::Pair})
|
||||
{
|
||||
_components.emplace_back(*nestedApp->argument);
|
||||
destTuple(*app->argument, _components);
|
||||
return;
|
||||
}
|
||||
_components.emplace_back(_term);
|
||||
return;
|
||||
}
|
||||
|
||||
Type type(Term const& _term)
|
||||
{
|
||||
return std::visit([](auto& term) { return term.type; }, _term);
|
||||
}
|
||||
|
||||
void setType(Term& _term, Type _type)
|
||||
{
|
||||
std::visit([&](auto& term) { term.type = _type; }, _term);
|
||||
}
|
||||
|
||||
string termPrinter(AST& _ast, Term const& _term, TypeEnvironment* _env = nullptr, bool _sugarPairs = true, bool _sugarConsts = true, size_t _indent = 0)
|
||||
{
|
||||
auto recurse = [&](Term const& _next) { return termPrinter(_ast, _next, _env, _sugarPairs, _sugarConsts, _indent); };
|
||||
static const std::map<BuiltinConstant, const char*> builtinConstants = {
|
||||
{BuiltinConstant::Unit, "()"},
|
||||
{BuiltinConstant::Pair, "Pair"},
|
||||
{BuiltinConstant::Fun, "Fun"},
|
||||
{BuiltinConstant::Constrain, "Constrain"},
|
||||
{BuiltinConstant::Return, "Return"},
|
||||
{BuiltinConstant::Block, "Block"},
|
||||
{BuiltinConstant::Statement, "Statement"},
|
||||
{BuiltinConstant::ChainStatements, "ChainStatements"},
|
||||
{BuiltinConstant::Assign, "Assign"},
|
||||
{BuiltinConstant::MemberAccess, "MemberAccess"},
|
||||
{BuiltinConstant::Mul, "Mul"},
|
||||
{BuiltinConstant::Add, "Add"},
|
||||
{BuiltinConstant::Void, "void"},
|
||||
{BuiltinConstant::Word, "word"},
|
||||
{BuiltinConstant::Integer, "Integer"},
|
||||
{BuiltinConstant::Bool, "Bool"},
|
||||
{BuiltinConstant::Undefined, "Undefined"},
|
||||
{BuiltinConstant::Equal, "Equal"}
|
||||
};
|
||||
string result = std::visit(util::GenericVisitor{
|
||||
[&](Application const& _app) {
|
||||
if (_sugarPairs)
|
||||
if (auto* nestedApp = get_if<Application>(_app.expression.get()))
|
||||
if (auto* constant = get_if<Constant>(nestedApp->expression.get()))
|
||||
if (constant->name == variant<string, BuiltinConstant>{BuiltinConstant::Pair})
|
||||
{
|
||||
list<reference_wrapper<Term const>> components;
|
||||
destTuple(_term, components);
|
||||
std::string result = "(";
|
||||
result += termPrinter(_ast, components.front(), _env);
|
||||
components.pop_front();
|
||||
for (auto const& component: components)
|
||||
{
|
||||
result += ", ";
|
||||
result += recurse(component);
|
||||
}
|
||||
result += ")";
|
||||
return result;
|
||||
}
|
||||
|
||||
if (_sugarConsts)
|
||||
if (auto* constant = get_if<Constant>(_app.expression.get()))
|
||||
if (auto* builtin = get_if<BuiltinConstant>(&constant->name))
|
||||
switch (*builtin)
|
||||
{
|
||||
case BuiltinConstant::Assign:
|
||||
if (auto pair = destPair(*_app.argument))
|
||||
return recurse(pair->first) + " = " + recurse(pair->second);
|
||||
break;
|
||||
case BuiltinConstant::Mul:
|
||||
if (auto pair = destPair(*_app.argument))
|
||||
return recurse(pair->first) + " * " + recurse(pair->second);
|
||||
break;
|
||||
case BuiltinConstant::Add:
|
||||
if (auto pair = destPair(*_app.argument))
|
||||
return recurse(pair->first) + " + " + recurse(pair->second);
|
||||
break;
|
||||
case BuiltinConstant::Constrain:
|
||||
if (auto pair = destPair(*_app.argument))
|
||||
return recurse(pair->first) + ":" + recurse(pair->second);
|
||||
break;
|
||||
case BuiltinConstant::MemberAccess:
|
||||
if (auto pair = destPair(*_app.argument))
|
||||
return recurse(pair->first) + "." + recurse(pair->second);
|
||||
break;
|
||||
case BuiltinConstant::Block:
|
||||
{
|
||||
string result = "{\n";
|
||||
_indent++;
|
||||
result += std::string(_indent, '\t');
|
||||
result += recurse(*_app.argument);
|
||||
_indent--;
|
||||
result += "\n" + std::string(_indent, '\t') + "}";
|
||||
return result;
|
||||
}
|
||||
case BuiltinConstant::ChainStatements:
|
||||
if (auto pair = destPair(*_app.argument))
|
||||
return recurse(pair->first) + "\n" + std::string(_indent, '\t') + recurse(pair->second);
|
||||
break;
|
||||
case BuiltinConstant::Statement:
|
||||
return recurse(*_app.argument) + ";";
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return recurse(*_app.expression) + "(" + recurse(*_app.argument) + ")";
|
||||
},
|
||||
[&](Lambda const& _lambda) {
|
||||
return "(" + recurse(*_lambda.argument) + " -> " + recurse(*_lambda.value) + ")";
|
||||
},
|
||||
[&](InlineAssembly const&) -> string {
|
||||
return "assembly";
|
||||
},
|
||||
[&](VariableDeclaration const& _varDecl) {
|
||||
return "let " + recurse(*_varDecl.namePattern) + (_varDecl.initialValue ? " = " + recurse(*_varDecl.initialValue) : "");
|
||||
},
|
||||
[&](Reference const& _reference) {
|
||||
return "" + _ast.declarations.at(_reference.index).name + "";
|
||||
},
|
||||
[&](Constant const& _constant) {
|
||||
return "" + std::visit(util::GenericVisitor{
|
||||
[](BuiltinConstant _constant) -> string {
|
||||
return builtinConstants.at(_constant);
|
||||
},
|
||||
[](std::string const& _name) {
|
||||
return _name;
|
||||
}
|
||||
}, _constant.name) + "";
|
||||
}
|
||||
}, _term);
|
||||
if (_env)
|
||||
{
|
||||
Type termType = type(_term);
|
||||
if (!holds_alternative<std::monostate>(termType))
|
||||
{
|
||||
result += "[:" + TypeEnvironmentHelpers{*_env}.typeToString(termType) + "]";
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string functionPrinter(AST& _ast, AST::FunctionInfo const& _info, TypeEnvironment* _env = nullptr, bool _sugarPairs = true, bool _sugarConsts = true, size_t _indent = 0)
|
||||
{
|
||||
auto printTerm = [&](Term const& _term) { return termPrinter(_ast, _term, _env, _sugarPairs, _sugarConsts, _indent + 1); };
|
||||
return "function (" + printTerm(*_info.arguments) + ") -> " + printTerm(*_info.returnType) + " = " + printTerm(*_info.function) + "\n";
|
||||
}
|
||||
|
||||
std::string astPrinter(AST& _ast, TypeEnvironment* _env = nullptr, bool _sugarPairs = true, bool _sugarConsts = true, size_t _indent = 0)
|
||||
{
|
||||
auto printTerm = [&](Term const& _term) { return termPrinter(_ast, _term, _env, _sugarPairs, _sugarConsts, _indent + 1); };
|
||||
auto printFunction = [&](AST::FunctionInfo const& _info) { return functionPrinter(_ast, _info, _env, _sugarPairs, _sugarConsts, _indent + 1); };
|
||||
std::string result;
|
||||
for (auto& info: _ast.typeDefinitions | ranges::view::values)
|
||||
{
|
||||
result += "type " + printTerm(*info.declaration);
|
||||
if (info.arguments)
|
||||
result += " " + printTerm(*info.arguments);
|
||||
if (info.value)
|
||||
result += " = " + printTerm(*info.declaration);
|
||||
result += "\n\n";
|
||||
}
|
||||
for (auto& info: _ast.typeClasses | ranges::view::values)
|
||||
{
|
||||
result += "class " + printTerm(*info.typeVariable) + ":" + printTerm(*info.declaration) + " {";
|
||||
_indent++;
|
||||
for (auto&& functionInfo: info.functions | ranges::view::values)
|
||||
result += printFunction(functionInfo);
|
||||
_indent--;
|
||||
result += "}\n\n";
|
||||
}
|
||||
for (auto& info: _ast.typeClassInstantiations | ranges::view::values)
|
||||
{
|
||||
result += "instantiation " + printTerm(*info.typeConstructor) + "(" + printTerm(*info.argumentSorts) + "):" + printTerm(*info.typeClass) + "{\n";
|
||||
_indent++;
|
||||
for (auto&& functionInfo: info.functions | ranges::view::values)
|
||||
result += printFunction(functionInfo);
|
||||
_indent--;
|
||||
}
|
||||
for (auto& functionInfo: _ast.functions | ranges::views::values)
|
||||
{
|
||||
result += printFunction(functionInfo);
|
||||
result += "\n";
|
||||
}
|
||||
for (auto& [contract, info]: _ast.contracts)
|
||||
{
|
||||
result += "contract " + contract->name() + " {\n";
|
||||
_indent++;
|
||||
for(auto& function: info.functions | ranges::view::values)
|
||||
result += printFunction(function);
|
||||
_indent--;
|
||||
result += "}\n\n";
|
||||
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
struct TVar
|
||||
{
|
||||
TypeEnvironment& env;
|
||||
Type type;
|
||||
};
|
||||
inline TVar operator>>(TVar a, TVar b)
|
||||
{
|
||||
TypeSystemHelpers helper{a.env.typeSystem()};
|
||||
return TVar{a.env, helper.functionType(a.type, b.type)};
|
||||
}
|
||||
inline TVar operator,(TVar a, TVar b)
|
||||
{
|
||||
TypeSystemHelpers helper{a.env.typeSystem()};
|
||||
return TVar{a.env, helper.tupleType({a.type, b.type})};
|
||||
}
|
||||
template <typename T>
|
||||
struct ArgumentCount;
|
||||
template <typename R, typename... Args>
|
||||
struct ArgumentCount<std::function<R(Args...)>> {
|
||||
static constexpr size_t value = sizeof...(Args);
|
||||
};
|
||||
struct TypeGenerator
|
||||
{
|
||||
template<typename Generator>
|
||||
TypeGenerator(Generator&& _generator):generator([generator = std::move(_generator)](TypeEnvironment& _env) -> Type {
|
||||
return invoke(_env, generator, std::make_index_sequence<ArgumentCount<decltype(std::function{_generator})>::value>{});
|
||||
}) {}
|
||||
TypeGenerator(TVar _type):generator([type = _type.type](TypeEnvironment& _env) -> Type { return _env.fresh(type); }) {}
|
||||
TypeGenerator(PrimitiveType _type): generator([type = _type](TypeEnvironment& _env) -> Type { return _env.typeSystem().type(type, {}); }) {}
|
||||
Type operator()(TypeEnvironment& _env) const { return generator(_env); }
|
||||
private:
|
||||
template<size_t I>
|
||||
static TVar makeFreshVariable(TypeEnvironment& _env) { return TVar{_env, _env.typeSystem().freshTypeVariable({}) }; }
|
||||
template<typename Generator, size_t... Is>
|
||||
static Type invoke(TypeEnvironment& _env, Generator&& _generator, std::index_sequence<Is...>)
|
||||
{
|
||||
// Use an auxiliary array to ensure deterministic evaluation order.
|
||||
std::array<TVar, sizeof...(Is)> tvars{makeFreshVariable<Is>(_env)...};
|
||||
return std::invoke(_generator, tvars[Is]...).type;
|
||||
}
|
||||
std::function<Type(TypeEnvironment&)> generator;
|
||||
};
|
||||
}
|
||||
|
||||
void TypeCheck::operator()(AST& _ast)
|
||||
{
|
||||
TypeSystem& typeSystem = m_analysis.typeSystem();
|
||||
TypeSystemHelpers helper{typeSystem};
|
||||
TypeEnvironment& env = typeSystem.env();
|
||||
|
||||
TVar unit = TVar{env, typeSystem.type(PrimitiveType::Unit, {})};
|
||||
TVar word = TVar{env, typeSystem.type(PrimitiveType::Word, {})};
|
||||
std::unique_ptr<TVar> currentReturn;
|
||||
std::map<BuiltinConstant, TypeGenerator> builtinConstantTypeGenerators{
|
||||
{BuiltinConstant::Unit, unit},
|
||||
{BuiltinConstant::Pair, [](TVar a, TVar b) { return a >> (b >> (a,b)); }},
|
||||
{BuiltinConstant::Word, word},
|
||||
{BuiltinConstant::Assign, [=](TVar a) { return (a,a) >> unit; }}, // TODO: (a,a) >> a
|
||||
{BuiltinConstant::Block, [](TVar a) { return a >> a; }},
|
||||
{BuiltinConstant::ChainStatements, [](TVar a, TVar b) { return (a,b) >> b; }},
|
||||
{BuiltinConstant::Statement, [=](TVar a) { return a >> unit; }},
|
||||
{BuiltinConstant::Return, [&]() {
|
||||
solAssert(currentReturn);
|
||||
return *currentReturn >> unit;
|
||||
}},
|
||||
{BuiltinConstant::Fun, [&](TVar a, TVar b) {
|
||||
return (a,b) >> (a >> b);
|
||||
}},
|
||||
};
|
||||
|
||||
auto unifyForTerm = [&](Type _a, Type _b, Term* _term) {
|
||||
for (auto failure: env.unify(_a, _b))
|
||||
{
|
||||
TypeEnvironmentHelpers envHelper{env};
|
||||
SourceLocation _location = _term ? locationOf(*_term) : SourceLocation{};
|
||||
std::visit(util::GenericVisitor{
|
||||
[&](TypeEnvironment::TypeMismatch _typeMismatch) {
|
||||
m_analysis.errorReporter().typeError(
|
||||
0000_error,
|
||||
_location,
|
||||
fmt::format(
|
||||
"Cannot unify {} and {}.",
|
||||
envHelper.typeToString(_typeMismatch.a),
|
||||
envHelper.typeToString(_typeMismatch.b))
|
||||
);
|
||||
},
|
||||
[&](TypeEnvironment::SortMismatch _sortMismatch) {
|
||||
m_analysis.errorReporter().typeError(0000_error, _location, fmt::format(
|
||||
"{} does not have sort {}",
|
||||
envHelper.typeToString(_sortMismatch.type),
|
||||
TypeSystemHelpers{m_analysis.typeSystem()}.sortToString(_sortMismatch.sort)
|
||||
));
|
||||
},
|
||||
[&](TypeEnvironment::RecursiveUnification _recursiveUnification) {
|
||||
m_analysis.errorReporter().typeError(
|
||||
0000_error,
|
||||
_location,
|
||||
fmt::format(
|
||||
"Recursive unification: {} occurs in {}.",
|
||||
envHelper.typeToString(_recursiveUnification.var),
|
||||
envHelper.typeToString(_recursiveUnification.type))
|
||||
);
|
||||
}
|
||||
}, failure);
|
||||
}
|
||||
};
|
||||
auto checkTerm = [&](Term& _root) {
|
||||
std::list<reference_wrapper<Term>> heap;
|
||||
heap.emplace_back(_root);
|
||||
auto checked = [](Term const& _term) {
|
||||
return !holds_alternative<std::monostate>(type(_term));
|
||||
};
|
||||
auto canCheck = [&](Term& _term) -> bool {
|
||||
bool hasUnchecked = false;
|
||||
auto stage = [&](Term& _term) {
|
||||
if (!checked(_term))
|
||||
{
|
||||
heap.push_back(_term);
|
||||
hasUnchecked = true;
|
||||
}
|
||||
};
|
||||
std::visit(util::GenericVisitor{
|
||||
[&](Application const& _app) {
|
||||
stage(*_app.expression);
|
||||
stage(*_app.argument);
|
||||
},
|
||||
[&](Lambda const& _lambda)
|
||||
{
|
||||
stage(*_lambda.argument);
|
||||
stage(*_lambda.value);
|
||||
},
|
||||
[&](InlineAssembly const&)
|
||||
{
|
||||
// TODO
|
||||
},
|
||||
[&](VariableDeclaration const& _varDecl)
|
||||
{
|
||||
stage(*_varDecl.namePattern);
|
||||
if (_varDecl.initialValue)
|
||||
stage(*_varDecl.initialValue);
|
||||
},
|
||||
[&](Reference const&)
|
||||
{
|
||||
},
|
||||
[&](Constant const&) {}
|
||||
}, _term);
|
||||
if (hasUnchecked)
|
||||
{
|
||||
stage(_term);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
std::map<size_t, Type> declarationTypes;
|
||||
while (!heap.empty())
|
||||
{
|
||||
Term& current = heap.front();
|
||||
heap.pop_front();
|
||||
if (checked(current))
|
||||
continue;
|
||||
if (!canCheck(current))
|
||||
continue;
|
||||
|
||||
auto unify = [&](Type _a, Type _b) { unifyForTerm(_a, _b, ¤t); };
|
||||
|
||||
std::visit(util::GenericVisitor{
|
||||
[&](Application const& _app) {
|
||||
if (auto* constant = get_if<Constant>(_app.expression.get()))
|
||||
if (auto* builtin = get_if<BuiltinConstant>(&constant->name))
|
||||
if (*builtin == BuiltinConstant::Constrain)
|
||||
if (auto args = destPair(*_app.argument))
|
||||
{
|
||||
Type result = type(args->first);
|
||||
unify(result, type(args->second));
|
||||
setType(current, result);
|
||||
return;
|
||||
}
|
||||
Type resultType = typeSystem.freshTypeVariable({});
|
||||
unify(helper.functionType(type(*_app.argument), resultType), type(*_app.expression));
|
||||
setType(current, resultType);
|
||||
},
|
||||
[&](Lambda const& _lambda)
|
||||
{
|
||||
setType(current, helper.functionType(type(*_lambda.argument), type(*_lambda.value)));
|
||||
},
|
||||
[&](InlineAssembly const& _inlineAssembly)
|
||||
{
|
||||
// TODO
|
||||
(void)_inlineAssembly;
|
||||
setType(current, typeSystem.type(PrimitiveType::Unit, {}));
|
||||
},
|
||||
[&](VariableDeclaration const& _varDecl)
|
||||
{
|
||||
Type name = type(*_varDecl.namePattern);
|
||||
if (_varDecl.initialValue)
|
||||
unify(name, type(*_varDecl.initialValue));
|
||||
setType(current, name);
|
||||
},
|
||||
[&](Reference const& _reference)
|
||||
{
|
||||
Type result = typeSystem.freshTypeVariable({});
|
||||
if (
|
||||
auto [it, newlyInserted] = declarationTypes.emplace(_reference.index, result);
|
||||
!newlyInserted
|
||||
)
|
||||
unify(result, it->second);
|
||||
setType(current, result);
|
||||
},
|
||||
[&](Constant const& _constant)
|
||||
{
|
||||
bool assigned = std::visit(util::GenericVisitor{
|
||||
[&](std::string const&) { return false; },
|
||||
[&](BuiltinConstant const& _constant) {
|
||||
if (auto* generator = util::valueOrNullptr(builtinConstantTypeGenerators, _constant))
|
||||
{
|
||||
setType(current, (*generator)(env));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}, _constant.name);
|
||||
if (!assigned)
|
||||
setType(current, typeSystem.freshTypeVariable({}));
|
||||
}
|
||||
}, current);
|
||||
solAssert(checked(current));
|
||||
if (auto declaration = termBase(current).declaration)
|
||||
{
|
||||
if (
|
||||
auto [it, newlyInserted] = declarationTypes.emplace(*declaration, type(current));
|
||||
!newlyInserted
|
||||
)
|
||||
unify(type(current), it->second);
|
||||
}
|
||||
}
|
||||
};
|
||||
for(auto& info: _ast.typeDefinitions | ranges::view::values)
|
||||
{
|
||||
if (info.arguments)
|
||||
checkTerm(*info.arguments);
|
||||
if (info.value)
|
||||
checkTerm(*info.value);
|
||||
checkTerm(*info.declaration);
|
||||
}
|
||||
for(auto& info: _ast.contracts | ranges::view::values)
|
||||
for(auto& function: info.functions | ranges::view::values)
|
||||
{
|
||||
checkTerm(*function.returnType);
|
||||
ScopedSaveAndRestore returnType{currentReturn, std::make_unique<TVar>(TVar{env,type(*function.returnType)})};
|
||||
checkTerm(*function.function);
|
||||
checkTerm(*function.arguments);
|
||||
// TODO: unify stuff?
|
||||
|
||||
}
|
||||
for(auto&& info: _ast.functions | ranges::view::values)
|
||||
{
|
||||
checkTerm(*info.returnType);
|
||||
ScopedSaveAndRestore returnType{currentReturn, std::make_unique<TVar>(TVar{env,type(*info.returnType)})};
|
||||
checkTerm(*info.function);
|
||||
checkTerm(*info.arguments);
|
||||
// TODO: unify stuff
|
||||
}
|
||||
|
||||
std::cout << astPrinter(_ast, &env) << std::endl;
|
||||
}
|
35
libsolidity/experimental/analysis/TypeCheck.h
Normal file
35
libsolidity/experimental/analysis/TypeCheck.h
Normal file
@ -0,0 +1,35 @@
|
||||
/*
|
||||
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/experimental/analysis/Analysis.h>
|
||||
#include <libsolidity/experimental/ast/AST.h>
|
||||
|
||||
namespace solidity::frontend::experimental
|
||||
{
|
||||
class TypeCheck
|
||||
{
|
||||
public:
|
||||
TypeCheck(Analysis& _analysis): m_analysis(_analysis) {}
|
||||
|
||||
void operator()(AST& _ast);
|
||||
|
||||
private:
|
||||
Analysis& m_analysis;
|
||||
};
|
||||
}
|
@ -194,7 +194,8 @@ bool TypeInference::visit(InlineAssembly const& _inlineAssembly)
|
||||
identifierInfo->valueSize = 1;
|
||||
return true;
|
||||
};
|
||||
solAssert(!_inlineAssembly.annotation().analysisInfo, "");
|
||||
// TODO: temporarily disabled due to also trying the Desugarer
|
||||
// solAssert(!_inlineAssembly.annotation().analysisInfo, "");
|
||||
_inlineAssembly.annotation().analysisInfo = make_shared<yul::AsmAnalysisInfo>();
|
||||
yul::AsmAnalyzer analyzer(
|
||||
*_inlineAssembly.annotation().analysisInfo,
|
||||
|
185
libsolidity/experimental/ast/AST.h
Normal file
185
libsolidity/experimental/ast/AST.h
Normal file
@ -0,0 +1,185 @@
|
||||
/*
|
||||
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/experimental/ast/TypeSystem.h>
|
||||
#include <libsolidity/ast/ASTForward.h>
|
||||
#include <liblangutil/SourceLocation.h>
|
||||
#include <libyul/ASTForward.h>
|
||||
|
||||
#include <string>
|
||||
#include <optional>
|
||||
#include <variant>
|
||||
|
||||
namespace solidity::frontend::experimental
|
||||
{
|
||||
|
||||
struct TermBase
|
||||
{
|
||||
langutil::SourceLocation location;
|
||||
std::optional<int64_t> legacyId;
|
||||
Type type;
|
||||
std::optional<size_t> declaration;
|
||||
};
|
||||
|
||||
|
||||
struct Application;
|
||||
struct Lambda;
|
||||
struct InlineAssembly;
|
||||
struct VariableDeclaration;
|
||||
struct Reference: TermBase
|
||||
{
|
||||
size_t index = std::numeric_limits<size_t>::max();
|
||||
};
|
||||
|
||||
enum class BuiltinConstant
|
||||
{
|
||||
// TODO: sort
|
||||
Unit,
|
||||
Pair,
|
||||
Fun,
|
||||
Constrain,
|
||||
Return,
|
||||
Block,
|
||||
Statement,
|
||||
ChainStatements,
|
||||
Assign,
|
||||
MemberAccess,
|
||||
Mul,
|
||||
Add,
|
||||
Void,
|
||||
Word,
|
||||
Integer,
|
||||
Bool,
|
||||
Undefined,
|
||||
Equal
|
||||
};
|
||||
|
||||
struct Constant: TermBase
|
||||
{
|
||||
std::variant<std::string, BuiltinConstant> name;
|
||||
};
|
||||
|
||||
using Term = std::variant<Application, Lambda, InlineAssembly, VariableDeclaration, Reference, Constant>;
|
||||
|
||||
struct InlineAssembly: TermBase
|
||||
{
|
||||
yul::Block const& block;
|
||||
std::map<yul::Identifier const*, std::unique_ptr<Term>> references;
|
||||
};
|
||||
|
||||
struct VariableDeclaration: TermBase
|
||||
{
|
||||
std::unique_ptr<Term> namePattern;
|
||||
std::unique_ptr<Term> initialValue;
|
||||
};
|
||||
|
||||
struct Application: TermBase
|
||||
{
|
||||
std::unique_ptr<Term> expression;
|
||||
std::unique_ptr<Term> argument;
|
||||
};
|
||||
|
||||
|
||||
struct Lambda: TermBase
|
||||
{
|
||||
std::unique_ptr<Term> argument;
|
||||
std::unique_ptr<Term> value;
|
||||
};
|
||||
|
||||
template<typename T, typename = void>
|
||||
struct LocationGetter
|
||||
{
|
||||
langutil::SourceLocation operator()(T const&) const { return {}; }
|
||||
};
|
||||
template<typename T>
|
||||
struct LocationGetter<T, std::enable_if_t<std::is_base_of_v<TermBase, T>>>
|
||||
{
|
||||
langutil::SourceLocation operator()(T const& _t) const
|
||||
{
|
||||
return _t.location;
|
||||
}
|
||||
};
|
||||
template<>
|
||||
struct LocationGetter<Term>
|
||||
{
|
||||
langutil::SourceLocation operator()(Term const& _term) const
|
||||
{
|
||||
return std::visit([](auto const& _expression) -> langutil::SourceLocation {
|
||||
return LocationGetter<std::remove_const_t<std::remove_reference_t<decltype(_expression)>>>{}(_expression);
|
||||
}, _term);
|
||||
}
|
||||
};
|
||||
|
||||
inline TermBase& termBase(Term& _term)
|
||||
{
|
||||
return std::visit([](auto& _term) -> TermBase& { return static_cast<TermBase&>(_term); }, _term);
|
||||
}
|
||||
inline TermBase const& termBase(Term const& _term)
|
||||
{
|
||||
return std::visit([](auto const& _term) -> TermBase const& { return static_cast<TermBase const&>(_term); }, _term);
|
||||
}
|
||||
|
||||
|
||||
template<typename T>
|
||||
langutil::SourceLocation locationOf(T const& _t)
|
||||
{
|
||||
return LocationGetter<T>{}(_t);
|
||||
}
|
||||
|
||||
|
||||
struct AST
|
||||
{
|
||||
struct FunctionInfo {
|
||||
std::unique_ptr<Term> function;
|
||||
std::unique_ptr<Term> arguments;
|
||||
std::unique_ptr<Term> returnType;
|
||||
};
|
||||
std::map<frontend::FunctionDefinition const*, FunctionInfo, ASTCompareByID<frontend::FunctionDefinition>> functions;
|
||||
struct ContractInfo {
|
||||
std::map<std::string, FunctionInfo> functions;
|
||||
};
|
||||
std::map<frontend::ContractDefinition const*, ContractInfo, ASTCompareByID<frontend::ContractDefinition>> contracts;
|
||||
struct TypeInformation {
|
||||
std::unique_ptr<Term> declaration;
|
||||
std::unique_ptr<Term> arguments;
|
||||
std::unique_ptr<Term> value;
|
||||
};
|
||||
std::map<frontend::TypeDefinition const*, TypeInformation, ASTCompareByID<frontend::TypeDefinition>> typeDefinitions;
|
||||
struct TypeClassInformation {
|
||||
std::unique_ptr<Term> declaration;
|
||||
std::unique_ptr<Term> typeVariable;
|
||||
std::map<std::string, FunctionInfo> functions;
|
||||
};
|
||||
struct TypeClassInstantiationInformation {
|
||||
std::unique_ptr<Term> typeConstructor;
|
||||
std::unique_ptr<Term> argumentSorts;
|
||||
std::unique_ptr<Term> typeClass;
|
||||
std::map<std::string, FunctionInfo> functions;
|
||||
};
|
||||
std::map<frontend::TypeClassDefinition const*, TypeClassInformation, ASTCompareByID<frontend::TypeClassDefinition>> typeClasses;
|
||||
std::map<frontend::TypeClassInstantiation const*, TypeClassInstantiationInformation, ASTCompareByID<frontend::TypeClassInstantiation>> typeClassInstantiations;
|
||||
struct DeclarationInfo
|
||||
{
|
||||
Term const* target = nullptr;
|
||||
std::string name;
|
||||
};
|
||||
std::vector<DeclarationInfo> declarations;
|
||||
};
|
||||
|
||||
}
|
185
libsolidity/experimental/ast/GenericASTVisitor.h
Normal file
185
libsolidity/experimental/ast/GenericASTVisitor.h
Normal file
@ -0,0 +1,185 @@
|
||||
/*
|
||||
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 <functional>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <utility>
|
||||
|
||||
namespace solidity::frontend
|
||||
{
|
||||
struct Visit {};
|
||||
struct EndVisit {};
|
||||
namespace detail {
|
||||
|
||||
template<typename Visitor>
|
||||
struct Helper: public ASTConstVisitor
|
||||
{
|
||||
Helper(Visitor _visitor): visitor(_visitor) {}
|
||||
Visitor visitor;
|
||||
private:
|
||||
template<typename NodeType>
|
||||
bool genericVisit(NodeType const& _node)
|
||||
{
|
||||
return visitor(_node, Visit{});
|
||||
}
|
||||
template<typename NodeType>
|
||||
void genericEndVisit(NodeType const& _node)
|
||||
{
|
||||
visitor(_node, EndVisit{});
|
||||
}
|
||||
bool visit(SourceUnit const& _node) override { return genericVisit(_node); }
|
||||
bool visit(PragmaDirective const& _node) override { return genericVisit(_node); }
|
||||
bool visit(ImportDirective const& _node) override { return genericVisit(_node); }
|
||||
bool visit(ContractDefinition const& _node) override { return genericVisit(_node); }
|
||||
bool visit(IdentifierPath const& _node) override { return genericVisit(_node); }
|
||||
bool visit(InheritanceSpecifier const& _node) override { return genericVisit(_node); }
|
||||
bool visit(UsingForDirective const& _node) override { return genericVisit(_node); }
|
||||
bool visit(UserDefinedValueTypeDefinition const& _node) override { return genericVisit(_node); }
|
||||
bool visit(StructDefinition const& _node) override { return genericVisit(_node); }
|
||||
bool visit(EnumDefinition const& _node) override { return genericVisit(_node); }
|
||||
bool visit(EnumValue const& _node) override { return genericVisit(_node); }
|
||||
bool visit(ParameterList const& _node) override { return genericVisit(_node); }
|
||||
bool visit(OverrideSpecifier const& _node) override { return genericVisit(_node); }
|
||||
bool visit(FunctionDefinition const& _node) override { return genericVisit(_node); }
|
||||
bool visit(VariableDeclaration const& _node) override { return genericVisit(_node); }
|
||||
bool visit(ModifierDefinition const& _node) override { return genericVisit(_node); }
|
||||
bool visit(ModifierInvocation const& _node) override { return genericVisit(_node); }
|
||||
bool visit(EventDefinition const& _node) override { return genericVisit(_node); }
|
||||
bool visit(ErrorDefinition const& _node) override { return genericVisit(_node); }
|
||||
bool visit(ElementaryTypeName const& _node) override { return genericVisit(_node); }
|
||||
bool visit(UserDefinedTypeName const& _node) override { return genericVisit(_node); }
|
||||
bool visit(FunctionTypeName const& _node) override { return genericVisit(_node); }
|
||||
bool visit(Mapping const& _node) override { return genericVisit(_node); }
|
||||
bool visit(ArrayTypeName const& _node) override { return genericVisit(_node); }
|
||||
bool visit(InlineAssembly const& _node) override { return genericVisit(_node); }
|
||||
bool visit(Block const& _node) override { return genericVisit(_node); }
|
||||
bool visit(PlaceholderStatement const& _node) override { return genericVisit(_node); }
|
||||
bool visit(IfStatement const& _node) override { return genericVisit(_node); }
|
||||
bool visit(TryCatchClause const& _node) override { return genericVisit(_node); }
|
||||
bool visit(TryStatement const& _node) override { return genericVisit(_node); }
|
||||
bool visit(WhileStatement const& _node) override { return genericVisit(_node); }
|
||||
bool visit(ForStatement const& _node) override { return genericVisit(_node); }
|
||||
bool visit(Continue const& _node) override { return genericVisit(_node); }
|
||||
bool visit(Break const& _node) override { return genericVisit(_node); }
|
||||
bool visit(Return const& _node) override { return genericVisit(_node); }
|
||||
bool visit(Throw const& _node) override { return genericVisit(_node); }
|
||||
bool visit(EmitStatement const& _node) override { return genericVisit(_node); }
|
||||
bool visit(RevertStatement const& _node) override { return genericVisit(_node); }
|
||||
bool visit(VariableDeclarationStatement const& _node) override { return genericVisit(_node); }
|
||||
bool visit(ExpressionStatement const& _node) override { return genericVisit(_node); }
|
||||
bool visit(Conditional const& _node) override { return genericVisit(_node); }
|
||||
bool visit(Assignment const& _node) override { return genericVisit(_node); }
|
||||
bool visit(TupleExpression const& _node) override { return genericVisit(_node); }
|
||||
bool visit(UnaryOperation const& _node) override { return genericVisit(_node); }
|
||||
bool visit(BinaryOperation const& _node) override { return genericVisit(_node); }
|
||||
bool visit(FunctionCall const& _node) override { return genericVisit(_node); }
|
||||
bool visit(FunctionCallOptions const& _node) override { return genericVisit(_node); }
|
||||
bool visit(NewExpression const& _node) override { return genericVisit(_node); }
|
||||
bool visit(MemberAccess const& _node) override { return genericVisit(_node); }
|
||||
bool visit(IndexAccess const& _node) override { return genericVisit(_node); }
|
||||
bool visit(IndexRangeAccess const& _node) override { return genericVisit(_node); }
|
||||
bool visit(Identifier const& _node) override { return genericVisit(_node); }
|
||||
bool visit(ElementaryTypeNameExpression const& _node) override { return genericVisit(_node); }
|
||||
bool visit(Literal const& _node) override { return genericVisit(_node); }
|
||||
bool visit(StructuredDocumentation const& _node) override { return genericVisit(_node); }
|
||||
/// Experimental Solidity nodes
|
||||
/// @{
|
||||
bool visit(TypeClassDefinition const& _node) override { return genericVisit(_node); }
|
||||
bool visit(TypeClassInstantiation const& _node) override { return genericVisit(_node); }
|
||||
/// @}
|
||||
|
||||
void endVisit(SourceUnit const& _node) override { genericEndVisit(_node); }
|
||||
void endVisit(PragmaDirective const& _node) override { genericEndVisit(_node); }
|
||||
void endVisit(ImportDirective const& _node) override { genericEndVisit(_node); }
|
||||
void endVisit(ContractDefinition const& _node) override { genericEndVisit(_node); }
|
||||
void endVisit(IdentifierPath const& _node) override { genericEndVisit(_node); }
|
||||
void endVisit(InheritanceSpecifier const& _node) override { genericEndVisit(_node); }
|
||||
void endVisit(UsingForDirective const& _node) override { genericEndVisit(_node); }
|
||||
void endVisit(UserDefinedValueTypeDefinition const& _node) override { genericEndVisit(_node); }
|
||||
void endVisit(StructDefinition const& _node) override { genericEndVisit(_node); }
|
||||
void endVisit(EnumDefinition const& _node) override { genericEndVisit(_node); }
|
||||
void endVisit(EnumValue const& _node) override { genericEndVisit(_node); }
|
||||
void endVisit(ParameterList const& _node) override { genericEndVisit(_node); }
|
||||
void endVisit(OverrideSpecifier const& _node) override { genericEndVisit(_node); }
|
||||
void endVisit(FunctionDefinition const& _node) override { genericEndVisit(_node); }
|
||||
void endVisit(VariableDeclaration const& _node) override { genericEndVisit(_node); }
|
||||
void endVisit(ModifierDefinition const& _node) override { genericEndVisit(_node); }
|
||||
void endVisit(ModifierInvocation const& _node) override { genericEndVisit(_node); }
|
||||
void endVisit(EventDefinition const& _node) override { genericEndVisit(_node); }
|
||||
void endVisit(ErrorDefinition const& _node) override { genericEndVisit(_node); }
|
||||
void endVisit(ElementaryTypeName const& _node) override { genericEndVisit(_node); }
|
||||
void endVisit(UserDefinedTypeName const& _node) override { genericEndVisit(_node); }
|
||||
void endVisit(FunctionTypeName const& _node) override { genericEndVisit(_node); }
|
||||
void endVisit(Mapping const& _node) override { genericEndVisit(_node); }
|
||||
void endVisit(ArrayTypeName const& _node) override { genericEndVisit(_node); }
|
||||
void endVisit(InlineAssembly const& _node) override { genericEndVisit(_node); }
|
||||
void endVisit(Block const& _node) override { genericEndVisit(_node); }
|
||||
void endVisit(PlaceholderStatement const& _node) override { genericEndVisit(_node); }
|
||||
void endVisit(IfStatement const& _node) override { genericEndVisit(_node); }
|
||||
void endVisit(TryCatchClause const& _node) override { genericEndVisit(_node); }
|
||||
void endVisit(TryStatement const& _node) override { genericEndVisit(_node); }
|
||||
void endVisit(WhileStatement const& _node) override { genericEndVisit(_node); }
|
||||
void endVisit(ForStatement const& _node) override { genericEndVisit(_node); }
|
||||
void endVisit(Continue const& _node) override { genericEndVisit(_node); }
|
||||
void endVisit(Break const& _node) override { genericEndVisit(_node); }
|
||||
void endVisit(Return const& _node) override { genericEndVisit(_node); }
|
||||
void endVisit(Throw const& _node) override { genericEndVisit(_node); }
|
||||
void endVisit(EmitStatement const& _node) override { genericEndVisit(_node); }
|
||||
void endVisit(RevertStatement const& _node) override { genericEndVisit(_node); }
|
||||
void endVisit(VariableDeclarationStatement const& _node) override { genericEndVisit(_node); }
|
||||
void endVisit(ExpressionStatement const& _node) override { genericEndVisit(_node); }
|
||||
void endVisit(Conditional const& _node) override { genericEndVisit(_node); }
|
||||
void endVisit(Assignment const& _node) override { genericEndVisit(_node); }
|
||||
void endVisit(TupleExpression const& _node) override { genericEndVisit(_node); }
|
||||
void endVisit(UnaryOperation const& _node) override { genericEndVisit(_node); }
|
||||
void endVisit(BinaryOperation const& _node) override { genericEndVisit(_node); }
|
||||
void endVisit(FunctionCall const& _node) override { genericEndVisit(_node); }
|
||||
void endVisit(FunctionCallOptions const& _node) override { genericEndVisit(_node); }
|
||||
void endVisit(NewExpression const& _node) override { genericEndVisit(_node); }
|
||||
void endVisit(MemberAccess const& _node) override { genericEndVisit(_node); }
|
||||
void endVisit(IndexAccess const& _node) override { genericEndVisit(_node); }
|
||||
void endVisit(IndexRangeAccess const& _node) override { genericEndVisit(_node); }
|
||||
void endVisit(Identifier const& _node) override { genericEndVisit(_node); }
|
||||
void endVisit(ElementaryTypeNameExpression const& _node) override { genericEndVisit(_node); }
|
||||
void endVisit(Literal const& _node) override { genericEndVisit(_node); }
|
||||
void endVisit(StructuredDocumentation const& _node) override { genericEndVisit(_node); }
|
||||
/// Experimental Solidity nodes
|
||||
/// @{
|
||||
void endVisit( TypeClassDefinition const& _node) override { genericEndVisit(_node); }
|
||||
void endVisit( TypeClassInstantiation const& _node) override { genericEndVisit(_node); }
|
||||
/// @}
|
||||
bool visitNode(ASTNode const&) override { solAssert(false); }
|
||||
void endVisitNode(ASTNode const&) override { solAssert(false); }
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
template<typename Visitor>
|
||||
void visitAST(ASTNode const& _astRoot, Visitor&& _visitor)
|
||||
{
|
||||
detail::Helper<Visitor> helper{std::forward<Visitor>(_visitor)};
|
||||
_astRoot.accept(helper);
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue
Block a user