Separate approach of AST transform.

This commit is contained in:
Daniel Kirchner 2023-06-28 16:45:51 +02:00
parent f9b424418c
commit 5adc255b3c
11 changed files with 1684 additions and 3 deletions

View File

@ -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

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

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

View File

@ -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) {

View 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>

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

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

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

View File

@ -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,

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

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