diff --git a/libsolidity/CMakeLists.txt b/libsolidity/CMakeLists.txt index 8adf0e04d..b10b48578 100644 --- a/libsolidity/CMakeLists.txt +++ b/libsolidity/CMakeLists.txt @@ -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 diff --git a/libsolidity/experimental/analysis/ASTTransform.cpp b/libsolidity/experimental/analysis/ASTTransform.cpp new file mode 100644 index 000000000..b0129d993 --- /dev/null +++ b/libsolidity/experimental/analysis/ASTTransform.cpp @@ -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 . +*/ +// SPDX-License-Identifier: GPL-3.0 +#include +#include +#include + +#include +#include + +#include +#include +#include + +#include +#include +#include + +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()) +{ +} + +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(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& functions = info.functions; + for (auto subNode: _typeClassDefinition.subNodes()) + { + auto const *function = dynamic_cast(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 { return builtinTypeClass(_token); }, + [&](ASTPointer _identifierPath) -> unique_ptr { + 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& functions = info.functions; + for (auto subNode: _typeClassInstantiation.subNodes()) + { + auto const *function = dynamic_cast(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(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>>); + if (_typeDefinition.typeExpression()) + info.value = term(*_typeDefinition.typeExpression()); + return false; +} + +unique_ptr ASTTransform::term(legacy::VariableDeclarationStatement const& _declaration) +{ + SetNode setNode(*this, _declaration); + solAssert(_declaration.declarations().size() == 1); + unique_ptr value; + if (_declaration.initialValue()) + value = term(*_declaration.initialValue()); + return term(*_declaration.declarations().front(), std::move(value)); +} + +unique_ptr 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 ASTTransform::term(TypeName const& _name) +{ + SetNode setNode(*this, _name); + if (auto const* elementaryTypeName = dynamic_cast(&_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(&_name)) + { + auto const* declaration = userDefinedTypeName->pathNode().annotation().referencedDeclaration; + solAssert(declaration); + return reference(*declaration); + } + else + solAssert(false); +} + +unique_ptr ASTTransform::term(legacy::Statement const& _statement) +{ + SetNode setNode(*this, _statement); + if (auto const* assembly = dynamic_cast(&_statement)) + return term(*assembly); + else if (auto const* declaration = dynamic_cast(&_statement)) + return term(*declaration); + else if (auto const* assign = dynamic_cast(&_statement)) + return term(*assign); + else if (auto const* expressionStatement = dynamic_cast(&_statement)) + return term(expressionStatement->expression()); + else if (auto const* returnStatement = dynamic_cast(&_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 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 body = nullptr; + unique_ptr argumentExpression = term(_functionDefinition.parameterList()); + if (_functionDefinition.isImplemented()) + body = term(_functionDefinition.body()); + unique_ptr returnType = termOrConstant(_functionDefinition.experimentalReturnExpression(), BuiltinConstant::Unit); + unique_ptr name = reference(_functionDefinition); + unique_ptr function = makeTerm(std::move(name), std::move(body)); + declare(_functionDefinition, *function); + return AST::FunctionInfo{ + std::move(function), + std::move(argumentExpression), + std::move(returnType) + }; +} + +unique_ptr 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>>); +} + +unique_ptr ASTTransform::term(legacy::VariableDeclaration const& _variableDeclaration, std::unique_ptr _initialValue) +{ + SetNode setNode(*this, _variableDeclaration); + solAssert(!_variableDeclaration.value()); + unique_ptr name = reference(_variableDeclaration); + if (_variableDeclaration.typeExpression()) + name = constrain(std::move(name), term(*_variableDeclaration.typeExpression())); + unique_ptr declaration = makeTerm(std::move(name), std::move(_initialValue)); + declare(_variableDeclaration, *declaration); + return declaration; +} + +unique_ptr ASTTransform::term(legacy::InlineAssembly const& _inlineAssembly) +{ + SetNode setNode(*this, _inlineAssembly); + std::map> 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::AsmAnalyzer analyzer( + *_inlineAssembly.annotation().analysisInfo, + m_analysis.errorReporter(), + _inlineAssembly.dialect(), + identifierAccess + ); + if (!analyzer.analyze(_inlineAssembly.operations())) + solAssert(m_analysis.errorReporter().hasErrors()); + + return makeTerm(_inlineAssembly.operations(), std::move(references)); +} + + +unique_ptr ASTTransform::term(legacy::Expression const& _expression) +{ + SetNode setNode(*this, _expression); + if (auto const* id = dynamic_cast(&_expression)) + { + solAssert(id->annotation().referencedDeclaration); + return reference(*id->annotation().referencedDeclaration); + } + else if (auto const* call = dynamic_cast(&_expression)) + { + list> 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(&_expression)) + return term(*assign); + else if (auto const* memberAccess = dynamic_cast(&_expression)) + { + unique_ptr 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(&_expression)) + { + unique_ptr left = term(operation->leftExpression()); + unique_ptr right = term(operation->rightExpression()); + return binaryOperation(operation->getOperator(), std::move(left), std::move(right)); + } + else if (auto const* typeNameExpression = dynamic_cast(&_expression)) + return term(typeNameExpression->type()); + else if (auto const* tupleExpression = dynamic_cast(&_expression)) + return tuple(tupleExpression->components() | ranges::view::transform([&](auto component) { + return term(*component); + }) | ranges::view::move | ranges::to>>); + else + { + m_analysis.errorReporter().fatalTypeError(0000_error, _expression.location(), "Unsupported expression."); + return tuple({}); + } +} + +unique_ptr ASTTransform::binaryOperation( + Token _operator, + unique_ptr _leftHandSide, + unique_ptr _rightHandSide +) +{ + return application(builtinBinaryOperator(_operator), std::move(_leftHandSide), std::move(_rightHandSide)); +} + +unique_ptr 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(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 ASTTransform::constrain(unique_ptr _value, unique_ptr _constraint) +{ + return application(BuiltinConstant::Constrain, std::move(_value), std::move(_constraint)); +} + +unique_ptr 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 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 ASTTransform::pair(unique_ptr _first, unique_ptr _second) +{ + return application( + application( + BuiltinConstant::Pair, + std::move(_first) + ), + std::move(_second) + ); +} + +unique_ptr ASTTransform::tuple(list> _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 ASTTransform::application(unique_ptr _function, std::list> _arguments) +{ + return makeTerm(std::move(_function), tuple(std::move(_arguments))); +} diff --git a/libsolidity/experimental/analysis/ASTTransform.h b/libsolidity/experimental/analysis/ASTTransform.h new file mode 100644 index 000000000..06f00aafb --- /dev/null +++ b/libsolidity/experimental/analysis/ASTTransform.h @@ -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 . +*/ +// SPDX-License-Identifier: GPL-3.0 +#pragma once +#include +#include + +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() + { + 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(legacy::ParameterList const& _parameterList); + std::unique_ptr term(legacy::VariableDeclaration const& _variableDeclaration, std::unique_ptr _initialValue = nullptr); + std::unique_ptr term(legacy::Block const& _block); + std::unique_ptr term(std::vector> const& _statements); + std::unique_ptr term(legacy::InlineAssembly const& _assembly); + std::unique_ptr term(legacy::VariableDeclarationStatement const& _declaration); + std::unique_ptr term(legacy::Statement const& _statements); + std::unique_ptr term(legacy::Expression const& _expression); + std::unique_ptr term(legacy::Assignment const& _assignment); + std::unique_ptr term(legacy::TypeName const& _name); + + std::unique_ptr binaryOperation( + langutil::Token _operator, + std::unique_ptr _leftHandSide, + std::unique_ptr _rightHandSide + ); + + std::unique_ptr constant(BuiltinConstant _constant) + { + return makeTerm(_constant); + } + std::unique_ptr constant(std::string _name) + { + return makeTerm(_name); + } + + // Allows for easy uniform treatment in the variadic templates below. + std::unique_ptr term(std::unique_ptr _term) { return _term; } + + std::unique_ptr tuple(std::list> _components); + template + std::unique_ptr tuple(Args&&... _args) + { + std::list> components; + (components.emplace_back(term(std::forward(_args))), ...); + return tuple(std::move(components)); + } + std::unique_ptr pair(std::unique_ptr _first, std::unique_ptr _second); + std::unique_ptr application(std::unique_ptr _function, std::list> _argument); + template + std::unique_ptr application(std::unique_ptr _function, Args&&... _args) + { + std::list> components; + (components.emplace_back(term(std::forward(_args))), ...); + return application(std::move(_function), std::move(components)); + } + + template + std::unique_ptr application(BuiltinConstant _function, Args&&... _args) + { + std::list> components; + (components.emplace_back(term(std::forward(_args))), ...); + return application(constant(_function), std::move(components)); + } + + std::unique_ptr constrain(std::unique_ptr _value, std::unique_ptr _constraint); + std::unique_ptr builtinBinaryOperator(langutil::Token); + std::unique_ptr builtinTypeClass(langutil::Token); + std::unique_ptr 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 + std::unique_ptr makeTerm(Args&&... _args) + { + return std::make_unique(TermKind{ + makeTermBase(), + std::forward(_args)... + }); + } + template + std::unique_ptr 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 m_ast; + + std::map> m_declarationIndices; +}; + +} diff --git a/libsolidity/experimental/analysis/Analysis.cpp b/libsolidity/experimental/analysis/Analysis.cpp index 3e1b7d59d..f7059b4d0 100644 --- a/libsolidity/experimental/analysis/Analysis.cpp +++ b/libsolidity/experimental/analysis/Analysis.cpp @@ -21,6 +21,9 @@ #include #include #include +#include +#include + using namespace std; using namespace solidity::langutil; @@ -121,9 +124,21 @@ std::tuple...> makeIndexTuple(std::index_sequ return std::make_tuple( std::integral_constant{}...); } -bool Analysis::check(vector> const& _sourceUnits) +bool Analysis::check(vector> const& _sourceUnits) { - using AnalysisSteps = std::tuple; + std::unique_ptr ast; + { + ASTTransform astTransform(*this); + for (auto source: _sourceUnits) + source->accept(astTransform); + ast = astTransform.ast(); + } + { + TypeCheck typeChecker(*this); + typeChecker(*ast); + } + + using AnalysisSteps = std::tuple; return std::apply([&](auto... _indexTuple) { return ([&](auto&& _step) { diff --git a/libsolidity/experimental/analysis/ExpressionEvaluator.cpp b/libsolidity/experimental/analysis/ExpressionEvaluator.cpp new file mode 100644 index 000000000..01f76ed32 --- /dev/null +++ b/libsolidity/experimental/analysis/ExpressionEvaluator.cpp @@ -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 . +*/ +// SPDX-License-Identifier: GPL-3.0 +#include + diff --git a/libsolidity/experimental/analysis/ExpressionEvaluator.h b/libsolidity/experimental/analysis/ExpressionEvaluator.h new file mode 100644 index 000000000..1a80eb4b4 --- /dev/null +++ b/libsolidity/experimental/analysis/ExpressionEvaluator.h @@ -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 . +*/ +// SPDX-License-Identifier: GPL-3.0 +#pragma once +#include +#include + +#include + +namespace solidity::frontend::experimental +{ + +class Analysis; + +class ExpressionEvaluator: public ASTConstVisitor +{ +public: + ExpressionEvaluator(Analysis& _analysis); + + bool analyze(SourceUnit const& _sourceUnit); + + struct Annotation + { + std::optional 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; +}; + +} diff --git a/libsolidity/experimental/analysis/TypeCheck.cpp b/libsolidity/experimental/analysis/TypeCheck.cpp new file mode 100644 index 000000000..469670c9d --- /dev/null +++ b/libsolidity/experimental/analysis/TypeCheck.cpp @@ -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 . +*/ +// SPDX-License-Identifier: GPL-3.0 +#include +#include +#include +#include +#include +#include + +#include +#include + +using namespace std; +using namespace solidity; +using namespace langutil; +using namespace solidity::frontend::experimental; + +namespace +{ +using Term = std::variant; + + +optional, reference_wrapper>> destPair(Term const& _term) +{ + if (auto const* app = get_if(&_term)) + if (auto* nestedApp = get_if(app->expression.get())) + if (auto* constant = get_if(nestedApp->expression.get())) + if (constant->name == variant{BuiltinConstant::Pair}) + return std::make_pair(ref(*nestedApp->argument), ref(*app->argument)); + return nullopt; +} + +void destTuple(Term const& _term, list>& _components) +{ + list> components; + if (auto const* app = get_if(&_term)) + if (auto* nestedApp = get_if(app->expression.get())) + if (auto* constant = get_if(nestedApp->expression.get())) + if (constant->name == variant{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 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(_app.expression.get())) + if (auto* constant = get_if(nestedApp->expression.get())) + if (constant->name == variant{BuiltinConstant::Pair}) + { + list> 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(_app.expression.get())) + if (auto* builtin = get_if(&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(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 +struct ArgumentCount; +template +struct ArgumentCount> { + static constexpr size_t value = sizeof...(Args); +}; +struct TypeGenerator +{ + template + TypeGenerator(Generator&& _generator):generator([generator = std::move(_generator)](TypeEnvironment& _env) -> Type { + return invoke(_env, generator, std::make_index_sequence::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 + static TVar makeFreshVariable(TypeEnvironment& _env) { return TVar{_env, _env.typeSystem().freshTypeVariable({}) }; } + template + static Type invoke(TypeEnvironment& _env, Generator&& _generator, std::index_sequence) + { + // Use an auxiliary array to ensure deterministic evaluation order. + std::array tvars{makeFreshVariable(_env)...}; + return std::invoke(_generator, tvars[Is]...).type; + } + std::function 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 currentReturn; + std::map 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> heap; + heap.emplace_back(_root); + auto checked = [](Term const& _term) { + return !holds_alternative(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 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(_app.expression.get())) + if (auto* builtin = get_if(&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{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{env,type(*info.returnType)})}; + checkTerm(*info.function); + checkTerm(*info.arguments); + // TODO: unify stuff + } + + std::cout << astPrinter(_ast, &env) << std::endl; +} \ No newline at end of file diff --git a/libsolidity/experimental/analysis/TypeCheck.h b/libsolidity/experimental/analysis/TypeCheck.h new file mode 100644 index 000000000..090251ab0 --- /dev/null +++ b/libsolidity/experimental/analysis/TypeCheck.h @@ -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 . +*/ +// SPDX-License-Identifier: GPL-3.0 +#pragma once + +#include +#include + +namespace solidity::frontend::experimental +{ +class TypeCheck +{ +public: + TypeCheck(Analysis& _analysis): m_analysis(_analysis) {} + + void operator()(AST& _ast); + +private: + Analysis& m_analysis; +}; +} \ No newline at end of file diff --git a/libsolidity/experimental/analysis/TypeInference.cpp b/libsolidity/experimental/analysis/TypeInference.cpp index f433451d8..d8e56082f 100644 --- a/libsolidity/experimental/analysis/TypeInference.cpp +++ b/libsolidity/experimental/analysis/TypeInference.cpp @@ -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::AsmAnalyzer analyzer( *_inlineAssembly.annotation().analysisInfo, diff --git a/libsolidity/experimental/ast/AST.h b/libsolidity/experimental/ast/AST.h new file mode 100644 index 000000000..510019c46 --- /dev/null +++ b/libsolidity/experimental/ast/AST.h @@ -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 . +*/ +// SPDX-License-Identifier: GPL-3.0 +#pragma once + +#include +#include +#include +#include + +#include +#include +#include + +namespace solidity::frontend::experimental +{ + +struct TermBase +{ + langutil::SourceLocation location; + std::optional legacyId; + Type type; + std::optional declaration; +}; + + +struct Application; +struct Lambda; +struct InlineAssembly; +struct VariableDeclaration; +struct Reference: TermBase +{ + size_t index = std::numeric_limits::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 name; +}; + +using Term = std::variant; + +struct InlineAssembly: TermBase +{ + yul::Block const& block; + std::map> references; +}; + +struct VariableDeclaration: TermBase +{ + std::unique_ptr namePattern; + std::unique_ptr initialValue; +}; + +struct Application: TermBase +{ + std::unique_ptr expression; + std::unique_ptr argument; +}; + + +struct Lambda: TermBase +{ + std::unique_ptr argument; + std::unique_ptr value; +}; + +template +struct LocationGetter +{ + langutil::SourceLocation operator()(T const&) const { return {}; } +}; +template +struct LocationGetter>> +{ + langutil::SourceLocation operator()(T const& _t) const + { + return _t.location; + } +}; +template<> +struct LocationGetter +{ + langutil::SourceLocation operator()(Term const& _term) const + { + return std::visit([](auto const& _expression) -> langutil::SourceLocation { + return LocationGetter>>{}(_expression); + }, _term); + } +}; + +inline TermBase& termBase(Term& _term) +{ + return std::visit([](auto& _term) -> TermBase& { return static_cast(_term); }, _term); +} +inline TermBase const& termBase(Term const& _term) +{ + return std::visit([](auto const& _term) -> TermBase const& { return static_cast(_term); }, _term); +} + + +template +langutil::SourceLocation locationOf(T const& _t) +{ + return LocationGetter{}(_t); +} + + +struct AST +{ + struct FunctionInfo { + std::unique_ptr function; + std::unique_ptr arguments; + std::unique_ptr returnType; + }; + std::map> functions; + struct ContractInfo { + std::map functions; + }; + std::map> contracts; + struct TypeInformation { + std::unique_ptr declaration; + std::unique_ptr arguments; + std::unique_ptr value; + }; + std::map> typeDefinitions; + struct TypeClassInformation { + std::unique_ptr declaration; + std::unique_ptr typeVariable; + std::map functions; + }; + struct TypeClassInstantiationInformation { + std::unique_ptr typeConstructor; + std::unique_ptr argumentSorts; + std::unique_ptr typeClass; + std::map functions; + }; + std::map> typeClasses; + std::map> typeClassInstantiations; + struct DeclarationInfo + { + Term const* target = nullptr; + std::string name; + }; + std::vector declarations; +}; + +} diff --git a/libsolidity/experimental/ast/GenericASTVisitor.h b/libsolidity/experimental/ast/GenericASTVisitor.h new file mode 100644 index 000000000..eab3b721c --- /dev/null +++ b/libsolidity/experimental/ast/GenericASTVisitor.h @@ -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 . +*/ +// SPDX-License-Identifier: GPL-3.0 + +#pragma once + +#include + +#include +#include +#include +#include + +namespace solidity::frontend +{ +struct Visit {}; +struct EndVisit {}; +namespace detail { + +template +struct Helper: public ASTConstVisitor +{ + Helper(Visitor _visitor): visitor(_visitor) {} + Visitor visitor; +private: + template + bool genericVisit(NodeType const& _node) + { + return visitor(_node, Visit{}); + } + template + 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 +void visitAST(ASTNode const& _astRoot, Visitor&& _visitor) +{ + detail::Helper helper{std::forward(_visitor)}; + _astRoot.accept(helper); +} + +}