aboutsummaryrefslogtreecommitdiffstats
path: root/libsolidity/analysis
diff options
context:
space:
mode:
authorchriseth <c@ethdev.com>2015-10-21 06:21:52 +0800
committerchriseth <c@ethdev.com>2015-10-21 06:46:01 +0800
commite3dffb611fe1736e3ffa170e6d8dc4dee17366bd (patch)
treeb2df13e7c4c16c01b6cdc7cd5c15932031185d95 /libsolidity/analysis
parentd41f8b7ce702c3b25c48d27e2e895ccdcd04e4e0 (diff)
downloaddexon-solidity-e3dffb611fe1736e3ffa170e6d8dc4dee17366bd.tar
dexon-solidity-e3dffb611fe1736e3ffa170e6d8dc4dee17366bd.tar.gz
dexon-solidity-e3dffb611fe1736e3ffa170e6d8dc4dee17366bd.tar.bz2
dexon-solidity-e3dffb611fe1736e3ffa170e6d8dc4dee17366bd.tar.lz
dexon-solidity-e3dffb611fe1736e3ffa170e6d8dc4dee17366bd.tar.xz
dexon-solidity-e3dffb611fe1736e3ffa170e6d8dc4dee17366bd.tar.zst
dexon-solidity-e3dffb611fe1736e3ffa170e6d8dc4dee17366bd.zip
File reorganisation.
Diffstat (limited to 'libsolidity/analysis')
-rw-r--r--libsolidity/analysis/ConstantEvaluator.cpp59
-rw-r--r--libsolidity/analysis/ConstantEvaluator.h50
-rw-r--r--libsolidity/analysis/DeclarationContainer.cpp85
-rw-r--r--libsolidity/analysis/DeclarationContainer.h67
-rw-r--r--libsolidity/analysis/GlobalContext.cpp96
-rw-r--r--libsolidity/analysis/GlobalContext.h64
-rw-r--r--libsolidity/analysis/NameAndTypeResolver.cpp555
-rw-r--r--libsolidity/analysis/NameAndTypeResolver.h169
-rw-r--r--libsolidity/analysis/ReferencesResolver.cpp234
-rw-r--r--libsolidity/analysis/ReferencesResolver.h69
-rw-r--r--libsolidity/analysis/TypeChecker.cpp1328
-rw-r--r--libsolidity/analysis/TypeChecker.h122
12 files changed, 2898 insertions, 0 deletions
diff --git a/libsolidity/analysis/ConstantEvaluator.cpp b/libsolidity/analysis/ConstantEvaluator.cpp
new file mode 100644
index 00000000..6beb655e
--- /dev/null
+++ b/libsolidity/analysis/ConstantEvaluator.cpp
@@ -0,0 +1,59 @@
+/*
+ This file is part of cpp-ethereum.
+
+ cpp-ethereum 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.
+
+ cpp-ethereum 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 cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
+*/
+/**
+ * @author Christian <c@ethdev.com>
+ * @date 2015
+ * Evaluator for types of constant expressions.
+ */
+
+#include <libsolidity/analysis/ConstantEvaluator.h>
+#include <libsolidity/ast/AST.h>
+
+using namespace std;
+using namespace dev;
+using namespace dev::solidity;
+
+
+void ConstantEvaluator::endVisit(UnaryOperation const& _operation)
+{
+ TypePointer const& subType = _operation.subExpression().annotation().type;
+ if (!dynamic_cast<IntegerConstantType const*>(subType.get()))
+ BOOST_THROW_EXCEPTION(_operation.subExpression().createTypeError("Invalid constant expression."));
+ TypePointer t = subType->unaryOperatorResult(_operation.getOperator());
+ _operation.annotation().type = t;
+}
+
+void ConstantEvaluator::endVisit(BinaryOperation const& _operation)
+{
+ TypePointer const& leftType = _operation.leftExpression().annotation().type;
+ TypePointer const& rightType = _operation.rightExpression().annotation().type;
+ if (!dynamic_cast<IntegerConstantType const*>(leftType.get()))
+ BOOST_THROW_EXCEPTION(_operation.leftExpression().createTypeError("Invalid constant expression."));
+ if (!dynamic_cast<IntegerConstantType const*>(rightType.get()))
+ BOOST_THROW_EXCEPTION(_operation.rightExpression().createTypeError("Invalid constant expression."));
+ TypePointer commonType = leftType->binaryOperatorResult(_operation.getOperator(), rightType);
+ if (Token::isCompareOp(_operation.getOperator()))
+ commonType = make_shared<BoolType>();
+ _operation.annotation().type = commonType;
+}
+
+void ConstantEvaluator::endVisit(Literal const& _literal)
+{
+ _literal.annotation().type = Type::forLiteral(_literal);
+ if (!_literal.annotation().type)
+ BOOST_THROW_EXCEPTION(_literal.createTypeError("Invalid literal value."));
+}
diff --git a/libsolidity/analysis/ConstantEvaluator.h b/libsolidity/analysis/ConstantEvaluator.h
new file mode 100644
index 00000000..f311efbf
--- /dev/null
+++ b/libsolidity/analysis/ConstantEvaluator.h
@@ -0,0 +1,50 @@
+/*
+ This file is part of cpp-ethereum.
+
+ cpp-ethereum 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.
+
+ cpp-ethereum 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 cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
+*/
+/**
+ * @author Christian <c@ethdev.com>
+ * @date 2015
+ * Evaluator for types of constant expressions.
+ */
+
+#pragma once
+
+#include <libsolidity/ast/ASTVisitor.h>
+
+namespace dev
+{
+namespace solidity
+{
+
+class TypeChecker;
+
+/**
+ * Small drop-in replacement for TypeChecker to evaluate simple expressions of integer constants.
+ */
+class ConstantEvaluator: private ASTConstVisitor
+{
+public:
+ ConstantEvaluator(Expression const& _expr) { _expr.accept(*this); }
+
+private:
+ virtual void endVisit(BinaryOperation const& _operation);
+ virtual void endVisit(UnaryOperation const& _operation);
+ virtual void endVisit(Literal const& _literal);
+
+};
+
+}
+}
diff --git a/libsolidity/analysis/DeclarationContainer.cpp b/libsolidity/analysis/DeclarationContainer.cpp
new file mode 100644
index 00000000..7339ad5d
--- /dev/null
+++ b/libsolidity/analysis/DeclarationContainer.cpp
@@ -0,0 +1,85 @@
+/*
+ This file is part of cpp-ethereum.
+
+ cpp-ethereum 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.
+
+ cpp-ethereum 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 cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
+*/
+/**
+ * @author Christian <c@ethdev.com>
+ * @date 2014
+ * Scope - object that holds declaration of names.
+ */
+
+#include <libsolidity/analysis/DeclarationContainer.h>
+#include <libsolidity/ast/AST.h>
+#include <libsolidity/ast/Types.h>
+
+using namespace std;
+using namespace dev;
+using namespace dev::solidity;
+
+Declaration const* DeclarationContainer::conflictingDeclaration(Declaration const& _declaration) const
+{
+ ASTString const& declarationName(_declaration.name());
+ solAssert(!declarationName.empty(), "");
+ vector<Declaration const*> declarations;
+ if (m_declarations.count(declarationName))
+ declarations += m_declarations.at(declarationName);
+ if (m_invisibleDeclarations.count(declarationName))
+ declarations += m_invisibleDeclarations.at(declarationName);
+
+ if (dynamic_cast<FunctionDefinition const*>(&_declaration))
+ {
+ // check that all other declarations with the same name are functions
+ for (Declaration const* declaration: declarations)
+ if (!dynamic_cast<FunctionDefinition const*>(declaration))
+ return declaration;
+ }
+ else if (!declarations.empty())
+ return declarations.front();
+
+ return nullptr;
+}
+
+bool DeclarationContainer::registerDeclaration(Declaration const& _declaration, bool _invisible, bool _update)
+{
+ ASTString const& declarationName(_declaration.name());
+ if (declarationName.empty())
+ return true;
+
+ if (_update)
+ {
+ solAssert(!dynamic_cast<FunctionDefinition const*>(&_declaration), "Attempt to update function definition.");
+ m_declarations.erase(declarationName);
+ m_invisibleDeclarations.erase(declarationName);
+ }
+ else if (conflictingDeclaration(_declaration))
+ return false;
+
+ if (_invisible)
+ m_invisibleDeclarations[declarationName].push_back(&_declaration);
+ else
+ m_declarations[declarationName].push_back(&_declaration);
+ return true;
+}
+
+std::vector<Declaration const*> DeclarationContainer::resolveName(ASTString const& _name, bool _recursive) const
+{
+ solAssert(!_name.empty(), "Attempt to resolve empty name.");
+ auto result = m_declarations.find(_name);
+ if (result != m_declarations.end())
+ return result->second;
+ if (_recursive && m_enclosingContainer)
+ return m_enclosingContainer->resolveName(_name, true);
+ return vector<Declaration const*>({});
+}
diff --git a/libsolidity/analysis/DeclarationContainer.h b/libsolidity/analysis/DeclarationContainer.h
new file mode 100644
index 00000000..064724d1
--- /dev/null
+++ b/libsolidity/analysis/DeclarationContainer.h
@@ -0,0 +1,67 @@
+/*
+ This file is part of cpp-ethereum.
+
+ cpp-ethereum 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.
+
+ cpp-ethereum 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 cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
+*/
+/**
+ * @author Christian <c@ethdev.com>
+ * @date 2014
+ * Scope - object that holds declaration of names.
+ */
+
+#pragma once
+
+#include <map>
+#include <set>
+#include <boost/noncopyable.hpp>
+
+#include <libsolidity/ast/ASTForward.h>
+
+namespace dev
+{
+namespace solidity
+{
+
+/**
+ * Container that stores mappings between names and declarations. It also contains a link to the
+ * enclosing scope.
+ */
+class DeclarationContainer
+{
+public:
+ explicit DeclarationContainer(
+ Declaration const* _enclosingDeclaration = nullptr,
+ DeclarationContainer const* _enclosingContainer = nullptr
+ ):
+ m_enclosingDeclaration(_enclosingDeclaration), m_enclosingContainer(_enclosingContainer) {}
+ /// Registers the declaration in the scope unless its name is already declared or the name is empty.
+ /// @param _invisible if true, registers the declaration, reports name clashes but does not return it in @a resolveName
+ /// @param _update if true, replaces a potential declaration that is already present
+ /// @returns false if the name was already declared.
+ bool registerDeclaration(Declaration const& _declaration, bool _invisible = false, bool _update = false);
+ std::vector<Declaration const*> resolveName(ASTString const& _name, bool _recursive = false) const;
+ Declaration const* enclosingDeclaration() const { return m_enclosingDeclaration; }
+ std::map<ASTString, std::vector<Declaration const*>> const& declarations() const { return m_declarations; }
+ /// @returns whether declaration is valid, and if not also returns previous declaration.
+ Declaration const* conflictingDeclaration(Declaration const& _declaration) const;
+
+private:
+ Declaration const* m_enclosingDeclaration;
+ DeclarationContainer const* m_enclosingContainer;
+ std::map<ASTString, std::vector<Declaration const*>> m_declarations;
+ std::map<ASTString, std::vector<Declaration const*>> m_invisibleDeclarations;
+};
+
+}
+}
diff --git a/libsolidity/analysis/GlobalContext.cpp b/libsolidity/analysis/GlobalContext.cpp
new file mode 100644
index 00000000..20f8272f
--- /dev/null
+++ b/libsolidity/analysis/GlobalContext.cpp
@@ -0,0 +1,96 @@
+/*
+ This file is part of cpp-ethereum.
+
+ cpp-ethereum 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.
+
+ cpp-ethereum 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 cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
+*/
+/**
+ * @author Christian <c@ethdev.com>
+ * @author Gav Wood <g@ethdev.com>
+ * @date 2014
+ * Container of the (implicit and explicit) global objects.
+ */
+
+#include <memory>
+#include <libsolidity/analysis/GlobalContext.h>
+#include <libsolidity/ast/AST.h>
+#include <libsolidity/ast/Types.h>
+
+using namespace std;
+
+namespace dev
+{
+namespace solidity
+{
+
+GlobalContext::GlobalContext():
+m_magicVariables(vector<shared_ptr<MagicVariableDeclaration const>>{make_shared<MagicVariableDeclaration>("block", make_shared<MagicType>(MagicType::Kind::Block)),
+ make_shared<MagicVariableDeclaration>("msg", make_shared<MagicType>(MagicType::Kind::Message)),
+ make_shared<MagicVariableDeclaration>("tx", make_shared<MagicType>(MagicType::Kind::Transaction)),
+ make_shared<MagicVariableDeclaration>("now", make_shared<IntegerType>(256)),
+ make_shared<MagicVariableDeclaration>("suicide",
+ make_shared<FunctionType>(strings{"address"}, strings{}, FunctionType::Location::Suicide)),
+ make_shared<MagicVariableDeclaration>("sha3",
+ make_shared<FunctionType>(strings(), strings{"bytes32"}, FunctionType::Location::SHA3, true)),
+ make_shared<MagicVariableDeclaration>("log0",
+ make_shared<FunctionType>(strings{"bytes32"}, strings{}, FunctionType::Location::Log0)),
+ make_shared<MagicVariableDeclaration>("log1",
+ make_shared<FunctionType>(strings{"bytes32", "bytes32"}, strings{}, FunctionType::Location::Log1)),
+ make_shared<MagicVariableDeclaration>("log2",
+ make_shared<FunctionType>(strings{"bytes32", "bytes32", "bytes32"}, strings{}, FunctionType::Location::Log2)),
+ make_shared<MagicVariableDeclaration>("log3",
+ make_shared<FunctionType>(strings{"bytes32", "bytes32", "bytes32", "bytes32"}, strings{}, FunctionType::Location::Log3)),
+ make_shared<MagicVariableDeclaration>("log4",
+ make_shared<FunctionType>(strings{"bytes32", "bytes32", "bytes32", "bytes32", "bytes32"}, strings{}, FunctionType::Location::Log4)),
+ make_shared<MagicVariableDeclaration>("sha256",
+ make_shared<FunctionType>(strings(), strings{"bytes32"}, FunctionType::Location::SHA256, true)),
+ make_shared<MagicVariableDeclaration>("ecrecover",
+ make_shared<FunctionType>(strings{"bytes32", "uint8", "bytes32", "bytes32"}, strings{"address"}, FunctionType::Location::ECRecover)),
+ make_shared<MagicVariableDeclaration>("ripemd160",
+ make_shared<FunctionType>(strings(), strings{"bytes20"}, FunctionType::Location::RIPEMD160, true))})
+{
+}
+
+void GlobalContext::setCurrentContract(ContractDefinition const& _contract)
+{
+ m_currentContract = &_contract;
+}
+
+vector<Declaration const*> GlobalContext::declarations() const
+{
+ vector<Declaration const*> declarations;
+ declarations.reserve(m_magicVariables.size());
+ for (ASTPointer<Declaration const> const& variable: m_magicVariables)
+ declarations.push_back(variable.get());
+ return declarations;
+}
+
+MagicVariableDeclaration const* GlobalContext::currentThis() const
+{
+ if (!m_thisPointer[m_currentContract])
+ m_thisPointer[m_currentContract] = make_shared<MagicVariableDeclaration>(
+ "this", make_shared<ContractType>(*m_currentContract));
+ return m_thisPointer[m_currentContract].get();
+
+}
+
+MagicVariableDeclaration const* GlobalContext::currentSuper() const
+{
+ if (!m_superPointer[m_currentContract])
+ m_superPointer[m_currentContract] = make_shared<MagicVariableDeclaration>(
+ "super", make_shared<ContractType>(*m_currentContract, true));
+ return m_superPointer[m_currentContract].get();
+}
+
+}
+}
diff --git a/libsolidity/analysis/GlobalContext.h b/libsolidity/analysis/GlobalContext.h
new file mode 100644
index 00000000..482391d3
--- /dev/null
+++ b/libsolidity/analysis/GlobalContext.h
@@ -0,0 +1,64 @@
+/*
+ This file is part of cpp-ethereum.
+
+ cpp-ethereum 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.
+
+ cpp-ethereum 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 cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
+*/
+/**
+ * @author Christian <c@ethdev.com>
+ * @date 2014
+ * Container of the (implicit and explicit) global objects.
+ */
+
+#pragma once
+
+#include <string>
+#include <vector>
+#include <map>
+#include <memory>
+#include <boost/noncopyable.hpp>
+#include <libsolidity/ast/ASTForward.h>
+
+namespace dev
+{
+namespace solidity
+{
+
+class Type; // forward
+
+/**
+ * Container for all global objects which look like AST nodes, but are not part of the AST
+ * that is currently being compiled.
+ * @note must not be destroyed or moved during compilation as its objects can be referenced from
+ * other objects.
+ */
+class GlobalContext: private boost::noncopyable
+{
+public:
+ GlobalContext();
+ void setCurrentContract(ContractDefinition const& _contract);
+ MagicVariableDeclaration const* currentThis() const;
+ MagicVariableDeclaration const* currentSuper() const;
+
+ /// @returns a vector of all implicit global declarations excluding "this".
+ std::vector<Declaration const*> declarations() const;
+
+private:
+ std::vector<std::shared_ptr<MagicVariableDeclaration const>> m_magicVariables;
+ ContractDefinition const* m_currentContract = nullptr;
+ std::map<ContractDefinition const*, std::shared_ptr<MagicVariableDeclaration const>> mutable m_thisPointer;
+ std::map<ContractDefinition const*, std::shared_ptr<MagicVariableDeclaration const>> mutable m_superPointer;
+};
+
+}
+}
diff --git a/libsolidity/analysis/NameAndTypeResolver.cpp b/libsolidity/analysis/NameAndTypeResolver.cpp
new file mode 100644
index 00000000..ffd01137
--- /dev/null
+++ b/libsolidity/analysis/NameAndTypeResolver.cpp
@@ -0,0 +1,555 @@
+/*
+ This file is part of cpp-ethereum.
+
+ cpp-ethereum 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.
+
+ cpp-ethereum 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 cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
+*/
+/**
+ * @author Christian <c@ethdev.com>
+ * @date 2014
+ * Parser part that determines the declarations corresponding to names and the types of expressions.
+ */
+
+#include <libsolidity/analysis/NameAndTypeResolver.h>
+#include <libsolidity/ast/AST.h>
+#include <libsolidity/analysis/TypeChecker.h>
+#include <libsolidity/interface/Exceptions.h>
+
+using namespace std;
+
+namespace dev
+{
+namespace solidity
+{
+
+NameAndTypeResolver::NameAndTypeResolver(
+ vector<Declaration const*> const& _globals,
+ ErrorList& _errors
+) :
+ m_errors(_errors)
+{
+ for (Declaration const* declaration: _globals)
+ m_scopes[nullptr].registerDeclaration(*declaration);
+}
+
+bool NameAndTypeResolver::registerDeclarations(SourceUnit& _sourceUnit)
+{
+ // The helper registers all declarations in m_scopes as a side-effect of its construction.
+ try
+ {
+ DeclarationRegistrationHelper registrar(m_scopes, _sourceUnit, m_errors);
+ }
+ catch (FatalError const& _e)
+ {
+ if (m_errors.empty())
+ throw; // Something is weird here, rather throw again.
+ return false;
+ }
+ return true;
+}
+
+bool NameAndTypeResolver::resolveNamesAndTypes(ContractDefinition& _contract)
+{
+ try
+ {
+ m_currentScope = &m_scopes[nullptr];
+
+ for (ASTPointer<InheritanceSpecifier> const& baseContract: _contract.baseContracts())
+ ReferencesResolver resolver(*baseContract, *this, &_contract, nullptr);
+
+ m_currentScope = &m_scopes[&_contract];
+
+ linearizeBaseContracts(_contract);
+ std::vector<ContractDefinition const*> properBases(
+ ++_contract.annotation().linearizedBaseContracts.begin(),
+ _contract.annotation().linearizedBaseContracts.end()
+ );
+
+ for (ContractDefinition const* base: properBases)
+ importInheritedScope(*base);
+
+ for (ASTPointer<StructDefinition> const& structDef: _contract.definedStructs())
+ ReferencesResolver resolver(*structDef, *this, &_contract, nullptr);
+ for (ASTPointer<EnumDefinition> const& enumDef: _contract.definedEnums())
+ ReferencesResolver resolver(*enumDef, *this, &_contract, nullptr);
+ for (ASTPointer<VariableDeclaration> const& variable: _contract.stateVariables())
+ ReferencesResolver resolver(*variable, *this, &_contract, nullptr);
+ for (ASTPointer<EventDefinition> const& event: _contract.events())
+ ReferencesResolver resolver(*event, *this, &_contract, nullptr);
+
+ // these can contain code, only resolve parameters for now
+ for (ASTPointer<ModifierDefinition> const& modifier: _contract.functionModifiers())
+ {
+ m_currentScope = &m_scopes[modifier.get()];
+ ReferencesResolver resolver(*modifier, *this, &_contract, nullptr);
+ }
+ for (ASTPointer<FunctionDefinition> const& function: _contract.definedFunctions())
+ {
+ m_currentScope = &m_scopes[function.get()];
+ ReferencesResolver referencesResolver(
+ *function,
+ *this,
+ &_contract,
+ function->returnParameterList().get()
+ );
+ }
+
+ m_currentScope = &m_scopes[&_contract];
+
+ // now resolve references inside the code
+ for (ASTPointer<ModifierDefinition> const& modifier: _contract.functionModifiers())
+ {
+ m_currentScope = &m_scopes[modifier.get()];
+ ReferencesResolver resolver(*modifier, *this, &_contract, nullptr, true);
+ }
+ for (ASTPointer<FunctionDefinition> const& function: _contract.definedFunctions())
+ {
+ m_currentScope = &m_scopes[function.get()];
+ ReferencesResolver referencesResolver(
+ *function,
+ *this,
+ &_contract,
+ function->returnParameterList().get(),
+ true
+ );
+ }
+ }
+ catch (FatalError const& _e)
+ {
+ if (m_errors.empty())
+ throw; // Something is weird here, rather throw again.
+ return false;
+ }
+ return true;
+}
+
+bool NameAndTypeResolver::updateDeclaration(Declaration const& _declaration)
+{
+ try
+ {
+ m_scopes[nullptr].registerDeclaration(_declaration, false, true);
+ solAssert(_declaration.scope() == nullptr, "Updated declaration outside global scope.");
+ }
+ catch (FatalError const& _error)
+ {
+ if (m_errors.empty())
+ throw; // Something is weird here, rather throw again.
+ return false;
+ }
+ return true;
+}
+
+vector<Declaration const*> NameAndTypeResolver::resolveName(ASTString const& _name, Declaration const* _scope) const
+{
+ auto iterator = m_scopes.find(_scope);
+ if (iterator == end(m_scopes))
+ return vector<Declaration const*>({});
+ return iterator->second.resolveName(_name, false);
+}
+
+vector<Declaration const*> NameAndTypeResolver::nameFromCurrentScope(ASTString const& _name, bool _recursive) const
+{
+ return m_currentScope->resolveName(_name, _recursive);
+}
+
+Declaration const* NameAndTypeResolver::pathFromCurrentScope(vector<ASTString> const& _path, bool _recursive) const
+{
+ solAssert(!_path.empty(), "");
+ vector<Declaration const*> candidates = m_currentScope->resolveName(_path.front(), _recursive);
+ for (size_t i = 1; i < _path.size() && candidates.size() == 1; i++)
+ {
+ if (!m_scopes.count(candidates.front()))
+ return nullptr;
+ candidates = m_scopes.at(candidates.front()).resolveName(_path[i], false);
+ }
+ if (candidates.size() == 1)
+ return candidates.front();
+ else
+ return nullptr;
+}
+
+vector<Declaration const*> NameAndTypeResolver::cleanedDeclarations(
+ Identifier const& _identifier,
+ vector<Declaration const*> const& _declarations
+)
+{
+ solAssert(_declarations.size() > 1, "");
+ vector<Declaration const*> uniqueFunctions;
+
+ for (auto it = _declarations.begin(); it != _declarations.end(); ++it)
+ {
+ solAssert(*it, "");
+ // the declaration is functionDefinition while declarations > 1
+ FunctionDefinition const& functionDefinition = dynamic_cast<FunctionDefinition const&>(**it);
+ FunctionType functionType(functionDefinition);
+ for (auto parameter: functionType.parameterTypes() + functionType.returnParameterTypes())
+ if (!parameter)
+ reportFatalDeclarationError(_identifier.location(), "Function type can not be used in this context");
+
+ if (uniqueFunctions.end() == find_if(
+ uniqueFunctions.begin(),
+ uniqueFunctions.end(),
+ [&](Declaration const* d)
+ {
+ FunctionType newFunctionType(dynamic_cast<FunctionDefinition const&>(*d));
+ return functionType.hasEqualArgumentTypes(newFunctionType);
+ }
+ ))
+ uniqueFunctions.push_back(*it);
+ }
+ return uniqueFunctions;
+}
+
+void NameAndTypeResolver::importInheritedScope(ContractDefinition const& _base)
+{
+ auto iterator = m_scopes.find(&_base);
+ solAssert(iterator != end(m_scopes), "");
+ for (auto const& nameAndDeclaration: iterator->second.declarations())
+ for (auto const& declaration: nameAndDeclaration.second)
+ // Import if it was declared in the base, is not the constructor and is visible in derived classes
+ if (declaration->scope() == &_base && declaration->isVisibleInDerivedContracts())
+ m_currentScope->registerDeclaration(*declaration);
+}
+
+void NameAndTypeResolver::linearizeBaseContracts(ContractDefinition& _contract)
+{
+ // order in the lists is from derived to base
+ // list of lists to linearize, the last element is the list of direct bases
+ list<list<ContractDefinition const*>> input(1, {});
+ for (ASTPointer<InheritanceSpecifier> const& baseSpecifier: _contract.baseContracts())
+ {
+ Identifier const& baseName = baseSpecifier->name();
+ auto base = dynamic_cast<ContractDefinition const*>(baseName.annotation().referencedDeclaration);
+ if (!base)
+ reportFatalTypeError(baseName.createTypeError("Contract expected."));
+ // "push_front" has the effect that bases mentioned later can overwrite members of bases
+ // mentioned earlier
+ input.back().push_front(base);
+ vector<ContractDefinition const*> const& basesBases = base->annotation().linearizedBaseContracts;
+ if (basesBases.empty())
+ reportFatalTypeError(baseName.createTypeError("Definition of base has to precede definition of derived contract"));
+ input.push_front(list<ContractDefinition const*>(basesBases.begin(), basesBases.end()));
+ }
+ input.back().push_front(&_contract);
+ vector<ContractDefinition const*> result = cThreeMerge(input);
+ if (result.empty())
+ reportFatalTypeError(_contract.createTypeError("Linearization of inheritance graph impossible"));
+ _contract.annotation().linearizedBaseContracts = result;
+ _contract.annotation().contractDependencies.insert(result.begin() + 1, result.end());
+}
+
+template <class _T>
+vector<_T const*> NameAndTypeResolver::cThreeMerge(list<list<_T const*>>& _toMerge)
+{
+ // returns true iff _candidate appears only as last element of the lists
+ auto appearsOnlyAtHead = [&](_T const* _candidate) -> bool
+ {
+ for (list<_T const*> const& bases: _toMerge)
+ {
+ solAssert(!bases.empty(), "");
+ if (find(++bases.begin(), bases.end(), _candidate) != bases.end())
+ return false;
+ }
+ return true;
+ };
+ // returns the next candidate to append to the linearized list or nullptr on failure
+ auto nextCandidate = [&]() -> _T const*
+ {
+ for (list<_T const*> const& bases: _toMerge)
+ {
+ solAssert(!bases.empty(), "");
+ if (appearsOnlyAtHead(bases.front()))
+ return bases.front();
+ }
+ return nullptr;
+ };
+ // removes the given contract from all lists
+ auto removeCandidate = [&](_T const* _candidate)
+ {
+ for (auto it = _toMerge.begin(); it != _toMerge.end();)
+ {
+ it->remove(_candidate);
+ if (it->empty())
+ it = _toMerge.erase(it);
+ else
+ ++it;
+ }
+ };
+
+ _toMerge.remove_if([](list<_T const*> const& _bases) { return _bases.empty(); });
+ vector<_T const*> result;
+ while (!_toMerge.empty())
+ {
+ _T const* candidate = nextCandidate();
+ if (!candidate)
+ return vector<_T const*>();
+ result.push_back(candidate);
+ removeCandidate(candidate);
+ }
+ return result;
+}
+
+void NameAndTypeResolver::reportDeclarationError(
+ SourceLocation _sourceLoction,
+ string const& _description,
+ SourceLocation _secondarySourceLocation,
+ string const& _secondaryDescription
+)
+{
+ auto err = make_shared<Error>(Error::Type::DeclarationError); // todo remove Error?
+ *err <<
+ errinfo_sourceLocation(_sourceLoction) <<
+ errinfo_comment(_description) <<
+ errinfo_secondarySourceLocation(
+ SecondarySourceLocation().append(_secondaryDescription, _secondarySourceLocation)
+ );
+
+ m_errors.push_back(err);
+}
+
+void NameAndTypeResolver::reportDeclarationError(SourceLocation _sourceLocation, string const& _description)
+{
+ auto err = make_shared<Error>(Error::Type::DeclarationError); // todo remove Error?
+ *err << errinfo_sourceLocation(_sourceLocation) << errinfo_comment(_description);
+
+ m_errors.push_back(err);
+}
+
+void NameAndTypeResolver::reportFatalDeclarationError(
+ SourceLocation _sourceLocation,
+ string const& _description
+)
+{
+ reportDeclarationError(_sourceLocation, _description);
+ BOOST_THROW_EXCEPTION(FatalError());
+}
+
+void NameAndTypeResolver::reportTypeError(Error const& _e)
+{
+ m_errors.push_back(make_shared<Error>(_e));
+}
+
+void NameAndTypeResolver::reportFatalTypeError(Error const& _e)
+{
+ reportTypeError(_e);
+ BOOST_THROW_EXCEPTION(FatalError());
+}
+
+DeclarationRegistrationHelper::DeclarationRegistrationHelper(
+ map<ASTNode const*, DeclarationContainer>& _scopes,
+ ASTNode& _astRoot,
+ ErrorList& _errors
+):
+ m_scopes(_scopes),
+ m_currentScope(nullptr),
+ m_errors(_errors)
+{
+ _astRoot.accept(*this);
+}
+
+bool DeclarationRegistrationHelper::visit(ContractDefinition& _contract)
+{
+ registerDeclaration(_contract, true);
+ _contract.annotation().canonicalName = currentCanonicalName();
+ return true;
+}
+
+void DeclarationRegistrationHelper::endVisit(ContractDefinition&)
+{
+ closeCurrentScope();
+}
+
+bool DeclarationRegistrationHelper::visit(StructDefinition& _struct)
+{
+ registerDeclaration(_struct, true);
+ _struct.annotation().canonicalName = currentCanonicalName();
+ return true;
+}
+
+void DeclarationRegistrationHelper::endVisit(StructDefinition&)
+{
+ closeCurrentScope();
+}
+
+bool DeclarationRegistrationHelper::visit(EnumDefinition& _enum)
+{
+ registerDeclaration(_enum, true);
+ _enum.annotation().canonicalName = currentCanonicalName();
+ return true;
+}
+
+void DeclarationRegistrationHelper::endVisit(EnumDefinition&)
+{
+ closeCurrentScope();
+}
+
+bool DeclarationRegistrationHelper::visit(EnumValue& _value)
+{
+ registerDeclaration(_value, false);
+ return true;
+}
+
+bool DeclarationRegistrationHelper::visit(FunctionDefinition& _function)
+{
+ registerDeclaration(_function, true);
+ m_currentFunction = &_function;
+ return true;
+}
+
+void DeclarationRegistrationHelper::endVisit(FunctionDefinition&)
+{
+ m_currentFunction = nullptr;
+ closeCurrentScope();
+}
+
+bool DeclarationRegistrationHelper::visit(ModifierDefinition& _modifier)
+{
+ registerDeclaration(_modifier, true);
+ m_currentFunction = &_modifier;
+ return true;
+}
+
+void DeclarationRegistrationHelper::endVisit(ModifierDefinition&)
+{
+ m_currentFunction = nullptr;
+ closeCurrentScope();
+}
+
+void DeclarationRegistrationHelper::endVisit(VariableDeclarationStatement& _variableDeclarationStatement)
+{
+ // Register the local variables with the function
+ // This does not fit here perfectly, but it saves us another AST visit.
+ solAssert(m_currentFunction, "Variable declaration without function.");
+ for (ASTPointer<VariableDeclaration> const& var: _variableDeclarationStatement.declarations())
+ if (var)
+ m_currentFunction->addLocalVariable(*var);
+}
+
+bool DeclarationRegistrationHelper::visit(VariableDeclaration& _declaration)
+{
+ registerDeclaration(_declaration, false);
+ return true;
+}
+
+bool DeclarationRegistrationHelper::visit(EventDefinition& _event)
+{
+ registerDeclaration(_event, true);
+ return true;
+}
+
+void DeclarationRegistrationHelper::endVisit(EventDefinition&)
+{
+ closeCurrentScope();
+}
+
+void DeclarationRegistrationHelper::enterNewSubScope(Declaration const& _declaration)
+{
+ map<ASTNode const*, DeclarationContainer>::iterator iter;
+ bool newlyAdded;
+ tie(iter, newlyAdded) = m_scopes.emplace(&_declaration, DeclarationContainer(m_currentScope, &m_scopes[m_currentScope]));
+ solAssert(newlyAdded, "Unable to add new scope.");
+ m_currentScope = &_declaration;
+}
+
+void DeclarationRegistrationHelper::closeCurrentScope()
+{
+ solAssert(m_currentScope, "Closed non-existing scope.");
+ m_currentScope = m_scopes[m_currentScope].enclosingDeclaration();
+}
+
+void DeclarationRegistrationHelper::registerDeclaration(Declaration& _declaration, bool _opensScope)
+{
+ if (!m_scopes[m_currentScope].registerDeclaration(_declaration, !_declaration.isVisibleInContract()))
+ {
+ SourceLocation firstDeclarationLocation;
+ SourceLocation secondDeclarationLocation;
+ Declaration const* conflictingDeclaration = m_scopes[m_currentScope].conflictingDeclaration(_declaration);
+ solAssert(conflictingDeclaration, "");
+
+ if (_declaration.location().start < conflictingDeclaration->location().start)
+ {
+ firstDeclarationLocation = _declaration.location();
+ secondDeclarationLocation = conflictingDeclaration->location();
+ }
+ else
+ {
+ firstDeclarationLocation = conflictingDeclaration->location();
+ secondDeclarationLocation = _declaration.location();
+ }
+
+ declarationError(
+ secondDeclarationLocation,
+ "Identifier already declared.",
+ firstDeclarationLocation,
+ "The previous declaration is here:"
+ );
+ }
+
+ _declaration.setScope(m_currentScope);
+ if (_opensScope)
+ enterNewSubScope(_declaration);
+}
+
+string DeclarationRegistrationHelper::currentCanonicalName() const
+{
+ string ret;
+ for (
+ Declaration const* scope = m_currentScope;
+ scope != nullptr;
+ scope = m_scopes[scope].enclosingDeclaration()
+ )
+ {
+ if (!ret.empty())
+ ret = "." + ret;
+ ret = scope->name() + ret;
+ }
+ return ret;
+}
+
+void DeclarationRegistrationHelper::declarationError(
+ SourceLocation _sourceLocation,
+ string const& _description,
+ SourceLocation _secondarySourceLocation,
+ string const& _secondaryDescription
+)
+{
+ auto err = make_shared<Error>(Error::Type::DeclarationError);
+ *err <<
+ errinfo_sourceLocation(_sourceLocation) <<
+ errinfo_comment(_description) <<
+ errinfo_secondarySourceLocation(
+ SecondarySourceLocation().append(_secondaryDescription, _secondarySourceLocation)
+ );
+
+ m_errors.push_back(err);
+}
+
+void DeclarationRegistrationHelper::declarationError(SourceLocation _sourceLocation, string const& _description)
+{
+ auto err = make_shared<Error>(Error::Type::DeclarationError);
+ *err << errinfo_sourceLocation(_sourceLocation) << errinfo_comment(_description);
+
+ m_errors.push_back(err);
+}
+
+void DeclarationRegistrationHelper::fatalDeclarationError(
+ SourceLocation _sourceLocation,
+ string const& _description
+)
+{
+ declarationError(_sourceLocation, _description);
+ BOOST_THROW_EXCEPTION(FatalError());
+}
+
+}
+}
diff --git a/libsolidity/analysis/NameAndTypeResolver.h b/libsolidity/analysis/NameAndTypeResolver.h
new file mode 100644
index 00000000..0d9b2477
--- /dev/null
+++ b/libsolidity/analysis/NameAndTypeResolver.h
@@ -0,0 +1,169 @@
+/*
+ This file is part of cpp-ethereum.
+
+ cpp-ethereum 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.
+
+ cpp-ethereum 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 cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
+*/
+/**
+ * @author Christian <c@ethdev.com>
+ * @date 2014
+ * Parser part that determines the declarations corresponding to names and the types of expressions.
+ */
+
+#pragma once
+
+#include <map>
+#include <list>
+#include <boost/noncopyable.hpp>
+#include <libsolidity/analysis/DeclarationContainer.h>
+#include <libsolidity/analysis/ReferencesResolver.h>
+#include <libsolidity/ast/ASTVisitor.h>
+#include <libsolidity/ast/ASTAnnotations.h>
+
+namespace dev
+{
+namespace solidity
+{
+
+/**
+ * Resolves name references, typenames and sets the (explicitly given) types for all variable
+ * declarations.
+ */
+class NameAndTypeResolver: private boost::noncopyable
+{
+public:
+ NameAndTypeResolver(std::vector<Declaration const*> const& _globals, ErrorList& _errors);
+ /// Registers all declarations found in the source unit.
+ /// @returns false in case of error.
+ bool registerDeclarations(SourceUnit& _sourceUnit);
+ /// Resolves all names and types referenced from the given contract.
+ /// @returns false in case of error.
+ bool resolveNamesAndTypes(ContractDefinition& _contract);
+ /// Updates the given global declaration (used for "this"). Not to be used with declarations
+ /// that create their own scope.
+ /// @returns false in case of error.
+ bool updateDeclaration(Declaration const& _declaration);
+
+ /// Resolves the given @a _name inside the scope @a _scope. If @a _scope is omitted,
+ /// the global scope is used (i.e. the one containing only the contract).
+ /// @returns a pointer to the declaration on success or nullptr on failure.
+ std::vector<Declaration const*> resolveName(ASTString const& _name, Declaration const* _scope = nullptr) const;
+
+ /// Resolves a name in the "current" scope. Should only be called during the initial
+ /// resolving phase.
+ std::vector<Declaration const*> nameFromCurrentScope(ASTString const& _name, bool _recursive = true) const;
+
+ /// Resolves a path starting from the "current" scope. Should only be called during the initial
+ /// resolving phase.
+ /// @note Returns a null pointer if any component in the path was not unique or not found.
+ Declaration const* pathFromCurrentScope(std::vector<ASTString> const& _path, bool _recursive = true) const;
+
+ /// returns the vector of declarations without repetitions
+ std::vector<Declaration const*> cleanedDeclarations(
+ Identifier const& _identifier,
+ std::vector<Declaration const*> const& _declarations
+ );
+
+private:
+ void reset();
+
+ /// Imports all members declared directly in the given contract (i.e. does not import inherited members)
+ /// into the current scope if they are not present already.
+ void importInheritedScope(ContractDefinition const& _base);
+
+ /// Computes "C3-Linearization" of base contracts and stores it inside the contract. Reports errors if any
+ void linearizeBaseContracts(ContractDefinition& _contract);
+ /// Computes the C3-merge of the given list of lists of bases.
+ /// @returns the linearized vector or an empty vector if linearization is not possible.
+ template <class _T>
+ static std::vector<_T const*> cThreeMerge(std::list<std::list<_T const*>>& _toMerge);
+
+ /// Maps nodes declaring a scope to scopes, i.e. ContractDefinition and FunctionDeclaration,
+ /// where nullptr denotes the global scope. Note that structs are not scope since they do
+ /// not contain code.
+ std::map<ASTNode const*, DeclarationContainer> m_scopes;
+
+ // creates the Declaration error and adds it in the errors list
+ void reportDeclarationError(
+ SourceLocation _sourceLoction,
+ std::string const& _description,
+ SourceLocation _secondarySourceLocation,
+ std::string const& _secondaryDescription
+ );
+ // creates the Declaration error and adds it in the errors list
+ void reportDeclarationError(SourceLocation _sourceLocation, std::string const& _description);
+ // creates the Declaration error and adds it in the errors list and throws FatalError
+ void reportFatalDeclarationError(SourceLocation _sourceLocation, std::string const& _description);
+
+ // creates the Declaration error and adds it in the errors list
+ void reportTypeError(Error const& _e);
+ // creates the Declaration error and adds it in the errors list and throws FatalError
+ void reportFatalTypeError(Error const& _e);
+
+ DeclarationContainer* m_currentScope = nullptr;
+ ErrorList& m_errors;
+};
+
+/**
+ * Traverses the given AST upon construction and fills _scopes with all declarations inside the
+ * AST.
+ */
+class DeclarationRegistrationHelper: private ASTVisitor
+{
+public:
+ DeclarationRegistrationHelper(std::map<ASTNode const*, DeclarationContainer>& _scopes, ASTNode& _astRoot, ErrorList& _errors);
+
+private:
+ bool visit(ContractDefinition& _contract) override;
+ void endVisit(ContractDefinition& _contract) override;
+ bool visit(StructDefinition& _struct) override;
+ void endVisit(StructDefinition& _struct) override;
+ bool visit(EnumDefinition& _enum) override;
+ void endVisit(EnumDefinition& _enum) override;
+ bool visit(EnumValue& _value) override;
+ bool visit(FunctionDefinition& _function) override;
+ void endVisit(FunctionDefinition& _function) override;
+ bool visit(ModifierDefinition& _modifier) override;
+ void endVisit(ModifierDefinition& _modifier) override;
+ void endVisit(VariableDeclarationStatement& _variableDeclarationStatement) override;
+ bool visit(VariableDeclaration& _declaration) override;
+ bool visit(EventDefinition& _event) override;
+ void endVisit(EventDefinition& _event) override;
+
+ void enterNewSubScope(Declaration const& _declaration);
+ void closeCurrentScope();
+ void registerDeclaration(Declaration& _declaration, bool _opensScope);
+
+ /// @returns the canonical name of the current scope.
+ std::string currentCanonicalName() const;
+ // creates the Declaration error and adds it in the errors list
+ void declarationError(
+ SourceLocation _sourceLocation,
+ std::string const& _description,
+ SourceLocation _secondarySourceLocation,
+ std::string const& _secondaryDescription
+ );
+
+ // creates the Declaration error and adds it in the errors list
+ void declarationError(SourceLocation _sourceLocation, std::string const& _description);
+ // creates the Declaration error and adds it in the errors list and throws FatalError
+ void fatalDeclarationError(SourceLocation _sourceLocation, std::string const& _description);
+
+ std::map<ASTNode const*, DeclarationContainer>& m_scopes;
+ Declaration const* m_currentScope;
+ VariableScope* m_currentFunction;
+ ErrorList& m_errors;
+};
+
+}
+}
diff --git a/libsolidity/analysis/ReferencesResolver.cpp b/libsolidity/analysis/ReferencesResolver.cpp
new file mode 100644
index 00000000..fb7cdb3e
--- /dev/null
+++ b/libsolidity/analysis/ReferencesResolver.cpp
@@ -0,0 +1,234 @@
+/*
+ This file is part of cpp-ethereum.
+
+ cpp-ethereum 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.
+
+ cpp-ethereum 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 cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
+*/
+/**
+ * @author Christian <c@ethdev.com>
+ * @date 2015
+ * Component that resolves type names to types and annotates the AST accordingly.
+ */
+
+#include <libsolidity/analysis/ReferencesResolver.h>
+#include <libsolidity/ast/AST.h>
+#include <libsolidity/analysis/NameAndTypeResolver.h>
+#include <libsolidity/interface/Exceptions.h>
+#include <libsolidity/analysis/ConstantEvaluator.h>
+
+using namespace std;
+using namespace dev;
+using namespace dev::solidity;
+
+
+ReferencesResolver::ReferencesResolver(
+ ASTNode& _root,
+ NameAndTypeResolver& _resolver,
+ ContractDefinition const* _currentContract,
+ ParameterList const* _returnParameters,
+ bool _resolveInsideCode
+):
+ m_resolver(_resolver),
+ m_currentContract(_currentContract),
+ m_returnParameters(_returnParameters),
+ m_resolveInsideCode(_resolveInsideCode)
+{
+ _root.accept(*this);
+}
+
+bool ReferencesResolver::visit(Return const& _return)
+{
+ _return.annotation().functionReturnParameters = m_returnParameters;
+ return true;
+}
+
+bool ReferencesResolver::visit(UserDefinedTypeName const& _typeName)
+{
+ Declaration const* declaration = m_resolver.pathFromCurrentScope(_typeName.namePath());
+ if (!declaration)
+ BOOST_THROW_EXCEPTION(
+ Error(Error::Type::DeclarationError) <<
+ errinfo_sourceLocation(_typeName.location()) <<
+ errinfo_comment("Identifier not found or not unique.")
+ );
+ _typeName.annotation().referencedDeclaration = declaration;
+ return true;
+}
+
+bool ReferencesResolver::visit(Identifier const& _identifier)
+{
+ auto declarations = m_resolver.nameFromCurrentScope(_identifier.name());
+ if (declarations.empty())
+ BOOST_THROW_EXCEPTION(
+ Error(Error::Type::DeclarationError) <<
+ errinfo_sourceLocation(_identifier.location()) <<
+ errinfo_comment("Undeclared identifier.")
+ );
+ else if (declarations.size() == 1)
+ {
+ _identifier.annotation().referencedDeclaration = declarations.front();
+ _identifier.annotation().contractScope = m_currentContract;
+ }
+ else
+ _identifier.annotation().overloadedDeclarations =
+ m_resolver.cleanedDeclarations(_identifier, declarations);
+ return false;
+}
+
+void ReferencesResolver::endVisit(VariableDeclaration const& _variable)
+{
+ if (_variable.annotation().type)
+ return;
+
+ TypePointer type;
+ if (_variable.typeName())
+ {
+ type = typeFor(*_variable.typeName());
+ using Location = VariableDeclaration::Location;
+ Location loc = _variable.referenceLocation();
+ // References are forced to calldata for external function parameters (not return)
+ // and memory for parameters (also return) of publicly visible functions.
+ // They default to memory for function parameters and storage for local variables.
+ // As an exception, "storage" is allowed for library functions.
+ if (auto ref = dynamic_cast<ReferenceType const*>(type.get()))
+ {
+ if (_variable.isExternalCallableParameter())
+ {
+ auto const& contract = dynamic_cast<ContractDefinition const&>(*_variable.scope()->scope());
+ if (contract.isLibrary())
+ {
+ if (loc == Location::Memory)
+ BOOST_THROW_EXCEPTION(_variable.createTypeError(
+ "Location has to be calldata or storage for external "
+ "library functions (remove the \"memory\" keyword)."
+ ));
+ }
+ else
+ {
+ // force location of external function parameters (not return) to calldata
+ if (loc != Location::Default)
+ BOOST_THROW_EXCEPTION(_variable.createTypeError(
+ "Location has to be calldata for external functions "
+ "(remove the \"memory\" or \"storage\" keyword)."
+ ));
+ }
+ if (loc == Location::Default)
+ type = ref->copyForLocation(DataLocation::CallData, true);
+ }
+ else if (_variable.isCallableParameter() && _variable.scope()->isPublic())
+ {
+ auto const& contract = dynamic_cast<ContractDefinition const&>(*_variable.scope()->scope());
+ // force locations of public or external function (return) parameters to memory
+ if (loc == Location::Storage && !contract.isLibrary())
+ BOOST_THROW_EXCEPTION(_variable.createTypeError(
+ "Location has to be memory for publicly visible functions "
+ "(remove the \"storage\" keyword)."
+ ));
+ if (loc == Location::Default || !contract.isLibrary())
+ type = ref->copyForLocation(DataLocation::Memory, true);
+ }
+ else
+ {
+ if (_variable.isConstant())
+ {
+ if (loc != Location::Default && loc != Location::Memory)
+ BOOST_THROW_EXCEPTION(_variable.createTypeError(
+ "Storage location has to be \"memory\" (or unspecified) for constants."
+ ));
+ loc = Location::Memory;
+ }
+ if (loc == Location::Default)
+ loc = _variable.isCallableParameter() ? Location::Memory : Location::Storage;
+ bool isPointer = !_variable.isStateVariable();
+ type = ref->copyForLocation(
+ loc == Location::Memory ?
+ DataLocation::Memory :
+ DataLocation::Storage,
+ isPointer
+ );
+ }
+ }
+ else if (loc != Location::Default && !ref)
+ BOOST_THROW_EXCEPTION(_variable.createTypeError(
+ "Storage location can only be given for array or struct types."
+ ));
+
+ if (!type)
+ BOOST_THROW_EXCEPTION(_variable.typeName()->createTypeError("Invalid type name."));
+
+ }
+ else if (!_variable.canHaveAutoType())
+ BOOST_THROW_EXCEPTION(_variable.createTypeError("Explicit type needed."));
+ // otherwise we have a "var"-declaration whose type is resolved by the first assignment
+
+ _variable.annotation().type = type;
+}
+
+TypePointer ReferencesResolver::typeFor(TypeName const& _typeName)
+{
+ if (_typeName.annotation().type)
+ return _typeName.annotation().type;
+
+ TypePointer type;
+ if (auto elemTypeName = dynamic_cast<ElementaryTypeName const*>(&_typeName))
+ type = Type::fromElementaryTypeName(elemTypeName->typeName());
+ else if (auto typeName = dynamic_cast<UserDefinedTypeName const*>(&_typeName))
+ {
+ Declaration const* declaration = typeName->annotation().referencedDeclaration;
+ solAssert(!!declaration, "");
+
+ if (StructDefinition const* structDef = dynamic_cast<StructDefinition const*>(declaration))
+ type = make_shared<StructType>(*structDef);
+ else if (EnumDefinition const* enumDef = dynamic_cast<EnumDefinition const*>(declaration))
+ type = make_shared<EnumType>(*enumDef);
+ else if (ContractDefinition const* contract = dynamic_cast<ContractDefinition const*>(declaration))
+ type = make_shared<ContractType>(*contract);
+ else
+ BOOST_THROW_EXCEPTION(typeName->createTypeError(
+ "Name has to refer to a struct, enum or contract."
+ ));
+ }
+ else if (auto mapping = dynamic_cast<Mapping const*>(&_typeName))
+ {
+ TypePointer keyType = typeFor(mapping->keyType());
+ TypePointer valueType = typeFor(mapping->valueType());
+ // Convert key type to memory.
+ keyType = ReferenceType::copyForLocationIfReference(DataLocation::Memory, keyType);
+ // Convert value type to storage reference.
+ valueType = ReferenceType::copyForLocationIfReference(DataLocation::Storage, valueType);
+ type = make_shared<MappingType>(keyType, valueType);
+ }
+ else if (auto arrayType = dynamic_cast<ArrayTypeName const*>(&_typeName))
+ {
+ TypePointer baseType = typeFor(arrayType->baseType());
+ if (baseType->storageBytes() == 0)
+ BOOST_THROW_EXCEPTION(arrayType->baseType().createTypeError(
+ "Illegal base type of storage size zero for array."
+ ));
+ if (Expression const* length = arrayType->length())
+ {
+ if (!length->annotation().type)
+ ConstantEvaluator e(*length);
+
+ auto const* lengthType = dynamic_cast<IntegerConstantType const*>(length->annotation().type.get());
+ if (!lengthType)
+ BOOST_THROW_EXCEPTION(length->createTypeError("Invalid array length."));
+ type = make_shared<ArrayType>(DataLocation::Storage, baseType, lengthType->literalValue(nullptr));
+ }
+ else
+ type = make_shared<ArrayType>(DataLocation::Storage, baseType);
+ }
+
+ return _typeName.annotation().type = move(type);
+}
+
diff --git a/libsolidity/analysis/ReferencesResolver.h b/libsolidity/analysis/ReferencesResolver.h
new file mode 100644
index 00000000..4276adaa
--- /dev/null
+++ b/libsolidity/analysis/ReferencesResolver.h
@@ -0,0 +1,69 @@
+/*
+ This file is part of cpp-ethereum.
+
+ cpp-ethereum 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.
+
+ cpp-ethereum 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 cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
+*/
+/**
+ * @author Christian <c@ethdev.com>
+ * @date 2014
+ * Component that resolves type names to types and annotates the AST accordingly.
+ */
+
+#pragma once
+
+#include <map>
+#include <list>
+#include <boost/noncopyable.hpp>
+#include <libsolidity/ast/ASTVisitor.h>
+#include <libsolidity/ast/ASTAnnotations.h>
+
+namespace dev
+{
+namespace solidity
+{
+
+class NameAndTypeResolver;
+
+/**
+ * Resolves references to declarations (of variables and types) and also establishes the link
+ * between a return statement and the return parameter list.
+ */
+class ReferencesResolver: private ASTConstVisitor
+{
+public:
+ ReferencesResolver(
+ ASTNode& _root,
+ NameAndTypeResolver& _resolver,
+ ContractDefinition const* _currentContract,
+ ParameterList const* _returnParameters,
+ bool _resolveInsideCode = false
+ );
+
+private:
+ virtual bool visit(Block const&) override { return m_resolveInsideCode; }
+ virtual bool visit(Identifier const& _identifier) override;
+ virtual bool visit(UserDefinedTypeName const& _typeName) override;
+ virtual bool visit(Return const& _return) override;
+ virtual void endVisit(VariableDeclaration const& _variable) override;
+
+ TypePointer typeFor(TypeName const& _typeName);
+
+ NameAndTypeResolver& m_resolver;
+ ContractDefinition const* m_currentContract;
+ ParameterList const* m_returnParameters;
+ bool m_resolveInsideCode;
+};
+
+}
+}
diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp
new file mode 100644
index 00000000..6b12c57f
--- /dev/null
+++ b/libsolidity/analysis/TypeChecker.cpp
@@ -0,0 +1,1328 @@
+/*
+ This file is part of cpp-ethereum.
+
+ cpp-ethereum 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.
+
+ cpp-ethereum 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 cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
+*/
+/**
+ * @author Christian <c@ethdev.com>
+ * @date 2015
+ * Type analyzer and checker.
+ */
+
+#include <libsolidity/analysis/TypeChecker.h>
+#include <memory>
+#include <boost/range/adaptor/reversed.hpp>
+#include <libsolidity/ast/AST.h>
+
+using namespace std;
+using namespace dev;
+using namespace dev::solidity;
+
+
+bool TypeChecker::checkTypeRequirements(const ContractDefinition& _contract)
+{
+ try
+ {
+ visit(_contract);
+ }
+ catch (FatalError const&)
+ {
+ // We got a fatal error which required to stop further type checking, but we can
+ // continue normally from here.
+ if (m_errors.empty())
+ throw; // Something is weird here, rather throw again.
+ }
+ return Error::containsOnlyWarnings(m_errors);
+}
+
+TypePointer const& TypeChecker::type(Expression const& _expression) const
+{
+ solAssert(!!_expression.annotation().type, "Type requested but not present.");
+ return _expression.annotation().type;
+}
+
+TypePointer const& TypeChecker::type(VariableDeclaration const& _variable) const
+{
+ solAssert(!!_variable.annotation().type, "Type requested but not present.");
+ return _variable.annotation().type;
+}
+
+bool TypeChecker::visit(ContractDefinition const& _contract)
+{
+ // We force our own visiting order here.
+ ASTNode::listAccept(_contract.definedStructs(), *this);
+ ASTNode::listAccept(_contract.baseContracts(), *this);
+
+ checkContractDuplicateFunctions(_contract);
+ checkContractIllegalOverrides(_contract);
+ checkContractAbstractFunctions(_contract);
+ checkContractAbstractConstructors(_contract);
+
+ FunctionDefinition const* function = _contract.constructor();
+ if (function && !function->returnParameters().empty())
+ typeError(*function->returnParameterList(), "Non-empty \"returns\" directive for constructor.");
+
+ FunctionDefinition const* fallbackFunction = nullptr;
+ for (ASTPointer<FunctionDefinition> const& function: _contract.definedFunctions())
+ {
+ if (function->name().empty())
+ {
+ if (fallbackFunction)
+ {
+ auto err = make_shared<Error>(Error::Type::DeclarationError);
+ *err << errinfo_comment("Only one fallback function is allowed.");
+ m_errors.push_back(err);
+ }
+ else
+ {
+ fallbackFunction = function.get();
+ if (!fallbackFunction->parameters().empty())
+ typeError(fallbackFunction->parameterList(), "Fallback function cannot take parameters.");
+ }
+ }
+ if (!function->isImplemented())
+ _contract.annotation().isFullyImplemented = false;
+ }
+
+ ASTNode::listAccept(_contract.stateVariables(), *this);
+ ASTNode::listAccept(_contract.events(), *this);
+ ASTNode::listAccept(_contract.functionModifiers(), *this);
+ ASTNode::listAccept(_contract.definedFunctions(), *this);
+
+ checkContractExternalTypeClashes(_contract);
+ // check for hash collisions in function signatures
+ set<FixedHash<4>> hashes;
+ for (auto const& it: _contract.interfaceFunctionList())
+ {
+ FixedHash<4> const& hash = it.first;
+ if (hashes.count(hash))
+ typeError(
+ _contract,
+ string("Function signature hash collision for ") + it.second->externalSignature()
+ );
+ hashes.insert(hash);
+ }
+
+ if (_contract.isLibrary())
+ checkLibraryRequirements(_contract);
+
+ return false;
+}
+
+void TypeChecker::checkContractDuplicateFunctions(ContractDefinition const& _contract)
+{
+ /// Checks that two functions with the same name defined in this contract have different
+ /// argument types and that there is at most one constructor.
+ map<string, vector<FunctionDefinition const*>> functions;
+ for (ASTPointer<FunctionDefinition> const& function: _contract.definedFunctions())
+ functions[function->name()].push_back(function.get());
+
+ // Constructor
+ if (functions[_contract.name()].size() > 1)
+ {
+ SecondarySourceLocation ssl;
+ auto it = ++functions[_contract.name()].begin();
+ for (; it != functions[_contract.name()].end(); ++it)
+ ssl.append("Another declaration is here:", (*it)->location());
+
+ auto err = make_shared<Error>(Error(Error::Type::DeclarationError));
+ *err <<
+ errinfo_sourceLocation(functions[_contract.name()].front()->location()) <<
+ errinfo_comment("More than one constructor defined.") <<
+ errinfo_secondarySourceLocation(ssl);
+ m_errors.push_back(err);
+ }
+ for (auto const& it: functions)
+ {
+ vector<FunctionDefinition const*> const& overloads = it.second;
+ for (size_t i = 0; i < overloads.size(); ++i)
+ for (size_t j = i + 1; j < overloads.size(); ++j)
+ if (FunctionType(*overloads[i]).hasEqualArgumentTypes(FunctionType(*overloads[j])))
+ {
+ auto err = make_shared<Error>(Error(Error::Type::DeclarationError));
+ *err <<
+ errinfo_sourceLocation(overloads[j]->location()) <<
+ errinfo_comment("Function with same name and arguments defined twice.") <<
+ errinfo_secondarySourceLocation(SecondarySourceLocation().append(
+ "Other declaration is here:", overloads[i]->location()));
+ m_errors.push_back(err);
+ }
+ }
+}
+
+void TypeChecker::checkContractAbstractFunctions(ContractDefinition const& _contract)
+{
+ // Mapping from name to function definition (exactly one per argument type equality class) and
+ // flag to indicate whether it is fully implemented.
+ using FunTypeAndFlag = std::pair<FunctionTypePointer, bool>;
+ map<string, vector<FunTypeAndFlag>> functions;
+
+ // Search from base to derived
+ for (ContractDefinition const* contract: boost::adaptors::reverse(_contract.annotation().linearizedBaseContracts))
+ for (ASTPointer<FunctionDefinition> const& function: contract->definedFunctions())
+ {
+ auto& overloads = functions[function->name()];
+ FunctionTypePointer funType = make_shared<FunctionType>(*function);
+ auto it = find_if(overloads.begin(), overloads.end(), [&](FunTypeAndFlag const& _funAndFlag)
+ {
+ return funType->hasEqualArgumentTypes(*_funAndFlag.first);
+ });
+ if (it == overloads.end())
+ overloads.push_back(make_pair(funType, function->isImplemented()));
+ else if (it->second)
+ {
+ if (!function->isImplemented())
+ typeError(*function, "Redeclaring an already implemented function as abstract");
+ }
+ else if (function->isImplemented())
+ it->second = true;
+ }
+
+ // Set to not fully implemented if at least one flag is false.
+ for (auto const& it: functions)
+ for (auto const& funAndFlag: it.second)
+ if (!funAndFlag.second)
+ {
+ _contract.annotation().isFullyImplemented = false;
+ return;
+ }
+}
+
+void TypeChecker::checkContractAbstractConstructors(ContractDefinition const& _contract)
+{
+ set<ContractDefinition const*> argumentsNeeded;
+ // check that we get arguments for all base constructors that need it.
+ // If not mark the contract as abstract (not fully implemented)
+
+ vector<ContractDefinition const*> const& bases = _contract.annotation().linearizedBaseContracts;
+ for (ContractDefinition const* contract: bases)
+ if (FunctionDefinition const* constructor = contract->constructor())
+ if (contract != &_contract && !constructor->parameters().empty())
+ argumentsNeeded.insert(contract);
+
+ for (ContractDefinition const* contract: bases)
+ {
+ if (FunctionDefinition const* constructor = contract->constructor())
+ for (auto const& modifier: constructor->modifiers())
+ {
+ auto baseContract = dynamic_cast<ContractDefinition const*>(
+ &dereference(*modifier->name())
+ );
+ if (baseContract)
+ argumentsNeeded.erase(baseContract);
+ }
+
+
+ for (ASTPointer<InheritanceSpecifier> const& base: contract->baseContracts())
+ {
+ auto baseContract = dynamic_cast<ContractDefinition const*>(&dereference(base->name()));
+ solAssert(baseContract, "");
+ if (!base->arguments().empty())
+ argumentsNeeded.erase(baseContract);
+ }
+ }
+ if (!argumentsNeeded.empty())
+ _contract.annotation().isFullyImplemented = false;
+}
+
+void TypeChecker::checkContractIllegalOverrides(ContractDefinition const& _contract)
+{
+ // TODO unify this at a later point. for this we need to put the constness and the access specifier
+ // into the types
+ map<string, vector<FunctionDefinition const*>> functions;
+ map<string, ModifierDefinition const*> modifiers;
+
+ // We search from derived to base, so the stored item causes the error.
+ for (ContractDefinition const* contract: _contract.annotation().linearizedBaseContracts)
+ {
+ for (ASTPointer<FunctionDefinition> const& function: contract->definedFunctions())
+ {
+ if (function->isConstructor())
+ continue; // constructors can neither be overridden nor override anything
+ string const& name = function->name();
+ if (modifiers.count(name))
+ typeError(*modifiers[name], "Override changes function to modifier.");
+ FunctionType functionType(*function);
+ // function should not change the return type
+ for (FunctionDefinition const* overriding: functions[name])
+ {
+ FunctionType overridingType(*overriding);
+ if (!overridingType.hasEqualArgumentTypes(functionType))
+ continue;
+ if (
+ overriding->visibility() != function->visibility() ||
+ overriding->isDeclaredConst() != function->isDeclaredConst() ||
+ overridingType != functionType
+ )
+ typeError(*overriding, "Override changes extended function signature.");
+ }
+ functions[name].push_back(function.get());
+ }
+ for (ASTPointer<ModifierDefinition> const& modifier: contract->functionModifiers())
+ {
+ string const& name = modifier->name();
+ ModifierDefinition const*& override = modifiers[name];
+ if (!override)
+ override = modifier.get();
+ else if (ModifierType(*override) != ModifierType(*modifier))
+ typeError(*override, "Override changes modifier signature.");
+ if (!functions[name].empty())
+ typeError(*override, "Override changes modifier to function.");
+ }
+ }
+}
+
+void TypeChecker::checkContractExternalTypeClashes(ContractDefinition const& _contract)
+{
+ map<string, vector<pair<Declaration const*, FunctionTypePointer>>> externalDeclarations;
+ for (ContractDefinition const* contract: _contract.annotation().linearizedBaseContracts)
+ {
+ for (ASTPointer<FunctionDefinition> const& f: contract->definedFunctions())
+ if (f->isPartOfExternalInterface())
+ {
+ auto functionType = make_shared<FunctionType>(*f);
+ externalDeclarations[functionType->externalSignature()].push_back(
+ make_pair(f.get(), functionType)
+ );
+ }
+ for (ASTPointer<VariableDeclaration> const& v: contract->stateVariables())
+ if (v->isPartOfExternalInterface())
+ {
+ auto functionType = make_shared<FunctionType>(*v);
+ externalDeclarations[functionType->externalSignature()].push_back(
+ make_pair(v.get(), functionType)
+ );
+ }
+ }
+ for (auto const& it: externalDeclarations)
+ for (size_t i = 0; i < it.second.size(); ++i)
+ for (size_t j = i + 1; j < it.second.size(); ++j)
+ if (!it.second[i].second->hasEqualArgumentTypes(*it.second[j].second))
+ typeError(
+ *it.second[j].first,
+ "Function overload clash during conversion to external types for arguments."
+ );
+}
+
+void TypeChecker::checkLibraryRequirements(ContractDefinition const& _contract)
+{
+ solAssert(_contract.isLibrary(), "");
+ if (!_contract.baseContracts().empty())
+ typeError(_contract, "Library is not allowed to inherit.");
+
+ for (auto const& var: _contract.stateVariables())
+ if (!var->isConstant())
+ typeError(*var, "Library cannot have non-constant state variables");
+}
+
+void TypeChecker::endVisit(InheritanceSpecifier const& _inheritance)
+{
+ auto base = dynamic_cast<ContractDefinition const*>(&dereference(_inheritance.name()));
+ solAssert(base, "Base contract not available.");
+
+ if (base->isLibrary())
+ typeError(_inheritance, "Libraries cannot be inherited from.");
+
+ auto const& arguments = _inheritance.arguments();
+ TypePointers parameterTypes = ContractType(*base).constructorType()->parameterTypes();
+ if (!arguments.empty() && parameterTypes.size() != arguments.size())
+ typeError(
+ _inheritance,
+ "Wrong argument count for constructor call: " +
+ toString(arguments.size()) +
+ " arguments given but expected " +
+ toString(parameterTypes.size()) +
+ "."
+ );
+
+ for (size_t i = 0; i < arguments.size(); ++i)
+ if (!type(*arguments[i])->isImplicitlyConvertibleTo(*parameterTypes[i]))
+ typeError(
+ *arguments[i],
+ "Invalid type for argument in constructor call. "
+ "Invalid implicit conversion from " +
+ type(*arguments[i])->toString() +
+ " to " +
+ parameterTypes[i]->toString() +
+ " requested."
+ );
+}
+
+bool TypeChecker::visit(StructDefinition const& _struct)
+{
+ for (ASTPointer<VariableDeclaration> const& member: _struct.members())
+ if (!type(*member)->canBeStored())
+ typeError(*member, "Type cannot be used in struct.");
+
+ // Check recursion, fatal error if detected.
+ using StructPointer = StructDefinition const*;
+ using StructPointersSet = set<StructPointer>;
+ function<void(StructPointer,StructPointersSet const&)> check = [&](StructPointer _struct, StructPointersSet const& _parents)
+ {
+ if (_parents.count(_struct))
+ fatalTypeError(*_struct, "Recursive struct definition.");
+ StructPointersSet parents = _parents;
+ parents.insert(_struct);
+ for (ASTPointer<VariableDeclaration> const& member: _struct->members())
+ if (type(*member)->category() == Type::Category::Struct)
+ {
+ auto const& typeName = dynamic_cast<UserDefinedTypeName const&>(*member->typeName());
+ check(&dynamic_cast<StructDefinition const&>(*typeName.annotation().referencedDeclaration), parents);
+ }
+ };
+ check(&_struct, StructPointersSet{});
+
+ ASTNode::listAccept(_struct.members(), *this);
+
+ return false;
+}
+
+bool TypeChecker::visit(FunctionDefinition const& _function)
+{
+ bool isLibraryFunction = dynamic_cast<ContractDefinition const&>(*_function.scope()).isLibrary();
+ for (ASTPointer<VariableDeclaration> const& var: _function.parameters() + _function.returnParameters())
+ {
+ if (!type(*var)->canLiveOutsideStorage())
+ typeError(*var, "Type is required to live outside storage.");
+ if (_function.visibility() >= FunctionDefinition::Visibility::Public && !(type(*var)->interfaceType(isLibraryFunction)))
+ fatalTypeError(*var, "Internal type is not allowed for public or external functions.");
+ }
+ for (ASTPointer<ModifierInvocation> const& modifier: _function.modifiers())
+ visitManually(
+ *modifier,
+ _function.isConstructor() ?
+ dynamic_cast<ContractDefinition const&>(*_function.scope()).annotation().linearizedBaseContracts :
+ vector<ContractDefinition const*>()
+ );
+ if (_function.isImplemented())
+ _function.body().accept(*this);
+ return false;
+}
+
+bool TypeChecker::visit(VariableDeclaration const& _variable)
+{
+ // Variables can be declared without type (with "var"), in which case the first assignment
+ // sets the type.
+ // Note that assignments before the first declaration are legal because of the special scoping
+ // rules inherited from JavaScript.
+
+ // type is filled either by ReferencesResolver directly from the type name or by
+ // TypeChecker at the VariableDeclarationStatement level.
+ TypePointer varType = _variable.annotation().type;
+ solAssert(!!varType, "Failed to infer variable type.");
+ if (_variable.isConstant())
+ {
+ if (!dynamic_cast<ContractDefinition const*>(_variable.scope()))
+ typeError(_variable, "Illegal use of \"constant\" specifier.");
+ if (!_variable.value())
+ typeError(_variable, "Uninitialized \"constant\" variable.");
+ if (!varType->isValueType())
+ {
+ bool constImplemented = false;
+ if (auto arrayType = dynamic_cast<ArrayType const*>(varType.get()))
+ constImplemented = arrayType->isByteArray();
+ if (!constImplemented)
+ typeError(
+ _variable,
+ "Illegal use of \"constant\" specifier. \"constant\" "
+ "is not yet implemented for this type."
+ );
+ }
+ }
+ if (_variable.value())
+ expectType(*_variable.value(), *varType);
+ if (!_variable.isStateVariable())
+ {
+ if (varType->dataStoredIn(DataLocation::Memory) || varType->dataStoredIn(DataLocation::CallData))
+ if (!varType->canLiveOutsideStorage())
+ typeError(_variable, "Type " + varType->toString() + " is only valid in storage.");
+ }
+ else if (
+ _variable.visibility() >= VariableDeclaration::Visibility::Public &&
+ !FunctionType(_variable).interfaceFunctionType()
+ )
+ typeError(_variable, "Internal type is not allowed for public state variables.");
+ return false;
+}
+
+void TypeChecker::visitManually(
+ ModifierInvocation const& _modifier,
+ vector<ContractDefinition const*> const& _bases
+)
+{
+ std::vector<ASTPointer<Expression>> const& arguments = _modifier.arguments();
+ for (ASTPointer<Expression> const& argument: arguments)
+ argument->accept(*this);
+ _modifier.name()->accept(*this);
+
+ auto const* declaration = &dereference(*_modifier.name());
+ vector<ASTPointer<VariableDeclaration>> emptyParameterList;
+ vector<ASTPointer<VariableDeclaration>> const* parameters = nullptr;
+ if (auto modifierDecl = dynamic_cast<ModifierDefinition const*>(declaration))
+ parameters = &modifierDecl->parameters();
+ else
+ // check parameters for Base constructors
+ for (ContractDefinition const* base: _bases)
+ if (declaration == base)
+ {
+ if (auto referencedConstructor = base->constructor())
+ parameters = &referencedConstructor->parameters();
+ else
+ parameters = &emptyParameterList;
+ break;
+ }
+ if (!parameters)
+ typeError(_modifier, "Referenced declaration is neither modifier nor base class.");
+ if (parameters->size() != arguments.size())
+ typeError(
+ _modifier,
+ "Wrong argument count for modifier invocation: " +
+ toString(arguments.size()) +
+ " arguments given but expected " +
+ toString(parameters->size()) +
+ "."
+ );
+ for (size_t i = 0; i < _modifier.arguments().size(); ++i)
+ if (!type(*arguments[i])->isImplicitlyConvertibleTo(*type(*(*parameters)[i])))
+ typeError(
+ *arguments[i],
+ "Invalid type for argument in modifier invocation. "
+ "Invalid implicit conversion from " +
+ type(*arguments[i])->toString() +
+ " to " +
+ type(*(*parameters)[i])->toString() +
+ " requested."
+ );
+}
+
+bool TypeChecker::visit(EventDefinition const& _eventDef)
+{
+ unsigned numIndexed = 0;
+ for (ASTPointer<VariableDeclaration> const& var: _eventDef.parameters())
+ {
+ if (var->isIndexed())
+ numIndexed++;
+ if (_eventDef.isAnonymous() && numIndexed > 4)
+ typeError(_eventDef, "More than 4 indexed arguments for anonymous event.");
+ else if (!_eventDef.isAnonymous() && numIndexed > 3)
+ typeError(_eventDef, "More than 3 indexed arguments for event.");
+ if (!type(*var)->canLiveOutsideStorage())
+ typeError(*var, "Type is required to live outside storage.");
+ if (!type(*var)->interfaceType(false))
+ typeError(*var, "Internal type is not allowed as event parameter type.");
+ }
+ return false;
+}
+
+
+bool TypeChecker::visit(IfStatement const& _ifStatement)
+{
+ expectType(_ifStatement.condition(), BoolType());
+ _ifStatement.trueStatement().accept(*this);
+ if (_ifStatement.falseStatement())
+ _ifStatement.falseStatement()->accept(*this);
+ return false;
+}
+
+bool TypeChecker::visit(WhileStatement const& _whileStatement)
+{
+ expectType(_whileStatement.condition(), BoolType());
+ _whileStatement.body().accept(*this);
+ return false;
+}
+
+bool TypeChecker::visit(ForStatement const& _forStatement)
+{
+ if (_forStatement.initializationExpression())
+ _forStatement.initializationExpression()->accept(*this);
+ if (_forStatement.condition())
+ expectType(*_forStatement.condition(), BoolType());
+ if (_forStatement.loopExpression())
+ _forStatement.loopExpression()->accept(*this);
+ _forStatement.body().accept(*this);
+ return false;
+}
+
+void TypeChecker::endVisit(Return const& _return)
+{
+ if (!_return.expression())
+ return;
+ ParameterList const* params = _return.annotation().functionReturnParameters;
+ if (!params)
+ {
+ typeError(_return, "Return arguments not allowed.");
+ return;
+ }
+ TypePointers returnTypes;
+ for (auto const& var: params->parameters())
+ returnTypes.push_back(type(*var));
+ if (auto tupleType = dynamic_cast<TupleType const*>(type(*_return.expression()).get()))
+ {
+ if (tupleType->components().size() != params->parameters().size())
+ typeError(_return, "Different number of arguments in return statement than in returns declaration.");
+ else if (!tupleType->isImplicitlyConvertibleTo(TupleType(returnTypes)))
+ typeError(
+ *_return.expression(),
+ "Return argument type " +
+ type(*_return.expression())->toString() +
+ " is not implicitly convertible to expected type " +
+ TupleType(returnTypes).toString(false) +
+ "."
+ );
+ }
+ else if (params->parameters().size() != 1)
+ typeError(_return, "Different number of arguments in return statement than in returns declaration.");
+ else
+ {
+ TypePointer const& expected = type(*params->parameters().front());
+ if (!type(*_return.expression())->isImplicitlyConvertibleTo(*expected))
+ typeError(
+ *_return.expression(),
+ "Return argument type " +
+ type(*_return.expression())->toString() +
+ " is not implicitly convertible to expected type (type of first return variable) " +
+ expected->toString() +
+ "."
+ );
+ }
+}
+
+bool TypeChecker::visit(VariableDeclarationStatement const& _statement)
+{
+ if (!_statement.initialValue())
+ {
+ // No initial value is only permitted for single variables with specified type.
+ if (_statement.declarations().size() != 1 || !_statement.declarations().front())
+ fatalTypeError(_statement, "Assignment necessary for type detection.");
+ VariableDeclaration const& varDecl = *_statement.declarations().front();
+ if (!varDecl.annotation().type)
+ fatalTypeError(_statement, "Assignment necessary for type detection.");
+ if (auto ref = dynamic_cast<ReferenceType const*>(type(varDecl).get()))
+ {
+ if (ref->dataStoredIn(DataLocation::Storage))
+ {
+ auto err = make_shared<Error>(Error::Type::Warning);
+ *err <<
+ errinfo_sourceLocation(varDecl.location()) <<
+ errinfo_comment("Uninitialized storage pointer. Did you mean '<type> memory " + varDecl.name() + "'?");
+ m_errors.push_back(err);
+ }
+ }
+ varDecl.accept(*this);
+ return false;
+ }
+
+ // Here we have an initial value and might have to derive some types before we can visit
+ // the variable declaration(s).
+
+ _statement.initialValue()->accept(*this);
+ TypePointers valueTypes;
+ if (auto tupleType = dynamic_cast<TupleType const*>(type(*_statement.initialValue()).get()))
+ valueTypes = tupleType->components();
+ else
+ valueTypes = TypePointers{type(*_statement.initialValue())};
+
+ // Determine which component is assigned to which variable.
+ // If numbers do not match, fill up if variables begin or end empty (not both).
+ vector<VariableDeclaration const*>& assignments = _statement.annotation().assignments;
+ assignments.resize(valueTypes.size(), nullptr);
+ vector<ASTPointer<VariableDeclaration>> const& variables = _statement.declarations();
+ if (variables.empty())
+ {
+ if (!valueTypes.empty())
+ fatalTypeError(
+ _statement,
+ "Too many components (" +
+ toString(valueTypes.size()) +
+ ") in value for variable assignment (0) needed"
+ );
+ }
+ else if (valueTypes.size() != variables.size() && !variables.front() && !variables.back())
+ fatalTypeError(
+ _statement,
+ "Wildcard both at beginning and end of variable declaration list is only allowed "
+ "if the number of components is equal."
+ );
+ size_t minNumValues = variables.size();
+ if (!variables.empty() && (!variables.back() || !variables.front()))
+ --minNumValues;
+ if (valueTypes.size() < minNumValues)
+ fatalTypeError(
+ _statement,
+ "Not enough components (" +
+ toString(valueTypes.size()) +
+ ") in value to assign all variables (" +
+ toString(minNumValues) + ")."
+ );
+ if (valueTypes.size() > variables.size() && variables.front() && variables.back())
+ fatalTypeError(
+ _statement,
+ "Too many components (" +
+ toString(valueTypes.size()) +
+ ") in value for variable assignment (" +
+ toString(minNumValues) +
+ " needed)."
+ );
+ bool fillRight = !variables.empty() && (!variables.back() || variables.front());
+ for (size_t i = 0; i < min(variables.size(), valueTypes.size()); ++i)
+ if (fillRight)
+ assignments[i] = variables[i].get();
+ else
+ assignments[assignments.size() - i - 1] = variables[variables.size() - i - 1].get();
+
+ for (size_t i = 0; i < assignments.size(); ++i)
+ {
+ if (!assignments[i])
+ continue;
+ VariableDeclaration const& var = *assignments[i];
+ solAssert(!var.value(), "Value has to be tied to statement.");
+ TypePointer const& valueComponentType = valueTypes[i];
+ solAssert(!!valueComponentType, "");
+ if (!var.annotation().type)
+ {
+ // Infer type from value.
+ solAssert(!var.typeName(), "");
+ if (
+ valueComponentType->category() == Type::Category::IntegerConstant &&
+ !dynamic_pointer_cast<IntegerConstantType const>(valueComponentType)->integerType()
+ )
+ fatalTypeError(*_statement.initialValue(), "Invalid integer constant " + valueComponentType->toString() + ".");
+ var.annotation().type = valueComponentType->mobileType();
+ var.accept(*this);
+ }
+ else
+ {
+ var.accept(*this);
+ if (!valueComponentType->isImplicitlyConvertibleTo(*var.annotation().type))
+ typeError(
+ _statement,
+ "Type " +
+ valueComponentType->toString() +
+ " is not implicitly convertible to expected type " +
+ var.annotation().type->toString() +
+ "."
+ );
+ }
+ }
+ return false;
+}
+
+void TypeChecker::endVisit(ExpressionStatement const& _statement)
+{
+ if (type(_statement.expression())->category() == Type::Category::IntegerConstant)
+ if (!dynamic_pointer_cast<IntegerConstantType const>(type(_statement.expression()))->integerType())
+ typeError(_statement.expression(), "Invalid integer constant.");
+}
+
+bool TypeChecker::visit(Assignment const& _assignment)
+{
+ requireLValue(_assignment.leftHandSide());
+ TypePointer t = type(_assignment.leftHandSide());
+ _assignment.annotation().type = t;
+ if (TupleType const* tupleType = dynamic_cast<TupleType const*>(t.get()))
+ {
+ // Sequenced assignments of tuples is not valid, make the result a "void" type.
+ _assignment.annotation().type = make_shared<TupleType>();
+ expectType(_assignment.rightHandSide(), *tupleType);
+ }
+ else if (t->category() == Type::Category::Mapping)
+ {
+ typeError(_assignment, "Mappings cannot be assigned to.");
+ _assignment.rightHandSide().accept(*this);
+ }
+ else if (_assignment.assignmentOperator() == Token::Assign)
+ expectType(_assignment.rightHandSide(), *t);
+ else
+ {
+ // compound assignment
+ _assignment.rightHandSide().accept(*this);
+ TypePointer resultType = t->binaryOperatorResult(
+ Token::AssignmentToBinaryOp(_assignment.assignmentOperator()),
+ type(_assignment.rightHandSide())
+ );
+ if (!resultType || *resultType != *t)
+ typeError(
+ _assignment,
+ "Operator " +
+ string(Token::toString(_assignment.assignmentOperator())) +
+ " not compatible with types " +
+ t->toString() +
+ " and " +
+ type(_assignment.rightHandSide())->toString()
+ );
+ }
+ return false;
+}
+
+bool TypeChecker::visit(TupleExpression const& _tuple)
+{
+ vector<ASTPointer<Expression>> const& components = _tuple.components();
+ TypePointers types;
+ if (_tuple.annotation().lValueRequested)
+ {
+ for (auto const& component: components)
+ if (component)
+ {
+ requireLValue(*component);
+ types.push_back(type(*component));
+ }
+ else
+ types.push_back(TypePointer());
+ _tuple.annotation().type = make_shared<TupleType>(types);
+ // If some of the components are not LValues, the error is reported above.
+ _tuple.annotation().isLValue = true;
+ }
+ else
+ {
+ for (size_t i = 0; i < components.size(); ++i)
+ {
+ // Outside of an lvalue-context, the only situation where a component can be empty is (x,).
+ if (!components[i] && !(i == 1 && components.size() == 2))
+ fatalTypeError(_tuple, "Tuple component cannot be empty.");
+ else if (components[i])
+ {
+ components[i]->accept(*this);
+ types.push_back(type(*components[i]));
+ }
+ else
+ types.push_back(TypePointer());
+ }
+ if (components.size() == 1)
+ _tuple.annotation().type = type(*components[0]);
+ else
+ {
+ if (components.size() == 2 && !components[1])
+ types.pop_back();
+ _tuple.annotation().type = make_shared<TupleType>(types);
+ }
+ }
+ return false;
+}
+
+bool TypeChecker::visit(UnaryOperation const& _operation)
+{
+ // Inc, Dec, Add, Sub, Not, BitNot, Delete
+ Token::Value op = _operation.getOperator();
+ if (op == Token::Value::Inc || op == Token::Value::Dec || op == Token::Value::Delete)
+ requireLValue(_operation.subExpression());
+ else
+ _operation.subExpression().accept(*this);
+ TypePointer const& subExprType = type(_operation.subExpression());
+ TypePointer t = type(_operation.subExpression())->unaryOperatorResult(op);
+ if (!t)
+ {
+ typeError(
+ _operation,
+ "Unary operator " +
+ string(Token::toString(op)) +
+ " cannot be applied to type " +
+ subExprType->toString()
+ );
+ t = subExprType;
+ }
+ _operation.annotation().type = t;
+ return false;
+}
+
+void TypeChecker::endVisit(BinaryOperation const& _operation)
+{
+ TypePointer const& leftType = type(_operation.leftExpression());
+ TypePointer const& rightType = type(_operation.rightExpression());
+ TypePointer commonType = leftType->binaryOperatorResult(_operation.getOperator(), rightType);
+ if (!commonType)
+ {
+ typeError(
+ _operation,
+ "Operator " +
+ string(Token::toString(_operation.getOperator())) +
+ " not compatible with types " +
+ leftType->toString() +
+ " and " +
+ rightType->toString()
+ );
+ commonType = leftType;
+ }
+ _operation.annotation().commonType = commonType;
+ _operation.annotation().type =
+ Token::isCompareOp(_operation.getOperator()) ?
+ make_shared<BoolType>() :
+ commonType;
+}
+
+bool TypeChecker::visit(FunctionCall const& _functionCall)
+{
+ bool isPositionalCall = _functionCall.names().empty();
+ vector<ASTPointer<Expression const>> arguments = _functionCall.arguments();
+ vector<ASTPointer<ASTString>> const& argumentNames = _functionCall.names();
+
+ // We need to check arguments' type first as they will be needed for overload resolution.
+ shared_ptr<TypePointers> argumentTypes;
+ if (isPositionalCall)
+ argumentTypes = make_shared<TypePointers>();
+ for (ASTPointer<Expression const> const& argument: arguments)
+ {
+ argument->accept(*this);
+ // only store them for positional calls
+ if (isPositionalCall)
+ argumentTypes->push_back(type(*argument));
+ }
+ if (isPositionalCall)
+ _functionCall.expression().annotation().argumentTypes = move(argumentTypes);
+
+ _functionCall.expression().accept(*this);
+ TypePointer expressionType = type(_functionCall.expression());
+
+ if (auto const* typeType = dynamic_cast<TypeType const*>(expressionType.get()))
+ {
+ _functionCall.annotation().isStructConstructorCall = (typeType->actualType()->category() == Type::Category::Struct);
+ _functionCall.annotation().isTypeConversion = !_functionCall.annotation().isStructConstructorCall;
+ }
+ else
+ _functionCall.annotation().isStructConstructorCall = _functionCall.annotation().isTypeConversion = false;
+
+ if (_functionCall.annotation().isTypeConversion)
+ {
+ TypeType const& t = dynamic_cast<TypeType const&>(*expressionType);
+ TypePointer resultType = t.actualType();
+ if (arguments.size() != 1)
+ typeError(_functionCall, "Exactly one argument expected for explicit type conversion.");
+ else if (!isPositionalCall)
+ typeError(_functionCall, "Type conversion cannot allow named arguments.");
+ else
+ {
+ TypePointer const& argType = type(*arguments.front());
+ if (auto argRefType = dynamic_cast<ReferenceType const*>(argType.get()))
+ // do not change the data location when converting
+ // (data location cannot yet be specified for type conversions)
+ resultType = ReferenceType::copyForLocationIfReference(argRefType->location(), resultType);
+ if (!argType->isExplicitlyConvertibleTo(*resultType))
+ typeError(_functionCall, "Explicit type conversion not allowed.");
+ }
+ _functionCall.annotation().type = resultType;
+
+ return false;
+ }
+
+ // Actual function call or struct constructor call.
+
+ FunctionTypePointer functionType;
+
+ /// For error message: Struct members that were removed during conversion to memory.
+ set<string> membersRemovedForStructConstructor;
+ if (_functionCall.annotation().isStructConstructorCall)
+ {
+ TypeType const& t = dynamic_cast<TypeType const&>(*expressionType);
+ auto const& structType = dynamic_cast<StructType const&>(*t.actualType());
+ functionType = structType.constructorType();
+ membersRemovedForStructConstructor = structType.membersMissingInMemory();
+ }
+ else
+ functionType = dynamic_pointer_cast<FunctionType const>(expressionType);
+
+ if (!functionType)
+ {
+ typeError(_functionCall, "Type is not callable");
+ _functionCall.annotation().type = make_shared<TupleType>();
+ return false;
+ }
+ else if (functionType->returnParameterTypes().size() == 1)
+ _functionCall.annotation().type = functionType->returnParameterTypes().front();
+ else
+ _functionCall.annotation().type = make_shared<TupleType>(functionType->returnParameterTypes());
+
+ TypePointers const& parameterTypes = functionType->parameterTypes();
+ if (!functionType->takesArbitraryParameters() && parameterTypes.size() != arguments.size())
+ {
+ string msg =
+ "Wrong argument count for function call: " +
+ toString(arguments.size()) +
+ " arguments given but expected " +
+ toString(parameterTypes.size()) +
+ ".";
+ // Extend error message in case we try to construct a struct with mapping member.
+ if (_functionCall.annotation().isStructConstructorCall && !membersRemovedForStructConstructor.empty())
+ {
+ msg += " Members that have to be skipped in memory:";
+ for (auto const& member: membersRemovedForStructConstructor)
+ msg += " " + member;
+ }
+ typeError(_functionCall, msg);
+ }
+ else if (isPositionalCall)
+ {
+ // call by positional arguments
+ for (size_t i = 0; i < arguments.size(); ++i)
+ {
+ auto const& argType = type(*arguments[i]);
+ if (functionType->takesArbitraryParameters())
+ {
+ if (auto t = dynamic_cast<IntegerConstantType const*>(argType.get()))
+ if (!t->integerType())
+ typeError(*arguments[i], "Integer constant too large.");
+ }
+ else if (!type(*arguments[i])->isImplicitlyConvertibleTo(*parameterTypes[i]))
+ typeError(
+ *arguments[i],
+ "Invalid type for argument in function call. "
+ "Invalid implicit conversion from " +
+ type(*arguments[i])->toString() +
+ " to " +
+ parameterTypes[i]->toString() +
+ " requested."
+ );
+ }
+ }
+ else
+ {
+ // call by named arguments
+ auto const& parameterNames = functionType->parameterNames();
+ if (functionType->takesArbitraryParameters())
+ typeError(
+ _functionCall,
+ "Named arguments cannnot be used for functions that take arbitrary parameters."
+ );
+ else if (parameterNames.size() > argumentNames.size())
+ typeError(_functionCall, "Some argument names are missing.");
+ else if (parameterNames.size() < argumentNames.size())
+ typeError(_functionCall, "Too many arguments.");
+ else
+ {
+ // check duplicate names
+ bool duplication = false;
+ for (size_t i = 0; i < argumentNames.size(); i++)
+ for (size_t j = i + 1; j < argumentNames.size(); j++)
+ if (*argumentNames[i] == *argumentNames[j])
+ {
+ duplication = true;
+ typeError(*arguments[i], "Duplicate named argument.");
+ }
+
+ // check actual types
+ if (!duplication)
+ for (size_t i = 0; i < argumentNames.size(); i++)
+ {
+ bool found = false;
+ for (size_t j = 0; j < parameterNames.size(); j++)
+ if (parameterNames[j] == *argumentNames[i])
+ {
+ found = true;
+ // check type convertible
+ if (!type(*arguments[i])->isImplicitlyConvertibleTo(*parameterTypes[j]))
+ typeError(
+ *arguments[i],
+ "Invalid type for argument in function call. "
+ "Invalid implicit conversion from " +
+ type(*arguments[i])->toString() +
+ " to " +
+ parameterTypes[i]->toString() +
+ " requested."
+ );
+ break;
+ }
+
+ if (!found)
+ typeError(
+ _functionCall,
+ "Named argument does not match function declaration."
+ );
+ }
+ }
+ }
+
+ return false;
+}
+
+void TypeChecker::endVisit(NewExpression const& _newExpression)
+{
+ auto contract = dynamic_cast<ContractDefinition const*>(&dereference(_newExpression.contractName()));
+
+ if (!contract)
+ fatalTypeError(_newExpression, "Identifier is not a contract.");
+ if (!contract->annotation().isFullyImplemented)
+ typeError(_newExpression, "Trying to create an instance of an abstract contract.");
+
+ auto scopeContract = _newExpression.contractName().annotation().contractScope;
+ scopeContract->annotation().contractDependencies.insert(contract);
+ solAssert(
+ !contract->annotation().linearizedBaseContracts.empty(),
+ "Linearized base contracts not yet available."
+ );
+ if (contractDependenciesAreCyclic(*scopeContract))
+ typeError(
+ _newExpression,
+ "Circular reference for contract creation (cannot create instance of derived or same contract)."
+ );
+
+ auto contractType = make_shared<ContractType>(*contract);
+ TypePointers const& parameterTypes = contractType->constructorType()->parameterTypes();
+ _newExpression.annotation().type = make_shared<FunctionType>(
+ parameterTypes,
+ TypePointers{contractType},
+ strings(),
+ strings(),
+ FunctionType::Location::Creation
+ );
+}
+
+bool TypeChecker::visit(MemberAccess const& _memberAccess)
+{
+ _memberAccess.expression().accept(*this);
+ TypePointer exprType = type(_memberAccess.expression());
+ ASTString const& memberName = _memberAccess.memberName();
+
+ // Retrieve the types of the arguments if this is used to call a function.
+ auto const& argumentTypes = _memberAccess.annotation().argumentTypes;
+ MemberList::MemberMap possibleMembers = exprType->members().membersByName(memberName);
+ if (possibleMembers.size() > 1 && argumentTypes)
+ {
+ // do overload resolution
+ for (auto it = possibleMembers.begin(); it != possibleMembers.end();)
+ if (
+ it->type->category() == Type::Category::Function &&
+ !dynamic_cast<FunctionType const&>(*it->type).canTakeArguments(*argumentTypes)
+ )
+ it = possibleMembers.erase(it);
+ else
+ ++it;
+ }
+ if (possibleMembers.size() == 0)
+ {
+ auto storageType = ReferenceType::copyForLocationIfReference(
+ DataLocation::Storage,
+ exprType
+ );
+ if (!storageType->members().membersByName(memberName).empty())
+ fatalTypeError(
+ _memberAccess,
+ "Member \"" + memberName + "\" is not available in " +
+ exprType->toString() +
+ " outside of storage."
+ );
+ fatalTypeError(
+ _memberAccess,
+ "Member \"" + memberName + "\" not found or not visible "
+ "after argument-dependent lookup in " + exprType->toString()
+ );
+ }
+ else if (possibleMembers.size() > 1)
+ fatalTypeError(
+ _memberAccess,
+ "Member \"" + memberName + "\" not unique "
+ "after argument-dependent lookup in " + exprType->toString()
+ );
+
+ auto& annotation = _memberAccess.annotation();
+ annotation.referencedDeclaration = possibleMembers.front().declaration;
+ annotation.type = possibleMembers.front().type;
+ if (exprType->category() == Type::Category::Struct)
+ annotation.isLValue = true;
+ else if (exprType->category() == Type::Category::Array)
+ {
+ auto const& arrayType(dynamic_cast<ArrayType const&>(*exprType));
+ annotation.isLValue = (
+ memberName == "length" &&
+ arrayType.location() == DataLocation::Storage &&
+ arrayType.isDynamicallySized()
+ );
+ }
+
+ return false;
+}
+
+bool TypeChecker::visit(IndexAccess const& _access)
+{
+ _access.baseExpression().accept(*this);
+ TypePointer baseType = type(_access.baseExpression());
+ TypePointer resultType;
+ bool isLValue = false;
+ Expression const* index = _access.indexExpression();
+ switch (baseType->category())
+ {
+ case Type::Category::Array:
+ {
+ ArrayType const& actualType = dynamic_cast<ArrayType const&>(*baseType);
+ if (!index)
+ typeError(_access, "Index expression cannot be omitted.");
+ else if (actualType.isString())
+ {
+ typeError(_access, "Index access for string is not possible.");
+ index->accept(*this);
+ }
+ else
+ {
+ expectType(*index, IntegerType(256));
+ if (auto integerType = dynamic_cast<IntegerConstantType const*>(type(*index).get()))
+ if (!actualType.isDynamicallySized() && actualType.length() <= integerType->literalValue(nullptr))
+ typeError(_access, "Out of bounds array access.");
+ }
+ resultType = actualType.baseType();
+ isLValue = actualType.location() != DataLocation::CallData;
+ break;
+ }
+ case Type::Category::Mapping:
+ {
+ MappingType const& actualType = dynamic_cast<MappingType const&>(*baseType);
+ if (!index)
+ typeError(_access, "Index expression cannot be omitted.");
+ else
+ expectType(*index, *actualType.keyType());
+ resultType = actualType.valueType();
+ isLValue = true;
+ break;
+ }
+ case Type::Category::TypeType:
+ {
+ TypeType const& typeType = dynamic_cast<TypeType const&>(*baseType);
+ if (!index)
+ resultType = make_shared<TypeType>(make_shared<ArrayType>(DataLocation::Memory, typeType.actualType()));
+ else
+ {
+ index->accept(*this);
+ if (auto length = dynamic_cast<IntegerConstantType const*>(type(*index).get()))
+ resultType = make_shared<TypeType>(make_shared<ArrayType>(
+ DataLocation::Memory,
+ typeType.actualType(),
+ length->literalValue(nullptr)
+ ));
+ else
+ typeError(*index, "Integer constant expected.");
+ }
+ break;
+ }
+ default:
+ fatalTypeError(
+ _access.baseExpression(),
+ "Indexed expression has to be a type, mapping or array (is " + baseType->toString() + ")"
+ );
+ }
+ _access.annotation().type = move(resultType);
+ _access.annotation().isLValue = isLValue;
+
+ return false;
+}
+
+bool TypeChecker::visit(Identifier const& _identifier)
+{
+ IdentifierAnnotation& annotation = _identifier.annotation();
+ if (!annotation.referencedDeclaration)
+ {
+ if (!annotation.argumentTypes)
+ fatalTypeError(_identifier, "Unable to determine overloaded type.");
+ if (annotation.overloadedDeclarations.empty())
+ fatalTypeError(_identifier, "No candidates for overload resolution found.");
+ else if (annotation.overloadedDeclarations.size() == 1)
+ annotation.referencedDeclaration = *annotation.overloadedDeclarations.begin();
+ else
+ {
+ vector<Declaration const*> candidates;
+
+ for (Declaration const* declaration: annotation.overloadedDeclarations)
+ {
+ TypePointer function = declaration->type(_identifier.annotation().contractScope);
+ solAssert(!!function, "Requested type not present.");
+ auto const* functionType = dynamic_cast<FunctionType const*>(function.get());
+ if (functionType && functionType->canTakeArguments(*annotation.argumentTypes))
+ candidates.push_back(declaration);
+ }
+ if (candidates.empty())
+ fatalTypeError(_identifier, "No matching declaration found after argument-dependent lookup.");
+ else if (candidates.size() == 1)
+ annotation.referencedDeclaration = candidates.front();
+ else
+ fatalTypeError(_identifier, "No unique declaration found after argument-dependent lookup.");
+ }
+ }
+ solAssert(
+ !!annotation.referencedDeclaration,
+ "Referenced declaration is null after overload resolution."
+ );
+ annotation.isLValue = annotation.referencedDeclaration->isLValue();
+ annotation.type = annotation.referencedDeclaration->type(_identifier.annotation().contractScope);
+ if (!annotation.type)
+ fatalTypeError(_identifier, "Declaration referenced before type could be determined.");
+ return false;
+}
+
+void TypeChecker::endVisit(ElementaryTypeNameExpression const& _expr)
+{
+ _expr.annotation().type = make_shared<TypeType>(Type::fromElementaryTypeName(_expr.typeToken()));
+}
+
+void TypeChecker::endVisit(Literal const& _literal)
+{
+ _literal.annotation().type = Type::forLiteral(_literal);
+ if (!_literal.annotation().type)
+ fatalTypeError(_literal, "Invalid literal value.");
+}
+
+bool TypeChecker::contractDependenciesAreCyclic(
+ ContractDefinition const& _contract,
+ std::set<ContractDefinition const*> const& _seenContracts
+) const
+{
+ // Naive depth-first search that remembers nodes already seen.
+ if (_seenContracts.count(&_contract))
+ return true;
+ set<ContractDefinition const*> seen(_seenContracts);
+ seen.insert(&_contract);
+ for (auto const* c: _contract.annotation().contractDependencies)
+ if (contractDependenciesAreCyclic(*c, seen))
+ return true;
+ return false;
+}
+
+Declaration const& TypeChecker::dereference(Identifier const& _identifier)
+{
+ solAssert(!!_identifier.annotation().referencedDeclaration, "Declaration not stored.");
+ return *_identifier.annotation().referencedDeclaration;
+}
+
+void TypeChecker::expectType(Expression const& _expression, Type const& _expectedType)
+{
+ _expression.accept(*this);
+
+ if (!type(_expression)->isImplicitlyConvertibleTo(_expectedType))
+ typeError(
+ _expression,
+ "Type " +
+ type(_expression)->toString() +
+ " is not implicitly convertible to expected type " +
+ _expectedType.toString() +
+ "."
+ );
+}
+
+void TypeChecker::requireLValue(Expression const& _expression)
+{
+ _expression.annotation().lValueRequested = true;
+ _expression.accept(*this);
+ if (!_expression.annotation().isLValue)
+ typeError(_expression, "Expression has to be an lvalue.");
+}
+
+void TypeChecker::typeError(ASTNode const& _node, string const& _description)
+{
+ auto err = make_shared<Error>(Error::Type::TypeError);
+ *err <<
+ errinfo_sourceLocation(_node.location()) <<
+ errinfo_comment(_description);
+
+ m_errors.push_back(err);
+}
+
+void TypeChecker::fatalTypeError(ASTNode const& _node, string const& _description)
+{
+ typeError(_node, _description);
+ BOOST_THROW_EXCEPTION(FatalError());
+}
diff --git a/libsolidity/analysis/TypeChecker.h b/libsolidity/analysis/TypeChecker.h
new file mode 100644
index 00000000..9a568349
--- /dev/null
+++ b/libsolidity/analysis/TypeChecker.h
@@ -0,0 +1,122 @@
+/*
+ This file is part of cpp-ethereum.
+
+ cpp-ethereum 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.
+
+ cpp-ethereum 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 cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
+*/
+/**
+ * @author Christian <c@ethdev.com>
+ * @date 2015
+ * Type analyzer and checker.
+ */
+
+#pragma once
+
+#include <libsolidity/analysis/TypeChecker.h>
+#include <libsolidity/ast/Types.h>
+#include <libsolidity/ast/ASTAnnotations.h>
+#include <libsolidity/ast/ASTForward.h>
+#include <libsolidity/ast/ASTVisitor.h>
+
+namespace dev
+{
+namespace solidity
+{
+
+
+/**
+ * The module that performs type analysis on the AST, checks the applicability of operations on
+ * those types and stores errors for invalid operations.
+ * Provides a way to retrieve the type of an AST node.
+ */
+class TypeChecker: private ASTConstVisitor
+{
+public:
+ /// @param _errors the reference to the list of errors and warnings to add them found during type checking.
+ TypeChecker(ErrorList& _errors): m_errors(_errors) {}
+
+ /// Performs type checking on the given contract and all of its sub-nodes.
+ /// @returns true iff all checks passed. Note even if all checks passed, errors() can still contain warnings
+ bool checkTypeRequirements(ContractDefinition const& _contract);
+
+ /// @returns the type of an expression and asserts that it is present.
+ TypePointer const& type(Expression const& _expression) const;
+ /// @returns the type of the given variable and throws if the type is not present
+ /// (this can happen for variables with non-explicit types before their types are resolved)
+ TypePointer const& type(VariableDeclaration const& _variable) const;
+
+private:
+ /// Adds a new error to the list of errors.
+ void typeError(ASTNode const& _node, std::string const& _description);
+
+ /// Adds a new error to the list of errors and throws to abort type checking.
+ void fatalTypeError(ASTNode const& _node, std::string const& _description);
+
+ virtual bool visit(ContractDefinition const& _contract) override;
+ /// Checks that two functions defined in this contract with the same name have different
+ /// arguments and that there is at most one constructor.
+ void checkContractDuplicateFunctions(ContractDefinition const& _contract);
+ void checkContractIllegalOverrides(ContractDefinition const& _contract);
+ void checkContractAbstractFunctions(ContractDefinition const& _contract);
+ void checkContractAbstractConstructors(ContractDefinition const& _contract);
+ /// Checks that different functions with external visibility end up having different
+ /// external argument types (i.e. different signature).
+ void checkContractExternalTypeClashes(ContractDefinition const& _contract);
+ /// Checks that all requirements for a library are fulfilled if this is a library.
+ void checkLibraryRequirements(ContractDefinition const& _contract);
+
+ virtual void endVisit(InheritanceSpecifier const& _inheritance) override;
+ virtual bool visit(StructDefinition const& _struct) override;
+ virtual bool visit(FunctionDefinition const& _function) override;
+ virtual bool visit(VariableDeclaration const& _variable) override;
+ /// We need to do this manually because we want to pass the bases of the current contract in
+ /// case this is a base constructor call.
+ void visitManually(ModifierInvocation const& _modifier, std::vector<ContractDefinition const*> const& _bases);
+ virtual bool visit(EventDefinition const& _eventDef) override;
+ virtual bool visit(IfStatement const& _ifStatement) override;
+ virtual bool visit(WhileStatement const& _whileStatement) override;
+ virtual bool visit(ForStatement const& _forStatement) override;
+ virtual void endVisit(Return const& _return) override;
+ virtual bool visit(VariableDeclarationStatement const& _variable) override;
+ virtual void endVisit(ExpressionStatement const& _statement) override;
+ virtual bool visit(Assignment const& _assignment) override;
+ virtual bool visit(TupleExpression const& _tuple) override;
+ virtual void endVisit(BinaryOperation const& _operation) override;
+ virtual bool visit(UnaryOperation const& _operation) override;
+ virtual bool visit(FunctionCall const& _functionCall) override;
+ virtual void endVisit(NewExpression const& _newExpression) override;
+ virtual bool visit(MemberAccess const& _memberAccess) override;
+ virtual bool visit(IndexAccess const& _indexAccess) override;
+ virtual bool visit(Identifier const& _identifier) override;
+ virtual void endVisit(ElementaryTypeNameExpression const& _expr) override;
+ virtual void endVisit(Literal const& _literal) override;
+
+ bool contractDependenciesAreCyclic(
+ ContractDefinition const& _contract,
+ std::set<ContractDefinition const*> const& _seenContracts = std::set<ContractDefinition const*>()
+ ) const;
+
+ /// @returns the referenced declaration and throws on error.
+ Declaration const& dereference(Identifier const& _identifier);
+
+ /// Runs type checks on @a _expression to infer its type and then checks that it is implicitly
+ /// convertible to @a _expectedType.
+ void expectType(Expression const& _expression, Type const& _expectedType);
+ /// Runs type checks on @a _expression to infer its type and then checks that it is an LValue.
+ void requireLValue(Expression const& _expression);
+
+ ErrorList& m_errors;
+};
+
+}
+}