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