aboutsummaryrefslogtreecommitdiffstats
path: root/libsolidity
diff options
context:
space:
mode:
authorchriseth <c@ethdev.com>2015-09-23 20:42:54 +0800
committerchriseth <c@ethdev.com>2015-09-23 20:42:54 +0800
commitefdea76d5eb026777e89b8683413184bf8c5287d (patch)
treed13122099f0ad3e5b65dd263a87f0e653db6e672 /libsolidity
parent028f561dac6aec0692daa8217b0ee1e3a0c5aa72 (diff)
parente32a063a10b41efeffeec451188b2f3466c8b308 (diff)
downloaddexon-solidity-efdea76d5eb026777e89b8683413184bf8c5287d.tar.gz
dexon-solidity-efdea76d5eb026777e89b8683413184bf8c5287d.tar.zst
dexon-solidity-efdea76d5eb026777e89b8683413184bf8c5287d.zip
Merge pull request #85 from chriseth/warnings
Support mulitple errors and warnings.
Diffstat (limited to 'libsolidity')
-rw-r--r--libsolidity/AST.cpp1049
-rw-r--r--libsolidity/AST.h305
-rw-r--r--libsolidity/ASTAnnotations.cpp28
-rw-r--r--libsolidity/ASTAnnotations.h123
-rw-r--r--libsolidity/ASTJsonConverter.cpp18
-rw-r--r--libsolidity/ASTJsonConverter.h3
-rw-r--r--libsolidity/ASTPrinter.cpp6
-rw-r--r--libsolidity/AST_accept.h6
-rw-r--r--libsolidity/Compiler.cpp37
-rw-r--r--libsolidity/Compiler.h7
-rw-r--r--libsolidity/CompilerContext.h1
-rw-r--r--libsolidity/CompilerStack.cpp39
-rw-r--r--libsolidity/CompilerStack.h27
-rw-r--r--libsolidity/CompilerUtils.cpp2
-rw-r--r--libsolidity/CompilerUtils.h2
-rw-r--r--libsolidity/ConstantEvaluator.cpp59
-rw-r--r--libsolidity/ConstantEvaluator.h50
-rw-r--r--libsolidity/Exceptions.h12
-rw-r--r--libsolidity/ExpressionCompiler.cpp147
-rw-r--r--libsolidity/ExpressionCompiler.h2
-rw-r--r--libsolidity/InterfaceHandler.cpp2
-rw-r--r--libsolidity/LValue.cpp8
-rw-r--r--libsolidity/LValue.h5
-rw-r--r--libsolidity/NameAndTypeResolver.cpp162
-rw-r--r--libsolidity/NameAndTypeResolver.h38
-rw-r--r--libsolidity/ReferencesResolver.cpp225
-rw-r--r--libsolidity/ReferencesResolver.h69
-rw-r--r--libsolidity/TypeChecker.cpp1143
-rw-r--r--libsolidity/TypeChecker.h114
-rw-r--r--libsolidity/Types.cpp73
30 files changed, 2176 insertions, 1586 deletions
diff --git a/libsolidity/AST.cpp b/libsolidity/AST.cpp
index daa84016..00b51c42 100644
--- a/libsolidity/AST.cpp
+++ b/libsolidity/AST.cpp
@@ -22,7 +22,6 @@
#include <algorithm>
#include <functional>
-#include <boost/range/adaptor/reversed.hpp>
#include <libsolidity/Utils.h>
#include <libsolidity/AST.h>
#include <libsolidity/ASTVisitor.h>
@@ -32,83 +31,29 @@
#include <libdevcore/SHA3.h>
using namespace std;
+using namespace dev;
+using namespace dev::solidity;
-namespace dev
-{
-namespace solidity
+ASTNode::ASTNode(SourceLocation const& _location):
+ m_location(_location)
{
+}
-TypeError ASTNode::createTypeError(string const& _description) const
+ASTNode::~ASTNode()
{
- return TypeError() << errinfo_sourceLocation(location()) << errinfo_comment(_description);
+ delete m_annotation;
}
-TypePointer ContractDefinition::type(ContractDefinition const* _currentContract) const
+ASTAnnotation& ASTNode::annotation() const
{
- return make_shared<TypeType>(make_shared<ContractType>(*this), _currentContract);
+ if (!m_annotation)
+ m_annotation = new ASTAnnotation();
+ return *m_annotation;
}
-void ContractDefinition::checkTypeRequirements()
+TypeError ASTNode::createTypeError(string const& _description) const
{
- for (ASTPointer<InheritanceSpecifier> const& baseSpecifier: baseContracts())
- baseSpecifier->checkTypeRequirements();
-
- checkDuplicateFunctions();
- checkIllegalOverrides();
- checkAbstractFunctions();
- checkAbstractConstructors();
-
- FunctionDefinition const* functionDefinition = constructor();
- if (functionDefinition && !functionDefinition->returnParameters().empty())
- BOOST_THROW_EXCEPTION(functionDefinition->returnParameterList()->createTypeError(
- "Non-empty \"returns\" directive for constructor."
- ));
-
- FunctionDefinition const* fallbackFunction = nullptr;
- for (ASTPointer<FunctionDefinition> const& function: definedFunctions())
- {
- if (function->name().empty())
- {
- if (fallbackFunction)
- BOOST_THROW_EXCEPTION(DeclarationError() << errinfo_comment("Only one fallback function is allowed."));
- else
- {
- fallbackFunction = function.get();
- if (!fallbackFunction->parameters().empty())
- BOOST_THROW_EXCEPTION(fallbackFunction->parameterList().createTypeError("Fallback function cannot take parameters."));
- }
- }
- if (!function->isFullyImplemented())
- setFullyImplemented(false);
- }
-
- for (ASTPointer<VariableDeclaration> const& variable: m_stateVariables)
- variable->checkTypeRequirements();
-
- for (ASTPointer<EventDefinition> const& event: events())
- event->checkTypeRequirements();
-
- for (ASTPointer<ModifierDefinition> const& modifier: functionModifiers())
- modifier->checkTypeRequirements();
-
- for (ASTPointer<FunctionDefinition> const& function: definedFunctions())
- function->checkTypeRequirements();
-
- checkExternalTypeClashes();
- // check for hash collisions in function signatures
- set<FixedHash<4>> hashes;
- for (auto const& it: interfaceFunctionList())
- {
- FixedHash<4> const& hash = it.first;
- if (hashes.count(hash))
- BOOST_THROW_EXCEPTION(createTypeError(
- string("Function signature hash collision for ") + it.second->externalSignature()
- ));
- hashes.insert(hash);
- }
-
- if (isLibrary())
- checkLibraryRequirements();
+ return TypeError() << errinfo_sourceLocation(location()) << errinfo_comment(_description);
}
map<FixedHash<4>, FunctionTypePointer> ContractDefinition::interfaceFunctions() const
@@ -137,227 +82,20 @@ FunctionDefinition const* ContractDefinition::constructor() const
FunctionDefinition const* ContractDefinition::fallbackFunction() const
{
- for (ContractDefinition const* contract: linearizedBaseContracts())
+ for (ContractDefinition const* contract: annotation().linearizedBaseContracts)
for (ASTPointer<FunctionDefinition> const& f: contract->definedFunctions())
if (f->name().empty())
return f.get();
return nullptr;
}
-void ContractDefinition::checkDuplicateFunctions() const
-{
- /// 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: definedFunctions())
- functions[function->name()].push_back(function.get());
-
- if (functions[name()].size() > 1)
- {
- SecondarySourceLocation ssl;
- auto it = functions[name()].begin();
- ++it;
- for (; it != functions[name()].end(); ++it)
- ssl.append("Another declaration is here:", (*it)->location());
-
- BOOST_THROW_EXCEPTION(
- DeclarationError() <<
- errinfo_sourceLocation(functions[name()].front()->location()) <<
- errinfo_comment("More than one constructor defined.") <<
- errinfo_secondarySourceLocation(ssl)
- );
- }
- 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])))
- BOOST_THROW_EXCEPTION(
- DeclarationError() <<
- 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())
- )
-
- );
- }
-}
-
-void ContractDefinition::checkAbstractFunctions()
-{
- // 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(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->isFullyImplemented()));
- else if (it->second)
- {
- if (!function->isFullyImplemented())
- BOOST_THROW_EXCEPTION(function->createTypeError("Redeclaring an already implemented function as abstract"));
- }
- else if (function->isFullyImplemented())
- 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)
- {
- setFullyImplemented(false);
- return;
- }
-}
-
-void ContractDefinition::checkAbstractConstructors()
-{
- 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 = linearizedBaseContracts();
- for (ContractDefinition const* contract: bases)
- if (FunctionDefinition const* constructor = contract->constructor())
- if (contract != this && !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*>(
- &modifier->name()->referencedDeclaration()
- );
- if (baseContract)
- argumentsNeeded.erase(baseContract);
- }
-
-
- for (ASTPointer<InheritanceSpecifier> const& base: contract->baseContracts())
- {
- auto baseContract = dynamic_cast<ContractDefinition const*>(
- &base->name()->referencedDeclaration()
- );
- solAssert(baseContract, "");
- if (!base->arguments().empty())
- argumentsNeeded.erase(baseContract);
- }
- }
- if (!argumentsNeeded.empty())
- setFullyImplemented(false);
-}
-
-void ContractDefinition::checkIllegalOverrides() const
-{
- // 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: 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))
- BOOST_THROW_EXCEPTION(modifiers[name]->createTypeError("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
- )
- BOOST_THROW_EXCEPTION(overriding->createTypeError("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))
- BOOST_THROW_EXCEPTION(override->createTypeError("Override changes modifier signature."));
- if (!functions[name].empty())
- BOOST_THROW_EXCEPTION(override->createTypeError("Override changes modifier to function."));
- }
- }
-}
-
-void ContractDefinition::checkExternalTypeClashes() const
-{
- map<string, vector<pair<Declaration const*, shared_ptr<FunctionType>>>> externalDeclarations;
- for (ContractDefinition const* contract: linearizedBaseContracts())
- {
- for (ASTPointer<FunctionDefinition> const& f: contract->definedFunctions())
- if (f->isPartOfExternalInterface())
- {
- auto functionType = make_shared<FunctionType>(*f);
- externalDeclarations[functionType->externalSignature(f->name())].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(v->name())].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))
- BOOST_THROW_EXCEPTION(it.second[j].first->createTypeError(
- "Function overload clash during conversion to external types for arguments."
- ));
-}
-
-void ContractDefinition::checkLibraryRequirements() const
-{
- solAssert(m_isLibrary, "");
- if (!m_baseContracts.empty())
- BOOST_THROW_EXCEPTION(createTypeError("Library is not allowed to inherit."));
-
- for (auto const& var: m_stateVariables)
- if (!var->isConstant())
- BOOST_THROW_EXCEPTION(var->createTypeError("Library cannot have non-constant state variables"));
-}
-
vector<ASTPointer<EventDefinition>> const& ContractDefinition::interfaceEvents() const
{
if (!m_interfaceEvents)
{
set<string> eventsSeen;
m_interfaceEvents.reset(new vector<ASTPointer<EventDefinition>>());
- for (ContractDefinition const* contract: linearizedBaseContracts())
+ for (ContractDefinition const* contract: annotation().linearizedBaseContracts)
for (ASTPointer<EventDefinition> const& e: contract->events())
if (eventsSeen.count(e->name()) == 0)
{
@@ -375,7 +113,7 @@ vector<pair<FixedHash<4>, FunctionTypePointer>> const& ContractDefinition::inter
set<string> functionsSeen;
set<string> signaturesSeen;
m_interfaceFunctionList.reset(new vector<pair<FixedHash<4>, FunctionTypePointer>>());
- for (ContractDefinition const* contract: linearizedBaseContracts())
+ for (ContractDefinition const* contract: annotation().linearizedBaseContracts)
{
for (ASTPointer<FunctionDefinition> const& f: contract->definedFunctions())
{
@@ -395,7 +133,7 @@ vector<pair<FixedHash<4>, FunctionTypePointer>> const& ContractDefinition::inter
if (functionsSeen.count(v->name()) == 0 && v->isPartOfExternalInterface())
{
FunctionType ftype(*v);
- solAssert(v->type().get(), "");
+ solAssert(!!v->annotation().type.get(), "");
functionsSeen.insert(v->name());
FixedHash<4> hash(dev::sha3(ftype.externalSignature(v->name())));
m_interfaceFunctionList->push_back(make_pair(hash, make_shared<FunctionType>(*v)));
@@ -453,84 +191,35 @@ vector<Declaration const*> const& ContractDefinition::inheritableMembers() const
return *m_inheritableMembers;
}
-TypePointer EnumValue::type(ContractDefinition const*) const
+TypePointer ContractDefinition::type(ContractDefinition const* m_currentContract) const
{
- EnumDefinition const* parentDef = dynamic_cast<EnumDefinition const*>(scope());
- solAssert(parentDef, "Enclosing Scope of EnumValue was not set");
- return make_shared<EnumType>(*parentDef);
+ return make_shared<TypeType>(make_shared<ContractType>(*this), m_currentContract);
}
-void InheritanceSpecifier::checkTypeRequirements()
+ContractDefinitionAnnotation& ContractDefinition::annotation() const
{
- m_baseName->checkTypeRequirements(nullptr);
- for (ASTPointer<Expression> const& argument: m_arguments)
- argument->checkTypeRequirements(nullptr);
-
- ContractDefinition const* base = dynamic_cast<ContractDefinition const*>(&m_baseName->referencedDeclaration());
- solAssert(base, "Base contract not available.");
-
- if (base->isLibrary())
- BOOST_THROW_EXCEPTION(createTypeError("Libraries cannot be inherited from."));
-
- TypePointers parameterTypes = ContractType(*base).constructorType()->parameterTypes();
- if (!m_arguments.empty() && parameterTypes.size() != m_arguments.size())
- BOOST_THROW_EXCEPTION(createTypeError(
- "Wrong argument count for constructor call: " +
- toString(m_arguments.size()) +
- " arguments given but expected " +
- toString(parameterTypes.size()) +
- "."
- ));
-
- for (size_t i = 0; i < m_arguments.size(); ++i)
- if (!m_arguments[i]->type()->isImplicitlyConvertibleTo(*parameterTypes[i]))
- BOOST_THROW_EXCEPTION(m_arguments[i]->createTypeError(
- "Invalid type for argument in constructor call. "
- "Invalid implicit conversion from " +
- m_arguments[i]->type()->toString() +
- " to " +
- parameterTypes[i]->toString() +
- " requested."
- ));
+ if (!m_annotation)
+ m_annotation = new ContractDefinitionAnnotation();
+ return static_cast<ContractDefinitionAnnotation&>(*m_annotation);
}
-TypePointer StructDefinition::type(ContractDefinition const*) const
+TypeNameAnnotation& TypeName::annotation() const
{
- return make_shared<TypeType>(make_shared<StructType>(*this));
+ if (!m_annotation)
+ m_annotation = new TypeNameAnnotation();
+ return static_cast<TypeNameAnnotation&>(*m_annotation);
}
-void StructDefinition::checkMemberTypes() const
+TypePointer StructDefinition::type(ContractDefinition const*) const
{
- for (ASTPointer<VariableDeclaration> const& member: members())
- if (!member->type()->canBeStored())
- BOOST_THROW_EXCEPTION(member->createTypeError("Type cannot be used in struct."));
+ return make_shared<TypeType>(make_shared<StructType>(*this));
}
-void StructDefinition::checkRecursion() const
+TypePointer EnumValue::type(ContractDefinition const*) const
{
- using StructPointer = StructDefinition const*;
- using StructPointersSet = set<StructPointer>;
- function<void(StructPointer,StructPointersSet const&)> check = [&](StructPointer _struct, StructPointersSet const& _parents)
- {
- if (_parents.count(_struct))
- BOOST_THROW_EXCEPTION(
- ParserError() <<
- errinfo_sourceLocation(_struct->location()) <<
- errinfo_comment("Recursive struct definition.")
- );
- set<StructDefinition const*> parents = _parents;
- parents.insert(_struct);
- for (ASTPointer<VariableDeclaration> const& member: _struct->members())
- if (member->type()->category() == Type::Category::Struct)
- {
- auto const& typeName = dynamic_cast<UserDefinedTypeName const&>(*member->typeName());
- check(
- &dynamic_cast<StructDefinition const&>(*typeName.referencedDeclaration()),
- parents
- );
- }
- };
- check(this, StructPointersSet{});
+ auto parentDef = dynamic_cast<EnumDefinition const*>(scope());
+ solAssert(parentDef, "Enclosing Scope of EnumValue was not set");
+ return make_shared<EnumType>(*parentDef);
}
TypePointer EnumDefinition::type(ContractDefinition const*) const
@@ -543,92 +232,32 @@ TypePointer FunctionDefinition::type(ContractDefinition const*) const
return make_shared<FunctionType>(*this);
}
-void FunctionDefinition::checkTypeRequirements()
+string FunctionDefinition::externalSignature() const
{
- for (ASTPointer<VariableDeclaration> const& var: parameters() + returnParameters())
- {
- if (!var->type()->canLiveOutsideStorage())
- BOOST_THROW_EXCEPTION(var->createTypeError("Type is required to live outside storage."));
- if (visibility() >= Visibility::Public && !(var->type()->externalType()))
- BOOST_THROW_EXCEPTION(var->createTypeError("Internal type is not allowed for public and external functions."));
- }
- for (ASTPointer<ModifierInvocation> const& modifier: m_functionModifiers)
- modifier->checkTypeRequirements(isConstructor() ?
- dynamic_cast<ContractDefinition const&>(*scope()).linearizedBaseContracts() :
- vector<ContractDefinition const*>());
- if (m_body)
- m_body->checkTypeRequirements();
+ return FunctionType(*this).externalSignature(name());
}
-string FunctionDefinition::externalSignature() const
+TypePointer ModifierDefinition::type(ContractDefinition const*) const
{
- return FunctionType(*this).externalSignature(name());
+ return make_shared<ModifierType>(*this);
}
-bool VariableDeclaration::isLValue() const
+TypePointer EventDefinition::type(ContractDefinition const*) const
{
- // External function parameters and constant declared variables are Read-Only
- return !isExternalCallableParameter() && !m_isConstant;
+ return make_shared<FunctionType>(*this);
}
-void VariableDeclaration::checkTypeRequirements()
+UserDefinedTypeNameAnnotation& UserDefinedTypeName::annotation() const
{
- // 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.
- if (m_isConstant)
- {
- if (!dynamic_cast<ContractDefinition const*>(scope()))
- BOOST_THROW_EXCEPTION(createTypeError("Illegal use of \"constant\" specifier."));
- if (!m_value)
- BOOST_THROW_EXCEPTION(createTypeError("Uninitialized \"constant\" variable."));
- if (m_type && !m_type->isValueType())
- {
- // TODO: const is implemented only for uint, bytesXX, string and enums types.
- bool constImplemented = false;
- if (auto arrayType = dynamic_cast<ArrayType const*>(m_type.get()))
- constImplemented = arrayType->isByteArray();
- if (!constImplemented)
- BOOST_THROW_EXCEPTION(createTypeError(
- "Illegal use of \"constant\" specifier. \"constant\" "
- "is not yet implemented for this type."
- ));
- }
- }
- if (m_type)
- {
- if (m_value)
- m_value->expectType(*m_type);
- }
- else
- {
- if (!m_value)
- // This feature might be extended in the future.
- BOOST_THROW_EXCEPTION(createTypeError("Assignment necessary for type detection."));
- m_value->checkTypeRequirements(nullptr);
+ if (!m_annotation)
+ m_annotation = new UserDefinedTypeNameAnnotation();
+ return static_cast<UserDefinedTypeNameAnnotation&>(*m_annotation);
+}
- TypePointer const& type = m_value->type();
- if (
- type->category() == Type::Category::IntegerConstant &&
- !dynamic_pointer_cast<IntegerConstantType const>(type)->integerType()
- )
- BOOST_THROW_EXCEPTION(m_value->createTypeError("Invalid integer constant " + type->toString() + "."));
- else if (type->category() == Type::Category::Void)
- BOOST_THROW_EXCEPTION(createTypeError("Variable cannot have void type."));
- m_type = type->mobileType();
- }
- solAssert(!!m_type, "");
- if (!m_isStateVariable)
- {
- if (m_type->dataStoredIn(DataLocation::Memory) || m_type->dataStoredIn(DataLocation::CallData))
- if (!m_type->canLiveOutsideStorage())
- BOOST_THROW_EXCEPTION(createTypeError(
- "Type " + m_type->toString() + " is only valid in storage."
- ));
- }
- else if (visibility() >= Visibility::Public && !FunctionType(*this).externalType())
- BOOST_THROW_EXCEPTION(createTypeError("Internal type is not allowed for public state variables."));
+bool VariableDeclaration::isLValue() const
+{
+ // External function parameters and constant declared variables are Read-Only
+ return !isExternalCallableParameter() && !m_isConstant;
}
bool VariableDeclaration::isCallableParameter() const
@@ -657,578 +286,62 @@ bool VariableDeclaration::isExternalCallableParameter() const
return false;
}
-TypePointer ModifierDefinition::type(ContractDefinition const*) const
-{
- return make_shared<ModifierType>(*this);
-}
-
-void ModifierDefinition::checkTypeRequirements()
-{
- m_body->checkTypeRequirements();
-}
-
-void ModifierInvocation::checkTypeRequirements(vector<ContractDefinition const*> const& _bases)
-{
- TypePointers argumentTypes;
- for (ASTPointer<Expression> const& argument: m_arguments)
- {
- argument->checkTypeRequirements(nullptr);
- argumentTypes.push_back(argument->type());
- }
- m_modifierName->checkTypeRequirements(&argumentTypes);
-
- auto const* declaration = &m_modifierName->referencedDeclaration();
- vector<ASTPointer<VariableDeclaration>> emptyParameterList;
- vector<ASTPointer<VariableDeclaration>> const* parameters = nullptr;
- if (auto modifier = dynamic_cast<ModifierDefinition const*>(declaration))
- parameters = &modifier->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)
- BOOST_THROW_EXCEPTION(createTypeError("Referenced declaration is neither modifier nor base class."));
- if (parameters->size() != m_arguments.size())
- BOOST_THROW_EXCEPTION(createTypeError(
- "Wrong argument count for modifier invocation: " +
- toString(m_arguments.size()) +
- " arguments given but expected " +
- toString(parameters->size()) +
- "."
- ));
- for (size_t i = 0; i < m_arguments.size(); ++i)
- if (!m_arguments[i]->type()->isImplicitlyConvertibleTo(*(*parameters)[i]->type()))
- BOOST_THROW_EXCEPTION(m_arguments[i]->createTypeError(
- "Invalid type for argument in modifier invocation. "
- "Invalid implicit conversion from " +
- m_arguments[i]->type()->toString() +
- " to " +
- (*parameters)[i]->type()->toString() +
- " requested."
- ));
-}
-
-void EventDefinition::checkTypeRequirements()
-{
- int numIndexed = 0;
- for (ASTPointer<VariableDeclaration> const& var: parameters())
- {
- if (var->isIndexed())
- numIndexed++;
- if (numIndexed > 3)
- BOOST_THROW_EXCEPTION(createTypeError("More than 3 indexed arguments for event."));
- if (!var->type()->canLiveOutsideStorage())
- BOOST_THROW_EXCEPTION(var->createTypeError("Type is required to live outside storage."));
- if (!var->type()->externalType())
- BOOST_THROW_EXCEPTION(var->createTypeError("Internal type is not allowed as event parameter type."));
- }
-}
-
-void Block::checkTypeRequirements()
-{
- for (shared_ptr<Statement> const& statement: m_statements)
- statement->checkTypeRequirements();
-}
-
-void IfStatement::checkTypeRequirements()
-{
- m_condition->expectType(BoolType());
- m_trueBody->checkTypeRequirements();
- if (m_falseBody)
- m_falseBody->checkTypeRequirements();
-}
-
-void WhileStatement::checkTypeRequirements()
-{
- m_condition->expectType(BoolType());
- m_body->checkTypeRequirements();
-}
-
-void ForStatement::checkTypeRequirements()
-{
- if (m_initExpression)
- m_initExpression->checkTypeRequirements();
- if (m_condExpression)
- m_condExpression->expectType(BoolType());
- if (m_loopExpression)
- m_loopExpression->checkTypeRequirements();
- m_body->checkTypeRequirements();
-}
-
-void Return::checkTypeRequirements()
+bool VariableDeclaration::canHaveAutoType() const
{
- if (!m_expression)
- return;
- if (!m_returnParameters)
- BOOST_THROW_EXCEPTION(createTypeError("Return arguments not allowed."));
- if (m_returnParameters->parameters().size() != 1)
- BOOST_THROW_EXCEPTION(createTypeError(
- "Different number of arguments in return statement "
- "than in returns declaration."
- ));
- // this could later be changed such that the paramaters type is an anonymous struct type,
- // but for now, we only allow one return parameter
- m_expression->expectType(*m_returnParameters->parameters().front()->type());
-}
-
-void VariableDeclarationStatement::checkTypeRequirements()
-{
- m_variable->checkTypeRequirements();
-}
-
-void Assignment::checkTypeRequirements(TypePointers const*)
-{
- m_leftHandSide->checkTypeRequirements(nullptr);
- m_leftHandSide->requireLValue();
- if (m_leftHandSide->type()->category() == Type::Category::Mapping)
- BOOST_THROW_EXCEPTION(createTypeError("Mappings cannot be assigned to."));
- m_type = m_leftHandSide->type();
- if (m_assigmentOperator == Token::Assign)
- m_rightHandSide->expectType(*m_type);
- else
- {
- // compound assignment
- m_rightHandSide->checkTypeRequirements(nullptr);
- TypePointer resultType = m_type->binaryOperatorResult(Token::AssignmentToBinaryOp(m_assigmentOperator),
- m_rightHandSide->type());
- if (!resultType || *resultType != *m_type)
- BOOST_THROW_EXCEPTION(createTypeError(
- "Operator " +
- string(Token::toString(m_assigmentOperator)) +
- " not compatible with types " +
- m_type->toString() +
- " and " +
- m_rightHandSide->type()->toString()
- ));
- }
-}
-
-void ExpressionStatement::checkTypeRequirements()
-{
- m_expression->checkTypeRequirements(nullptr);
- if (m_expression->type()->category() == Type::Category::IntegerConstant)
- if (!dynamic_pointer_cast<IntegerConstantType const>(m_expression->type())->integerType())
- BOOST_THROW_EXCEPTION(m_expression->createTypeError("Invalid integer constant."));
-}
-
-void Expression::expectType(Type const& _expectedType)
-{
- checkTypeRequirements(nullptr);
- Type const& currentType = *type();
- if (!currentType.isImplicitlyConvertibleTo(_expectedType))
- BOOST_THROW_EXCEPTION(
- createTypeError(
- "Type " +
- currentType.toString() +
- " is not implicitly convertible to expected type " +
- _expectedType.toString() +
- "."
- )
- );
-}
-
-void Expression::requireLValue()
-{
- if (!isLValue())
- BOOST_THROW_EXCEPTION(createTypeError("Expression has to be an lvalue."));
- m_lvalueRequested = true;
-}
-
-void UnaryOperation::checkTypeRequirements(TypePointers const*)
-{
- // Inc, Dec, Add, Sub, Not, BitNot, Delete
- m_subExpression->checkTypeRequirements(nullptr);
- if (m_operator == Token::Value::Inc || m_operator == Token::Value::Dec || m_operator == Token::Value::Delete)
- m_subExpression->requireLValue();
- m_type = m_subExpression->type()->unaryOperatorResult(m_operator);
- if (!m_type)
- BOOST_THROW_EXCEPTION(createTypeError("Unary operator not compatible with type."));
-}
-
-void BinaryOperation::checkTypeRequirements(TypePointers const*)
-{
- m_left->checkTypeRequirements(nullptr);
- m_right->checkTypeRequirements(nullptr);
- m_commonType = m_left->type()->binaryOperatorResult(m_operator, m_right->type());
- if (!m_commonType)
- BOOST_THROW_EXCEPTION(createTypeError(
- "Operator " + string(Token::toString(m_operator)) +
- " not compatible with types " +
- m_left->type()->toString() +
- " and " +
- m_right->type()->toString()
- ));
- m_type = Token::isCompareOp(m_operator) ? make_shared<BoolType>() : m_commonType;
-}
-
-void FunctionCall::checkTypeRequirements(TypePointers const*)
-{
- bool isPositionalCall = m_names.empty();
-
- // we need to check arguments' type first as they will be forwarded to
- // m_expression->checkTypeRequirements
- TypePointers argumentTypes;
- for (ASTPointer<Expression> const& argument: m_arguments)
- {
- argument->checkTypeRequirements(nullptr);
- // only store them for positional calls
- if (isPositionalCall)
- argumentTypes.push_back(argument->type());
- }
-
- m_expression->checkTypeRequirements(isPositionalCall ? &argumentTypes : nullptr);
-
- TypePointer const& expressionType = m_expression->type();
- FunctionTypePointer functionType;
- if (isTypeConversion())
- {
- TypeType const& type = dynamic_cast<TypeType const&>(*expressionType);
- if (m_arguments.size() != 1)
- BOOST_THROW_EXCEPTION(createTypeError("Exactly one argument expected for explicit type conversion."));
- if (!isPositionalCall)
- BOOST_THROW_EXCEPTION(createTypeError("Type conversion cannot allow named arguments."));
- m_type = type.actualType();
- auto argType = m_arguments.front()->type();
- 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)
- m_type = ReferenceType::copyForLocationIfReference(argRefType->location(), m_type);
- if (!argType->isExplicitlyConvertibleTo(*m_type))
- BOOST_THROW_EXCEPTION(createTypeError("Explicit type conversion not allowed."));
-
- return;
- }
-
- /// For error message: Struct members that were removed during conversion to memory.
- set<string> membersRemovedForStructConstructor;
- if (isStructConstructorCall())
- {
- TypeType const& type = dynamic_cast<TypeType const&>(*expressionType);
- auto const& structType = dynamic_cast<StructType const&>(*type.actualType());
- functionType = structType.constructorType();
- membersRemovedForStructConstructor = structType.membersMissingInMemory();
- }
- else
- functionType = dynamic_pointer_cast<FunctionType const>(expressionType);
-
- if (!functionType)
- BOOST_THROW_EXCEPTION(createTypeError("Type is not callable."));
-
- //@todo would be nice to create a struct type from the arguments
- // and then ask if that is implicitly convertible to the struct represented by the
- // function parameters
- TypePointers const& parameterTypes = functionType->parameterTypes();
- if (!functionType->takesArbitraryParameters() && parameterTypes.size() != m_arguments.size())
- {
- string msg =
- "Wrong argument count for function call: " +
- toString(m_arguments.size()) +
- " arguments given but expected " +
- toString(parameterTypes.size()) +
- ".";
- // Extend error message in case we try to construct a struct with mapping member.
- if (isStructConstructorCall() && !membersRemovedForStructConstructor.empty())
- {
- msg += " Members that have to be skipped in memory:";
- for (auto const& member: membersRemovedForStructConstructor)
- msg += " " + member;
- }
- BOOST_THROW_EXCEPTION(createTypeError(msg));
- }
-
- if (isPositionalCall)
- {
- // call by positional arguments
- for (size_t i = 0; i < m_arguments.size(); ++i)
- if (
- !functionType->takesArbitraryParameters() &&
- !m_arguments[i]->type()->isImplicitlyConvertibleTo(*parameterTypes[i])
- )
- BOOST_THROW_EXCEPTION(m_arguments[i]->createTypeError(
- "Invalid type for argument in function call. "
- "Invalid implicit conversion from " +
- m_arguments[i]->type()->toString() +
- " to " +
- parameterTypes[i]->toString() +
- " requested."
- ));
- }
- else
- {
- // call by named arguments
- if (functionType->takesArbitraryParameters())
- BOOST_THROW_EXCEPTION(createTypeError(
- "Named arguments cannnot be used for functions that take arbitrary parameters."
- ));
- auto const& parameterNames = functionType->parameterNames();
- if (parameterNames.size() != m_names.size())
- BOOST_THROW_EXCEPTION(createTypeError("Some argument names are missing."));
-
- // check duplicate names
- for (size_t i = 0; i < m_names.size(); i++)
- for (size_t j = i + 1; j < m_names.size(); j++)
- if (*m_names[i] == *m_names[j])
- BOOST_THROW_EXCEPTION(m_arguments[i]->createTypeError("Duplicate named argument."));
-
- for (size_t i = 0; i < m_names.size(); i++) {
- bool found = false;
- for (size_t j = 0; j < parameterNames.size(); j++) {
- if (parameterNames[j] == *m_names[i]) {
- // check type convertible
- if (!m_arguments[i]->type()->isImplicitlyConvertibleTo(*parameterTypes[j]))
- BOOST_THROW_EXCEPTION(m_arguments[i]->createTypeError(
- "Invalid type for argument in function call. "
- "Invalid implicit conversion from " +
- m_arguments[i]->type()->toString() +
- " to " +
- parameterTypes[i]->toString() +
- " requested."
- ));
-
- found = true;
- break;
- }
- }
- if (!found)
- BOOST_THROW_EXCEPTION(createTypeError("Named argument does not match function declaration."));
- }
- }
-
- // @todo actually the return type should be an anonymous struct,
- // but we change it to the type of the first return value until we have anonymous
- // structs and tuples
- if (functionType->returnParameterTypes().empty())
- m_type = make_shared<VoidType>();
- else
- m_type = functionType->returnParameterTypes().front();
-}
-
-bool FunctionCall::isTypeConversion() const
-{
- return m_expression->type()->category() == Type::Category::TypeType && !isStructConstructorCall();
-}
-
-bool FunctionCall::isStructConstructorCall() const
-{
- if (auto const* type = dynamic_cast<TypeType const*>(m_expression->type().get()))
- return type->actualType()->category() == Type::Category::Struct;
- else
- return false;
+ auto const* callable = dynamic_cast<CallableDeclaration const*>(scope());
+ return (!!callable && !isCallableParameter());
}
-void NewExpression::checkTypeRequirements(TypePointers const*)
+TypePointer VariableDeclaration::type(ContractDefinition const*) const
{
- m_contractName->checkTypeRequirements(nullptr);
- m_contract = dynamic_cast<ContractDefinition const*>(&m_contractName->referencedDeclaration());
-
- if (!m_contract)
- BOOST_THROW_EXCEPTION(createTypeError("Identifier is not a contract."));
- if (!m_contract->isFullyImplemented())
- BOOST_THROW_EXCEPTION(createTypeError("Trying to create an instance of an abstract contract."));
-
- auto scopeContract = m_contractName->contractScope();
- auto bases = m_contract->linearizedBaseContracts();
- if (find(bases.begin(), bases.end(), scopeContract) != bases.end())
- BOOST_THROW_EXCEPTION(createTypeError("Circular reference for contract creation: cannot create instance of derived or same contract."));
-
- shared_ptr<ContractType const> contractType = make_shared<ContractType>(*m_contract);
- TypePointers const& parameterTypes = contractType->constructorType()->parameterTypes();
- m_type = make_shared<FunctionType>(
- parameterTypes,
- TypePointers{contractType},
- strings(),
- strings(),
- FunctionType::Location::Creation
- );
+ return annotation().type;
}
-void MemberAccess::checkTypeRequirements(TypePointers const* _argumentTypes)
+VariableDeclarationAnnotation& VariableDeclaration::annotation() const
{
- m_expression->checkTypeRequirements(nullptr);
- Type const& type = *m_expression->type();
-
- MemberList::MemberMap possibleMembers = type.members().membersByName(*m_memberName);
- if (possibleMembers.size() > 1 && _argumentTypes)
- {
- // do override 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,
- m_expression->type()
- );
- if (!storageType->members().membersByName(*m_memberName).empty())
- BOOST_THROW_EXCEPTION(createTypeError(
- "Member \"" +
- *m_memberName +
- "\" is not available in " +
- type.toString() +
- " outside of storage."
- ));
- BOOST_THROW_EXCEPTION(createTypeError(
- "Member \"" +
- *m_memberName +
- "\" not found or not visible after argument-dependent lookup in " +
- type.toString()
- ));
- }
- else if (possibleMembers.size() > 1)
- BOOST_THROW_EXCEPTION(createTypeError(
- "Member \"" +
- *m_memberName +
- "\" not unique after argument-dependent lookup in " +
- type.toString()
- ));
-
- m_referencedDeclaration = possibleMembers.front().declaration;
- m_type = possibleMembers.front().type;
- if (type.category() == Type::Category::Struct)
- m_isLValue = true;
- else if (type.category() == Type::Category::Array)
- {
- auto const& arrayType(dynamic_cast<ArrayType const&>(type));
- m_isLValue = (
- *m_memberName == "length" &&
- arrayType.location() == DataLocation::Storage &&
- arrayType.isDynamicallySized()
- );
- }
- else
- m_isLValue = false;
+ if (!m_annotation)
+ m_annotation = new VariableDeclarationAnnotation();
+ return static_cast<VariableDeclarationAnnotation&>(*m_annotation);
}
-void IndexAccess::checkTypeRequirements(TypePointers const*)
+ReturnAnnotation& Return::annotation() const
{
- m_base->checkTypeRequirements(nullptr);
- switch (m_base->type()->category())
- {
- case Type::Category::Array:
- {
- ArrayType const& type = dynamic_cast<ArrayType const&>(*m_base->type());
- if (!m_index)
- BOOST_THROW_EXCEPTION(createTypeError("Index expression cannot be omitted."));
- if (type.isString())
- BOOST_THROW_EXCEPTION(createTypeError("Index access for string is not possible."));
- m_index->expectType(IntegerType(256));
-
- m_type = type.baseType();
- if (auto integerType = dynamic_cast<IntegerConstantType const*>(m_index->type().get()))
- if (!type.isDynamicallySized() && type.length() <= integerType->literalValue(nullptr))
- BOOST_THROW_EXCEPTION(createTypeError("Out of bounds access."));
-
- m_isLValue = type.location() != DataLocation::CallData;
- break;
- }
- case Type::Category::Mapping:
- {
- MappingType const& type = dynamic_cast<MappingType const&>(*m_base->type());
- if (!m_index)
- BOOST_THROW_EXCEPTION(createTypeError("Index expression cannot be omitted."));
- m_index->expectType(*type.keyType());
- m_type = type.valueType();
- m_isLValue = true;
- break;
- }
- case Type::Category::TypeType:
- {
- TypeType const& type = dynamic_cast<TypeType const&>(*m_base->type());
- if (!m_index)
- m_type = make_shared<TypeType>(make_shared<ArrayType>(DataLocation::Memory, type.actualType()));
- else
- {
- m_index->checkTypeRequirements(nullptr);
- auto length = dynamic_cast<IntegerConstantType const*>(m_index->type().get());
- if (!length)
- BOOST_THROW_EXCEPTION(m_index->createTypeError("Integer constant expected."));
- m_type = make_shared<TypeType>(make_shared<ArrayType>(
- DataLocation::Memory,
- type.actualType(),
- length->literalValue(nullptr)
- ));
- }
- break;
- }
- default:
- BOOST_THROW_EXCEPTION(m_base->createTypeError(
- "Indexed expression has to be a type, mapping or array (is " +
- m_base->type()->toString() +
- ")"
- ));
- }
+ if (!m_annotation)
+ m_annotation = new ReturnAnnotation();
+ return static_cast<ReturnAnnotation&>(*m_annotation);
}
-void Identifier::checkTypeRequirements(TypePointers const* _argumentTypes)
+ExpressionAnnotation& Expression::annotation() const
{
- if (!m_referencedDeclaration)
- {
- if (!_argumentTypes)
- BOOST_THROW_EXCEPTION(createTypeError("Unable to determine overloaded type."));
- overloadResolution(*_argumentTypes);
- }
- solAssert(!!m_referencedDeclaration, "Referenced declaration is null after overload resolution.");
- m_isLValue = m_referencedDeclaration->isLValue();
- m_type = m_referencedDeclaration->type(m_contractScope);
- if (!m_type)
- BOOST_THROW_EXCEPTION(createTypeError("Declaration referenced before type could be determined."));
+ if (!m_annotation)
+ m_annotation = new ExpressionAnnotation();
+ return static_cast<ExpressionAnnotation&>(*m_annotation);
}
-Declaration const& Identifier::referencedDeclaration() const
+MemberAccessAnnotation& MemberAccess::annotation() const
{
- solAssert(!!m_referencedDeclaration, "Identifier not resolved.");
- return *m_referencedDeclaration;
+ if (!m_annotation)
+ m_annotation = new MemberAccessAnnotation();
+ return static_cast<MemberAccessAnnotation&>(*m_annotation);
}
-void Identifier::overloadResolution(TypePointers const& _argumentTypes)
+BinaryOperationAnnotation& BinaryOperation::annotation() const
{
- solAssert(!m_referencedDeclaration, "Referenced declaration should be null before overload resolution.");
- solAssert(!m_overloadedDeclarations.empty(), "No candidates for overload resolution found.");
-
- vector<Declaration const*> possibles;
- if (m_overloadedDeclarations.size() == 1)
- m_referencedDeclaration = *m_overloadedDeclarations.begin();
-
- for (Declaration const* declaration: m_overloadedDeclarations)
- {
- TypePointer const& function = declaration->type();
- auto const* functionType = dynamic_cast<FunctionType const*>(function.get());
- if (functionType && functionType->canTakeArguments(_argumentTypes))
- possibles.push_back(declaration);
- }
- if (possibles.size() == 1)
- m_referencedDeclaration = possibles.front();
- else if (possibles.empty())
- BOOST_THROW_EXCEPTION(createTypeError("No matching declaration found after argument-dependent lookup."));
- else
- BOOST_THROW_EXCEPTION(createTypeError("No unique declaration found after argument-dependent lookup."));
+ if (!m_annotation)
+ m_annotation = new BinaryOperationAnnotation();
+ return static_cast<BinaryOperationAnnotation&>(*m_annotation);
}
-void ElementaryTypeNameExpression::checkTypeRequirements(TypePointers const*)
+FunctionCallAnnotation& FunctionCall::annotation() const
{
- m_type = make_shared<TypeType>(Type::fromElementaryTypeName(m_typeToken));
+ if (!m_annotation)
+ m_annotation = new FunctionCallAnnotation();
+ return static_cast<FunctionCallAnnotation&>(*m_annotation);
}
-void Literal::checkTypeRequirements(TypePointers const*)
+IdentifierAnnotation& Identifier::annotation() const
{
- m_type = Type::forLiteral(*this);
- if (!m_type)
- BOOST_THROW_EXCEPTION(createTypeError("Invalid literal value."));
-}
-
-}
+ if (!m_annotation)
+ m_annotation = new IdentifierAnnotation();
+ return static_cast<IdentifierAnnotation&>(*m_annotation);
}
diff --git a/libsolidity/AST.h b/libsolidity/AST.h
index ba529a8a..134ca148 100644
--- a/libsolidity/AST.h
+++ b/libsolidity/AST.h
@@ -33,6 +33,7 @@
#include <libsolidity/Token.h>
#include <libsolidity/Types.h>
#include <libsolidity/Exceptions.h>
+#include <libsolidity/ASTAnnotations.h>
namespace dev
{
@@ -51,9 +52,8 @@ class ASTConstVisitor;
class ASTNode: private boost::noncopyable
{
public:
- explicit ASTNode(SourceLocation const& _location): m_location(_location) {}
-
- virtual ~ASTNode() {}
+ explicit ASTNode(SourceLocation const& _location);
+ virtual ~ASTNode();
virtual void accept(ASTVisitor& _visitor) = 0;
virtual void accept(ASTConstVisitor& _visitor) const = 0;
@@ -77,6 +77,9 @@ public:
/// the given description
TypeError createTypeError(std::string const& _description) const;
+ ///@todo make this const-safe by providing a different way to access the annotation
+ virtual ASTAnnotation& annotation() const;
+
///@{
///@name equality operators
/// Equality relies on the fact that nodes cannot be copied.
@@ -84,6 +87,10 @@ public:
bool operator!=(ASTNode const& _other) const { return !operator==(_other); }
///@}
+protected:
+ /// Annotation - is specialised in derived classes, is created upon request (because of polymorphism).
+ mutable ASTAnnotation* m_annotation = nullptr;
+
private:
SourceLocation m_location;
};
@@ -151,12 +158,14 @@ public:
Declaration const* scope() const { return m_scope; }
void setScope(Declaration const* _scope) { m_scope = _scope; }
+ virtual bool isLValue() const { return false; }
+ virtual bool isPartOfExternalInterface() const { return false; }
+
/// @returns the type of expressions referencing this declaration.
/// The current contract has to be given since this context can change the type, especially of
/// contract types.
+ /// This can only be called once types of variable declarations have already been resolved.
virtual TypePointer type(ContractDefinition const* m_currentContract = nullptr) const = 0;
- virtual bool isLValue() const { return false; }
- virtual bool isPartOfExternalInterface() const { return false; }
protected:
virtual Visibility defaultVisibility() const { return Visibility::Public; }
@@ -205,8 +214,7 @@ public:
explicit ImplementationOptional(bool _implemented): m_implemented(_implemented) {}
/// @return whether this node is fully implemented or not
- bool isFullyImplemented() const { return m_implemented; }
- void setFullyImplemented(bool _implemented) { m_implemented = _implemented; }
+ bool isImplemented() const { return m_implemented; }
protected:
bool m_implemented;
@@ -219,7 +227,7 @@ protected:
* document order. It first visits all struct declarations, then all variable declarations and
* finally all function declarations.
*/
-class ContractDefinition: public Declaration, public Documented, public ImplementationOptional
+class ContractDefinition: public Declaration, public Documented
{
public:
ContractDefinition(
@@ -237,7 +245,6 @@ public:
):
Declaration(_location, _name),
Documented(_documentation),
- ImplementationOptional(true),
m_baseContracts(_baseContracts),
m_definedStructs(_definedStructs),
m_definedEnums(_definedEnums),
@@ -261,24 +268,14 @@ public:
std::vector<ASTPointer<EventDefinition>> const& interfaceEvents() const;
bool isLibrary() const { return m_isLibrary; }
- virtual TypePointer type(ContractDefinition const* m_currentContract) const override;
-
- /// Checks that there are no illegal overrides, that the constructor does not have a "returns"
- /// and calls checkTypeRequirements on all its functions.
- void checkTypeRequirements();
-
/// @returns a map of canonical function signatures to FunctionDefinitions
/// as intended for use by the ABI.
std::map<FixedHash<4>, FunctionTypePointer> interfaceFunctions() const;
+ std::vector<std::pair<FixedHash<4>, FunctionTypePointer>> const& interfaceFunctionList() const;
/// @returns a list of the inheritable members of this contract
std::vector<Declaration const*> const& inheritableMembers() const;
- /// List of all (direct and indirect) base contracts in order from derived to base, including
- /// the contract itself. Available after name resolution
- std::vector<ContractDefinition const*> const& linearizedBaseContracts() const { return m_linearizedBaseContracts; }
- void setLinearizedBaseContracts(std::vector<ContractDefinition const*> const& _bases) { m_linearizedBaseContracts = _bases; }
-
/// Returns the constructor or nullptr if no constructor was specified.
FunctionDefinition const* constructor() const;
/// Returns the fallback function or nullptr if no fallback function was specified.
@@ -290,21 +287,11 @@ public:
std::string const& devDocumentation() const;
void setDevDocumentation(std::string const& _devDocumentation);
-private:
- /// Checks that two functions defined in this contract with the same name have different
- /// arguments and that there is at most one constructor.
- void checkDuplicateFunctions() const;
- void checkIllegalOverrides() const;
- void checkAbstractFunctions();
- void checkAbstractConstructors();
- /// Checks that different functions with external visibility end up having different
- /// external argument types (i.e. different signature).
- void checkExternalTypeClashes() const;
- /// Checks that all requirements for a library are fulfilled if this is a library.
- void checkLibraryRequirements() const;
+ virtual TypePointer type(ContractDefinition const* m_currentContract) const override;
- std::vector<std::pair<FixedHash<4>, FunctionTypePointer>> const& interfaceFunctionList() const;
+ virtual ContractDefinitionAnnotation& annotation() const override;
+private:
std::vector<ASTPointer<InheritanceSpecifier>> m_baseContracts;
std::vector<ASTPointer<StructDefinition>> m_definedStructs;
std::vector<ASTPointer<EnumDefinition>> m_definedEnums;
@@ -327,18 +314,19 @@ private:
class InheritanceSpecifier: public ASTNode
{
public:
- InheritanceSpecifier(SourceLocation const& _location, ASTPointer<Identifier> const& _baseName,
- std::vector<ASTPointer<Expression>> _arguments):
+ InheritanceSpecifier(
+ SourceLocation const& _location,
+ ASTPointer<Identifier> const& _baseName,
+ std::vector<ASTPointer<Expression>> _arguments
+ ):
ASTNode(_location), m_baseName(_baseName), m_arguments(_arguments) {}
virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override;
- ASTPointer<Identifier> const& name() const { return m_baseName; }
+ Identifier const& name() const { return *m_baseName; }
std::vector<ASTPointer<Expression>> const& arguments() const { return m_arguments; }
- void checkTypeRequirements();
-
private:
ASTPointer<Identifier> m_baseName;
std::vector<ASTPointer<Expression>> m_arguments;
@@ -359,32 +347,27 @@ public:
std::vector<ASTPointer<VariableDeclaration>> const& members() const { return m_members; }
- virtual TypePointer type(ContractDefinition const*) const override;
-
- /// Checks that the members do not include any recursive structs and have valid types
- /// (e.g. no functions).
- void checkValidityOfMembers() const;
+ virtual TypePointer type(ContractDefinition const* m_currentContract) const override;
private:
- void checkMemberTypes() const;
- void checkRecursion() const;
-
std::vector<ASTPointer<VariableDeclaration>> m_members;
};
class EnumDefinition: public Declaration
{
public:
- EnumDefinition(SourceLocation const& _location,
- ASTPointer<ASTString> const& _name,
- std::vector<ASTPointer<EnumValue>> const& _members):
+ EnumDefinition(
+ SourceLocation const& _location,
+ ASTPointer<ASTString> const& _name,
+ std::vector<ASTPointer<EnumValue>> const& _members
+ ):
Declaration(_location, _name), m_members(_members) {}
virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override;
std::vector<ASTPointer<EnumValue>> const& members() const { return m_members; }
- virtual TypePointer type(ContractDefinition const*) const override;
+ virtual TypePointer type(ContractDefinition const* m_currentContract) const override;
private:
std::vector<ASTPointer<EnumValue>> m_members;
@@ -395,14 +378,14 @@ private:
*/
class EnumValue: public Declaration
{
- public:
- EnumValue(SourceLocation const& _location,
- ASTPointer<ASTString> const& _name):
+public:
+ EnumValue(SourceLocation const& _location, ASTPointer<ASTString> const& _name):
Declaration(_location, _name) {}
virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override;
- virtual TypePointer type(ContractDefinition const* = nullptr) const override;
+
+ virtual TypePointer type(ContractDefinition const* m_currentContract) const override;
};
/**
@@ -491,17 +474,15 @@ public:
{
return Declaration::isVisibleInContract() && !isConstructor() && !name().empty();
}
- virtual TypePointer type(ContractDefinition const*) const override;
virtual bool isPartOfExternalInterface() const override { return isPublic() && !m_isConstructor && !name().empty(); }
- /// Checks that all parameters have allowed types and calls checkTypeRequirements on the body.
- void checkTypeRequirements();
-
/// @returns the external signature of the function
/// That consists of the name of the function followed by the types of the
/// arguments separated by commas all enclosed in parentheses without any spaces.
std::string externalSignature() const;
+ virtual TypePointer type(ContractDefinition const* m_currentContract) const override;
+
private:
bool m_isConstructor;
bool m_isDeclaredConst;
@@ -540,28 +521,29 @@ public:
virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override;
- TypeName* typeName() { return m_typeName.get(); }
+ TypeName* typeName() const { return m_typeName.get(); }
ASTPointer<Expression> const& value() const { return m_value; }
- /// Returns the declared or inferred type. Can be an empty pointer if no type was explicitly
- /// declared and there is no assignment to the variable that fixes the type.
- TypePointer type(ContractDefinition const* = nullptr) const override { return m_type; }
- void setType(std::shared_ptr<Type const> const& _type) { m_type = _type; }
-
virtual bool isLValue() const override;
virtual bool isPartOfExternalInterface() const override { return isPublic(); }
- void checkTypeRequirements();
bool isLocalVariable() const { return !!dynamic_cast<FunctionDefinition const*>(scope()); }
/// @returns true if this variable is a parameter or return parameter of a function.
bool isCallableParameter() const;
/// @returns true if this variable is a parameter (not return parameter) of an external function.
bool isExternalCallableParameter() const;
+ /// @returns true if the type of the variable does not need to be specified, i.e. it is declared
+ /// in the body of a function or modifier.
+ bool canHaveAutoType() const;
bool isStateVariable() const { return m_isStateVariable; }
bool isIndexed() const { return m_isIndexed; }
bool isConstant() const { return m_isConstant; }
Location referenceLocation() const { return m_location; }
+ virtual TypePointer type(ContractDefinition const* m_currentContract) const override;
+
+ virtual VariableDeclarationAnnotation& annotation() const override;
+
protected:
Visibility defaultVisibility() const override { return Visibility::Internal; }
@@ -572,8 +554,6 @@ private:
bool m_isIndexed; ///< Whether this is an indexed variable (used by events).
bool m_isConstant; ///< Whether the variable is a compile-time constant.
Location m_location; ///< Location of the variable if it is of reference type.
-
- std::shared_ptr<Type const> m_type; ///< derived type, initially empty
};
/**
@@ -600,9 +580,7 @@ public:
Block const& body() const { return *m_body; }
- virtual TypePointer type(ContractDefinition const* = nullptr) const override;
-
- void checkTypeRequirements();
+ virtual TypePointer type(ContractDefinition const* m_currentContract) const override;
private:
ASTPointer<Block> m_body;
@@ -614,8 +592,11 @@ private:
class ModifierInvocation: public ASTNode
{
public:
- ModifierInvocation(SourceLocation const& _location, ASTPointer<Identifier> const& _name,
- std::vector<ASTPointer<Expression>> _arguments):
+ ModifierInvocation(
+ SourceLocation const& _location,
+ ASTPointer<Identifier> const& _name,
+ std::vector<ASTPointer<Expression>> _arguments
+ ):
ASTNode(_location), m_modifierName(_name), m_arguments(_arguments) {}
virtual void accept(ASTVisitor& _visitor) override;
@@ -624,9 +605,6 @@ public:
ASTPointer<Identifier> const& name() const { return m_modifierName; }
std::vector<ASTPointer<Expression>> const& arguments() const { return m_arguments; }
- /// @param _bases is the list of base contracts for base constructor calls. For modifiers an empty vector should be passed.
- void checkTypeRequirements(std::vector<ContractDefinition const*> const& _bases);
-
private:
ASTPointer<Identifier> m_modifierName;
std::vector<ASTPointer<Expression>> m_arguments;
@@ -656,12 +634,7 @@ public:
bool isAnonymous() const { return m_anonymous; }
- virtual TypePointer type(ContractDefinition const* = nullptr) const override
- {
- return std::make_shared<FunctionType>(*this);
- }
-
- void checkTypeRequirements();
+ virtual TypePointer type(ContractDefinition const* m_currentContract) const override;
private:
bool m_anonymous = false;
@@ -681,7 +654,7 @@ public:
virtual void accept(ASTConstVisitor&) const override { BOOST_THROW_EXCEPTION(InternalCompilerError()
<< errinfo_comment("MagicVariableDeclaration used inside real AST.")); }
- virtual TypePointer type(ContractDefinition const* = nullptr) const override { return m_type; }
+ virtual TypePointer type(ContractDefinition const*) const override { return m_type; }
private:
std::shared_ptr<Type const> m_type;
@@ -700,10 +673,7 @@ public:
virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override;
- /// Retrieve the element of the type hierarchy this node refers to. Can return an empty shared
- /// pointer until the types have been resolved using the @ref NameAndTypeResolver.
- /// If it returns an empty shared pointer after that, this indicates that the type was not found.
- virtual std::shared_ptr<Type const> toType() = 0;
+ virtual TypeNameAnnotation& annotation() const override;
};
/**
@@ -720,7 +690,6 @@ public:
}
virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override;
- virtual std::shared_ptr<Type const> toType() override { return Type::fromElementaryTypeName(m_type); }
Token::Value typeName() const { return m_type; }
@@ -735,19 +704,16 @@ class UserDefinedTypeName: public TypeName
{
public:
UserDefinedTypeName(SourceLocation const& _location, ASTPointer<ASTString> const& _name):
- TypeName(_location), m_name(_name), m_referencedDeclaration(nullptr) {}
+ TypeName(_location), m_name(_name) {}
virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override;
- virtual std::shared_ptr<Type const> toType() override { return Type::fromUserDefinedTypeName(*this); }
ASTString const& name() const { return *m_name; }
- void setReferencedDeclaration(Declaration const& _referencedDeclaration) { m_referencedDeclaration = &_referencedDeclaration; }
- Declaration const* referencedDeclaration() const { return m_referencedDeclaration; }
+
+ virtual UserDefinedTypeNameAnnotation& annotation() const override;
private:
ASTPointer<ASTString> m_name;
-
- Declaration const* m_referencedDeclaration;
};
/**
@@ -761,7 +727,6 @@ public:
TypeName(_location), m_keyType(_keyType), m_valueType(_valueType) {}
virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override;
- virtual TypePointer toType() override { return Type::fromMapping(*m_keyType, *m_valueType); }
ElementaryTypeName const& keyType() const { return *m_keyType; }
TypeName const& valueType() const { return *m_valueType; }
@@ -782,7 +747,6 @@ public:
TypeName(_location), m_baseType(_baseType), m_length(_length) {}
virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override;
- virtual std::shared_ptr<Type const> toType() override { return Type::fromArrayTypeName(*m_baseType, m_length.get()); }
TypeName const& baseType() const { return *m_baseType; }
Expression const* length() const { return m_length.get(); }
@@ -805,11 +769,6 @@ class Statement: public ASTNode
{
public:
explicit Statement(SourceLocation const& _location): ASTNode(_location) {}
-
- /// Check all type requirements, throws exception if some requirement is not met.
- /// This includes checking that operators are applicable to their arguments but also that
- /// the number of function call arguments matches the number of formal parameters and so forth.
- virtual void checkTypeRequirements() = 0;
};
/**
@@ -823,8 +782,6 @@ public:
virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override;
- virtual void checkTypeRequirements() override;
-
private:
std::vector<ASTPointer<Statement>> m_statements;
};
@@ -840,8 +797,6 @@ public:
virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override;
-
- virtual void checkTypeRequirements() override { }
};
/**
@@ -857,7 +812,6 @@ public:
m_condition(_condition), m_trueBody(_trueBody), m_falseBody(_falseBody) {}
virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override;
- virtual void checkTypeRequirements() override;
Expression const& condition() const { return *m_condition; }
Statement const& trueStatement() const { return *m_trueBody; }
@@ -887,7 +841,6 @@ public:
BreakableStatement(_location), m_condition(_condition), m_body(_body) {}
virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override;
- virtual void checkTypeRequirements() override;
Expression const& condition() const { return *m_condition; }
Statement const& body() const { return *m_body; }
@@ -903,19 +856,21 @@ private:
class ForStatement: public BreakableStatement
{
public:
- ForStatement(SourceLocation const& _location,
- ASTPointer<Statement> const& _initExpression,
- ASTPointer<Expression> const& _conditionExpression,
- ASTPointer<ExpressionStatement> const& _loopExpression,
- ASTPointer<Statement> const& _body):
+ ForStatement(
+ SourceLocation const& _location,
+ ASTPointer<Statement> const& _initExpression,
+ ASTPointer<Expression> const& _conditionExpression,
+ ASTPointer<ExpressionStatement> const& _loopExpression,
+ ASTPointer<Statement> const& _body
+ ):
BreakableStatement(_location),
m_initExpression(_initExpression),
m_condExpression(_conditionExpression),
m_loopExpression(_loopExpression),
- m_body(_body) {}
+ m_body(_body)
+ {}
virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override;
- virtual void checkTypeRequirements() override;
Statement const* initializationExpression() const { return m_initExpression.get(); }
Expression const* condition() const { return m_condExpression.get(); }
@@ -939,7 +894,6 @@ public:
Continue(SourceLocation const& _location): Statement(_location) {}
virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override;
- virtual void checkTypeRequirements() override {}
};
class Break: public Statement
@@ -948,27 +902,22 @@ public:
Break(SourceLocation const& _location): Statement(_location) {}
virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override;
- virtual void checkTypeRequirements() override {}
};
class Return: public Statement
{
public:
Return(SourceLocation const& _location, ASTPointer<Expression> _expression):
- Statement(_location), m_expression(_expression), m_returnParameters(nullptr) {}
+ Statement(_location), m_expression(_expression) {}
virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override;
- virtual void checkTypeRequirements() override;
- void setFunctionReturnParameters(ParameterList const* _parameters) { m_returnParameters = _parameters; }
- ParameterList const* functionReturnParameters() const { return m_returnParameters; }
Expression const* expression() const { return m_expression.get(); }
+ virtual ReturnAnnotation& annotation() const override;
+
private:
ASTPointer<Expression> m_expression; ///< value to return, optional
-
- /// Pointer to the parameter list of the function, filled by the @ref NameAndTypeResolver.
- ParameterList const* m_returnParameters;
};
/**
@@ -980,7 +929,6 @@ public:
Throw(SourceLocation const& _location): Statement(_location) {}
virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override;
- virtual void checkTypeRequirements() override {};
};
/**
@@ -995,7 +943,6 @@ public:
Statement(_location), m_variable(_variable) {}
virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override;
- virtual void checkTypeRequirements() override;
VariableDeclaration const& declaration() const { return *m_variable; }
Expression const* expression() const { return m_variable->value().get(); }
@@ -1014,7 +961,6 @@ public:
Statement(_location), m_expression(_expression) {}
virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override;
- virtual void checkTypeRequirements() override;
Expression const& expression() const { return *m_expression; }
@@ -1036,32 +982,8 @@ class Expression: public ASTNode
{
public:
Expression(SourceLocation const& _location): ASTNode(_location) {}
- /// Performs type checking after which m_type should be set.
- /// @arg _argumentTypes if set, provides the argument types for the case that this expression
- /// is used in the context of a call, used for function overload resolution.
- virtual void checkTypeRequirements(TypePointers const* _argumentTypes) = 0;
-
- std::shared_ptr<Type const> const& type() const { return m_type; }
- bool isLValue() const { return m_isLValue; }
-
- /// Helper function, infer the type via @ref checkTypeRequirements and then check that it
- /// is implicitly convertible to @a _expectedType. If not, throw exception.
- void expectType(Type const& _expectedType);
- /// Checks that this expression is an lvalue and also registers that an address and
- /// not a value is generated during compilation. Can be called after checkTypeRequirements()
- /// by an enclosing expression.
- void requireLValue();
- /// Returns true if @a requireLValue was previously called on this expression.
- bool lvalueRequested() const { return m_lvalueRequested; }
-protected:
- //! Inferred type of the expression, only filled after a call to checkTypeRequirements().
- std::shared_ptr<Type const> m_type;
- //! If this expression is an lvalue (i.e. something that can be assigned to).
- //! This is set during calls to @a checkTypeRequirements()
- bool m_isLValue = false;
- //! Whether the outer expression requested the address (true) or the value (false) of this expression.
- bool m_lvalueRequested = false;
+ ExpressionAnnotation& annotation() const override;
};
/// Assignment, can also be a compound assignment.
@@ -1069,16 +991,21 @@ protected:
class Assignment: public Expression
{
public:
- Assignment(SourceLocation const& _location, ASTPointer<Expression> const& _leftHandSide,
- Token::Value _assignmentOperator, ASTPointer<Expression> const& _rightHandSide):
- Expression(_location), m_leftHandSide(_leftHandSide),
- m_assigmentOperator(_assignmentOperator), m_rightHandSide(_rightHandSide)
+ Assignment(
+ SourceLocation const& _location,
+ ASTPointer<Expression> const& _leftHandSide,
+ Token::Value _assignmentOperator,
+ ASTPointer<Expression> const& _rightHandSide
+ ):
+ Expression(_location),
+ m_leftHandSide(_leftHandSide),
+ m_assigmentOperator(_assignmentOperator),
+ m_rightHandSide(_rightHandSide)
{
solAssert(Token::isAssignmentOp(_assignmentOperator), "");
}
virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override;
- virtual void checkTypeRequirements(TypePointers const* _argumentTypes) override;
Expression const& leftHandSide() const { return *m_leftHandSide; }
Token::Value assignmentOperator() const { return m_assigmentOperator; }
@@ -1106,7 +1033,6 @@ public:
}
virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override;
- virtual void checkTypeRequirements(TypePointers const* _argumentTypes) override;
Token::Value getOperator() const { return m_operator; }
bool isPrefixOperation() const { return m_isPrefix; }
@@ -1133,21 +1059,17 @@ public:
}
virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override;
- virtual void checkTypeRequirements(TypePointers const* _argumentTypes) override;
Expression const& leftExpression() const { return *m_left; }
Expression const& rightExpression() const { return *m_right; }
Token::Value getOperator() const { return m_operator; }
- Type const& commonType() const { return *m_commonType; }
+
+ BinaryOperationAnnotation& annotation() const override;
private:
ASTPointer<Expression> m_left;
Token::Value m_operator;
ASTPointer<Expression> m_right;
-
- /// The common type that is used for the operation, not necessarily the result type (e.g. for
- /// comparisons, this is always bool).
- std::shared_ptr<Type const> m_commonType;
};
/**
@@ -1161,17 +1083,12 @@ public:
Expression(_location), m_expression(_expression), m_arguments(_arguments), m_names(_names) {}
virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override;
- virtual void checkTypeRequirements(TypePointers const* _argumentTypes) override;
Expression const& expression() const { return *m_expression; }
std::vector<ASTPointer<Expression const>> arguments() const { return {m_arguments.begin(), m_arguments.end()}; }
std::vector<ASTPointer<ASTString>> const& names() const { return m_names; }
- /// @returns true if this is not an actual function call, but an explicit type conversion.
- /// Returns false for struct constructor calls.
- bool isTypeConversion() const;
- /// @return true if this is a constructor call for a struct, i.e. StructName(...).
- bool isStructConstructorCall() const;
+ virtual FunctionCallAnnotation& annotation() const override;
private:
ASTPointer<Expression> m_expression;
@@ -1189,15 +1106,11 @@ public:
Expression(_location), m_contractName(_contractName) {}
virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override;
- virtual void checkTypeRequirements(TypePointers const* _argumentTypes) override;
- /// Returns the referenced contract. Can only be called after type checking.
- ContractDefinition const* contract() const { solAssert(m_contract, ""); return m_contract; }
+ Identifier const& contractName() const { return *m_contractName; }
private:
ASTPointer<Identifier> m_contractName;
-
- ContractDefinition const* m_contract = nullptr;
};
/**
@@ -1213,18 +1126,12 @@ public:
virtual void accept(ASTConstVisitor& _visitor) const override;
Expression const& expression() const { return *m_expression; }
ASTString const& memberName() const { return *m_memberName; }
- /// @returns the declaration referenced by this expression. Might return nullptr even if the
- /// expression is valid, e.g. if the member does not correspond to an AST node.
- Declaration const* referencedDeclaration() const { return m_referencedDeclaration; }
- virtual void checkTypeRequirements(TypePointers const* _argumentTypes) override;
+
+ virtual MemberAccessAnnotation& annotation() const override;
private:
ASTPointer<Expression> m_expression;
ASTPointer<ASTString> m_memberName;
-
- /// Pointer to the referenced declaration, this is sometimes needed to resolve function over
- /// loads in the type-checking phase.
- Declaration const* m_referencedDeclaration = nullptr;
};
/**
@@ -1238,7 +1145,6 @@ public:
Expression(_location), m_base(_base), m_index(_index) {}
virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override;
- virtual void checkTypeRequirements(TypePointers const* _argumentTypes) override;
Expression const& baseExpression() const { return *m_base; }
Expression const* indexExpression() const { return m_index.get(); }
@@ -1268,44 +1174,13 @@ public:
PrimaryExpression(_location), m_name(_name) {}
virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override;
- virtual void checkTypeRequirements(TypePointers const* _argumentTypes) override;
ASTString const& name() const { return *m_name; }
- void setReferencedDeclaration(
- Declaration const& _referencedDeclaration,
- ContractDefinition const* _currentContract = nullptr
- )
- {
- m_referencedDeclaration = &_referencedDeclaration;
- m_contractScope = _currentContract;
- }
- Declaration const& referencedDeclaration() const;
-
- /// Stores a set of possible declarations referenced by this identifier. Has to be resolved
- /// providing argument types using overloadResolution before the referenced declaration
- /// is accessed.
- void setOverloadedDeclarations(std::vector<Declaration const*> const& _declarations)
- {
- m_overloadedDeclarations = _declarations;
- }
-
- /// Tries to find exactly one of the possible referenced declarations provided the given
- /// argument types in a call context.
- void overloadResolution(TypePointers const& _argumentTypes);
-
- ContractDefinition const* contractScope() const { return m_contractScope; }
+ virtual IdentifierAnnotation& annotation() const override;
private:
ASTPointer<ASTString> m_name;
-
- /// Declaration the name refers to.
- Declaration const* m_referencedDeclaration = nullptr;
- /// Stores a reference to the current contract. This is needed because types of base contracts
- /// change depending on the context.
- ContractDefinition const* m_contractScope = nullptr;
- /// A vector of overloaded declarations, right now only FunctionDefinition has overloaded declarations.
- std::vector<Declaration const*> m_overloadedDeclarations;
};
/**
@@ -1323,7 +1198,6 @@ public:
}
virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override;
- virtual void checkTypeRequirements(TypePointers const* _argumentTypes) override;
Token::Value typeToken() const { return m_typeToken; }
@@ -1357,7 +1231,6 @@ public:
PrimaryExpression(_location), m_token(_token), m_value(_value), m_subDenomination(_sub) {}
virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override;
- virtual void checkTypeRequirements(TypePointers const* _argumentTypes) override;
Token::Value token() const { return m_token; }
/// @returns the non-parsed value of the literal
diff --git a/libsolidity/ASTAnnotations.cpp b/libsolidity/ASTAnnotations.cpp
new file mode 100644
index 00000000..4253e16d
--- /dev/null
+++ b/libsolidity/ASTAnnotations.cpp
@@ -0,0 +1,28 @@
+/*
+ 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
+ * Object containing the type and other annotations for the AST nodes.
+ */
+
+#include <libsolidity/ASTAnnotations.h>
+
+using namespace std;
+using namespace dev;
+using namespace dev::solidity;
+
diff --git a/libsolidity/ASTAnnotations.h b/libsolidity/ASTAnnotations.h
new file mode 100644
index 00000000..195f11c8
--- /dev/null
+++ b/libsolidity/ASTAnnotations.h
@@ -0,0 +1,123 @@
+/*
+ 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
+ * Object containing the type and other annotations for the AST nodes.
+ */
+
+#pragma once
+
+#include <map>
+#include <memory>
+#include <vector>
+#include <libsolidity/ASTForward.h>
+
+namespace dev
+{
+namespace solidity
+{
+
+class Type;
+using TypePointer = std::shared_ptr<Type const>;
+
+struct ASTAnnotation
+{
+ virtual ~ASTAnnotation() {}
+};
+
+struct ContractDefinitionAnnotation: ASTAnnotation
+{
+ /// Whether all functions are implemented.
+ bool isFullyImplemented = true;
+ /// List of all (direct and indirect) base contracts in order from derived to
+ /// base, including the contract itself.
+ std::vector<ContractDefinition const*> linearizedBaseContracts;
+};
+
+struct VariableDeclarationAnnotation: ASTAnnotation
+{
+ /// Type of variable (type of identifier referencing this variable).
+ TypePointer type;
+};
+
+struct ReturnAnnotation: ASTAnnotation
+{
+ /// Reference to the return parameters of the function.
+ ParameterList const* functionReturnParameters = nullptr;
+};
+
+struct TypeNameAnnotation: ASTAnnotation
+{
+ /// Type declared by this type name, i.e. type of a variable where this type name is used.
+ /// Set during reference resolution stage.
+ TypePointer type;
+};
+
+struct UserDefinedTypeNameAnnotation: TypeNameAnnotation
+{
+ /// Referenced declaration, set during reference resolution stage.
+ Declaration const* referencedDeclaration = nullptr;
+};
+
+struct ExpressionAnnotation: ASTAnnotation
+{
+ /// Inferred type of the expression.
+ TypePointer type;
+ /// Whether it is an LValue (i.e. something that can be assigned to).
+ bool isLValue = false;
+ /// Whether the expression is used in a context where the LValue is actually required.
+ bool lValueRequested = false;
+ /// Types of arguments if the expression is a function that is called - used
+ /// for overload resolution.
+ std::shared_ptr<std::vector<TypePointer>> argumentTypes;
+};
+
+struct IdentifierAnnotation: ExpressionAnnotation
+{
+ /// Stores a reference to the current contract.
+ /// This is needed because types of base contracts change depending on the context.
+ ContractDefinition const* contractScope = nullptr;
+ /// Referenced declaration, set at latest during overload resolution stage.
+ Declaration const* referencedDeclaration = nullptr;
+ /// List of possible declarations it could refer to.
+ std::vector<Declaration const*> overloadedDeclarations;
+};
+
+struct MemberAccessAnnotation: ExpressionAnnotation
+{
+ /// Referenced declaration, set at latest during overload resolution stage.
+ Declaration const* referencedDeclaration = nullptr;
+};
+
+struct BinaryOperationAnnotation: ExpressionAnnotation
+{
+ /// The common type that is used for the operation, not necessarily the result type (which
+ /// e.g. for comparisons is bool).
+ TypePointer commonType;
+};
+
+struct FunctionCallAnnotation: ExpressionAnnotation
+{
+ /// Whether this is an explicit type conversion.
+ bool isTypeConversion = false;
+ /// Whether this is a struct constructor call.
+ bool isStructConstructorCall = false;
+};
+
+}
+}
diff --git a/libsolidity/ASTJsonConverter.cpp b/libsolidity/ASTJsonConverter.cpp
index 96e5cf2a..3edcb77e 100644
--- a/libsolidity/ASTJsonConverter.cpp
+++ b/libsolidity/ASTJsonConverter.cpp
@@ -232,19 +232,19 @@ bool ASTJsonConverter::visit(UnaryOperation const& _node)
bool ASTJsonConverter::visit(BinaryOperation const& _node)
{
- addJsonNode("BinaryOperation",
- { make_pair("operator", Token::toString(_node.getOperator())),
- make_pair("type", type(_node))},
- true);
+ addJsonNode("BinaryOperation", {
+ make_pair("operator", Token::toString(_node.getOperator())),
+ make_pair("type", type(_node))
+ }, true);
return true;
}
bool ASTJsonConverter::visit(FunctionCall const& _node)
{
- addJsonNode("FunctionCall",
- { make_pair("type_conversion", boost::lexical_cast<std::string>(_node.isTypeConversion())),
- make_pair("type", type(_node)) },
- true);
+ addJsonNode("FunctionCall", {
+ make_pair("type_conversion", boost::lexical_cast<std::string>(_node.annotation().isTypeConversion)),
+ make_pair("type", type(_node))
+ }, true);
return true;
}
@@ -441,7 +441,7 @@ void ASTJsonConverter::process()
string ASTJsonConverter::type(Expression const& _expression)
{
- return (_expression.type()) ? _expression.type()->toString() : "Unknown";
+ return _expression.annotation().type ? _expression.annotation().type->toString() : "Unknown";
}
}
diff --git a/libsolidity/ASTJsonConverter.h b/libsolidity/ASTJsonConverter.h
index 77230d92..a44df876 100644
--- a/libsolidity/ASTJsonConverter.h
+++ b/libsolidity/ASTJsonConverter.h
@@ -27,6 +27,7 @@
#include <libsolidity/ASTVisitor.h>
#include <libsolidity/Exceptions.h>
#include <libsolidity/Utils.h>
+#include <libsolidity/ASTAnnotations.h>
#include <json/json.h>
namespace dev
@@ -41,7 +42,7 @@ class ASTJsonConverter: public ASTConstVisitor
{
public:
/// Create a converter to JSON for the given abstract syntax tree.
- ASTJsonConverter(ASTNode const& _ast);
+ explicit ASTJsonConverter(ASTNode const& _ast);
/// Output the json representation of the AST to _stream.
void print(std::ostream& _stream);
Json::Value const& json();
diff --git a/libsolidity/ASTPrinter.cpp b/libsolidity/ASTPrinter.cpp
index 8ed45d87..5762051d 100644
--- a/libsolidity/ASTPrinter.cpp
+++ b/libsolidity/ASTPrinter.cpp
@@ -62,7 +62,7 @@ bool ASTPrinter::visit(ContractDefinition const& _node)
bool ASTPrinter::visit(InheritanceSpecifier const& _node)
{
- writeLine("InheritanceSpecifier \"" + _node.name()->name() + "\"");
+ writeLine("InheritanceSpecifier \"" + _node.name().name() + "\"");
printSourcePart(_node);
return goDeeper();
}
@@ -530,8 +530,8 @@ void ASTPrinter::printSourcePart(ASTNode const& _node)
void ASTPrinter::printType(Expression const& _expression)
{
- if (_expression.type())
- *m_ostream << indentation() << " Type: " << _expression.type()->toString() << "\n";
+ if (_expression.annotation().type)
+ *m_ostream << indentation() << " Type: " << _expression.annotation().type->toString() << "\n";
else
*m_ostream << indentation() << " Type unknown.\n";
}
diff --git a/libsolidity/AST_accept.h b/libsolidity/AST_accept.h
index 1388466c..dfa9c425 100644
--- a/libsolidity/AST_accept.h
+++ b/libsolidity/AST_accept.h
@@ -147,12 +147,6 @@ void StructDefinition::accept(ASTConstVisitor& _visitor) const
_visitor.endVisit(*this);
}
-void StructDefinition::checkValidityOfMembers() const
-{
- checkMemberTypes();
- checkRecursion();
-}
-
void ParameterList::accept(ASTVisitor& _visitor)
{
if (_visitor.visit(*this))
diff --git a/libsolidity/Compiler.cpp b/libsolidity/Compiler.cpp
index d6582653..cb74072d 100644
--- a/libsolidity/Compiler.cpp
+++ b/libsolidity/Compiler.cpp
@@ -53,7 +53,7 @@ void Compiler::compileContract(
std::map<const ContractDefinition*, eth::Assembly const*> const& _contracts
)
{
- m_context = CompilerContext(); // clear it just in case
+ m_context = CompilerContext();
{
CompilerContext::LocationSetter locationSetterRunTime(m_context, _contract);
initializeContext(_contract, _contracts);
@@ -105,9 +105,9 @@ void Compiler::initializeContext(
map<ContractDefinition const*, eth::Assembly const*> const& _compiledContracts
)
{
- CompilerUtils(m_context).initialiseFreeMemoryPointer();
m_context.setCompiledContracts(_compiledContracts);
- m_context.setInheritanceHierarchy(_contract.linearizedBaseContracts());
+ m_context.setInheritanceHierarchy(_contract.annotation().linearizedBaseContracts);
+ CompilerUtils(m_context).initialiseFreeMemoryPointer();
registerStateVariables(_contract);
m_context.resetVisitedNodes(&_contract);
}
@@ -115,14 +115,14 @@ void Compiler::initializeContext(
void Compiler::appendInitAndConstructorCode(ContractDefinition const& _contract)
{
// Determine the arguments that are used for the base constructors.
- std::vector<ContractDefinition const*> const& bases = _contract.linearizedBaseContracts();
+ std::vector<ContractDefinition const*> const& bases = _contract.annotation().linearizedBaseContracts;
for (ContractDefinition const* contract: bases)
{
if (FunctionDefinition const* constructor = contract->constructor())
for (auto const& modifier: constructor->modifiers())
{
auto baseContract = dynamic_cast<ContractDefinition const*>(
- &modifier->name()->referencedDeclaration());
+ modifier->name()->annotation().referencedDeclaration);
if (baseContract)
if (m_baseArguments.count(baseContract->constructor()) == 0)
m_baseArguments[baseContract->constructor()] = &modifier->arguments();
@@ -131,7 +131,8 @@ void Compiler::appendInitAndConstructorCode(ContractDefinition const& _contract)
for (ASTPointer<InheritanceSpecifier> const& base: contract->baseContracts())
{
ContractDefinition const* baseContract = dynamic_cast<ContractDefinition const*>(
- &base->name()->referencedDeclaration());
+ base->name().annotation().referencedDeclaration
+ );
solAssert(baseContract, "");
if (m_baseArguments.count(baseContract->constructor()) == 0)
@@ -187,13 +188,13 @@ void Compiler::appendConstructor(FunctionDefinition const& _constructor)
{
unsigned argumentSize = 0;
for (ASTPointer<VariableDeclaration> const& var: _constructor.parameters())
- if (var->type()->isDynamicallySized())
+ if (var->annotation().type->isDynamicallySized())
{
argumentSize = 0;
break;
}
else
- argumentSize += var->type()->calldataEncodedSize();
+ argumentSize += var->annotation().type->calldataEncodedSize();
CompilerUtils(m_context).fetchFreeMemoryPointer();
if (argumentSize == 0)
@@ -418,7 +419,7 @@ bool Compiler::visit(FunctionDefinition const& _function)
for (ASTPointer<VariableDeclaration const> const& variable: _function.parameters())
{
m_context.addVariable(*variable, parametersSize);
- parametersSize -= variable->type()->sizeOnStack();
+ parametersSize -= variable->annotation().type->sizeOnStack();
}
for (ASTPointer<VariableDeclaration const> const& variable: _function.returnParameters())
@@ -594,9 +595,9 @@ bool Compiler::visit(Return const& _return)
//@todo modifications are needed to make this work with functions returning multiple values
if (Expression const* expression = _return.expression())
{
- solAssert(_return.functionReturnParameters(), "Invalid return parameters pointer.");
- VariableDeclaration const& firstVariable = *_return.functionReturnParameters()->parameters().front();
- compileExpression(*expression, firstVariable.type());
+ solAssert(_return.annotation().functionReturnParameters, "Invalid return parameters pointer.");
+ VariableDeclaration const& firstVariable = *_return.annotation().functionReturnParameters->parameters().front();
+ compileExpression(*expression, firstVariable.annotation().type);
CompilerUtils(m_context).moveToStackVariable(firstVariable);
}
for (unsigned i = 0; i < m_stackCleanupForReturn; ++i)
@@ -619,7 +620,7 @@ bool Compiler::visit(VariableDeclarationStatement const& _variableDeclarationSta
CompilerContext::LocationSetter locationSetter(m_context, _variableDeclarationStatement);
if (Expression const* expression = _variableDeclarationStatement.expression())
{
- compileExpression(*expression, _variableDeclarationStatement.declaration().type());
+ compileExpression(*expression, _variableDeclarationStatement.declaration().annotation().type);
CompilerUtils(m_context).moveToStackVariable(_variableDeclarationStatement.declaration());
}
checker.check();
@@ -632,7 +633,7 @@ bool Compiler::visit(ExpressionStatement const& _expressionStatement)
CompilerContext::LocationSetter locationSetter(m_context, _expressionStatement);
Expression const& expression = _expressionStatement.expression();
compileExpression(expression);
- CompilerUtils(m_context).popStackElement(*expression.type());
+ CompilerUtils(m_context).popStackElement(*expression.annotation().type);
checker.check();
return false;
}
@@ -672,7 +673,7 @@ void Compiler::appendModifierOrFunctionCode()
ASTPointer<ModifierInvocation> const& modifierInvocation = m_currentFunction->modifiers()[m_modifierDepth];
// constructor call should be excluded
- if (dynamic_cast<ContractDefinition const*>(&modifierInvocation->name()->referencedDeclaration()))
+ if (dynamic_cast<ContractDefinition const*>(modifierInvocation->name()->annotation().referencedDeclaration))
{
++m_modifierDepth;
appendModifierOrFunctionCode();
@@ -688,7 +689,7 @@ void Compiler::appendModifierOrFunctionCode()
m_context.addVariable(*modifier.parameters()[i]);
compileExpression(
*modifierInvocation->arguments()[i],
- modifier.parameters()[i]->type()
+ modifier.parameters()[i]->annotation().type
);
}
for (VariableDeclaration const* localVariable: modifier.localVariables())
@@ -710,7 +711,7 @@ void Compiler::appendStackVariableInitialisation(VariableDeclaration const& _var
{
CompilerContext::LocationSetter location(m_context, _variable);
m_context.addVariable(_variable);
- CompilerUtils(m_context).pushZeroValue(*_variable.type());
+ CompilerUtils(m_context).pushZeroValue(*_variable.annotation().type);
}
void Compiler::compileExpression(Expression const& _expression, TypePointer const& _targetType)
@@ -718,7 +719,7 @@ void Compiler::compileExpression(Expression const& _expression, TypePointer cons
ExpressionCompiler expressionCompiler(m_context, m_optimize);
expressionCompiler.compile(_expression);
if (_targetType)
- CompilerUtils(m_context).convertType(*_expression.type(), *_targetType);
+ CompilerUtils(m_context).convertType(*_expression.annotation().type, *_targetType);
}
eth::Assembly Compiler::cloneRuntime()
diff --git a/libsolidity/Compiler.h b/libsolidity/Compiler.h
index c7dd7d90..7b7cffcf 100644
--- a/libsolidity/Compiler.h
+++ b/libsolidity/Compiler.h
@@ -37,10 +37,8 @@ public:
explicit Compiler(bool _optimize = false, unsigned _runs = 200):
m_optimize(_optimize),
m_optimizeRuns(_runs),
- m_context(),
m_returnTag(m_context.newTag())
- {
- }
+ { }
void compileContract(
ContractDefinition const& _contract,
@@ -71,7 +69,8 @@ public:
eth::AssemblyItem functionEntryLabel(FunctionDefinition const& _function) const;
private:
- /// Registers the non-function objects inside the contract with the context.
+ /// Registers the non-function objects inside the contract with the context and stores the basic
+ /// information about the contract like the AST annotations.
void initializeContext(
ContractDefinition const& _contract,
std::map<ContractDefinition const*, eth::Assembly const*> const& _compiledContracts
diff --git a/libsolidity/CompilerContext.h b/libsolidity/CompilerContext.h
index 39f4e75f..46ebfcf8 100644
--- a/libsolidity/CompilerContext.h
+++ b/libsolidity/CompilerContext.h
@@ -29,6 +29,7 @@
#include <libevmasm/Assembly.h>
#include <libsolidity/ASTForward.h>
#include <libsolidity/Types.h>
+#include <libsolidity/ASTAnnotations.h>
#include <libdevcore/Common.h>
namespace dev {
diff --git a/libsolidity/CompilerStack.cpp b/libsolidity/CompilerStack.cpp
index 70bb1b4c..e6b87264 100644
--- a/libsolidity/CompilerStack.cpp
+++ b/libsolidity/CompilerStack.cpp
@@ -27,6 +27,7 @@
#include <libsolidity/Parser.h>
#include <libsolidity/GlobalContext.h>
#include <libsolidity/NameAndTypeResolver.h>
+#include <libsolidity/TypeChecker.h>
#include <libsolidity/Compiler.h>
#include <libsolidity/CompilerStack.h>
#include <libsolidity/InterfaceHandler.h>
@@ -76,6 +77,7 @@ void CompilerStack::reset(bool _keepSources, bool _addStandardSources)
m_globalContext.reset();
m_sourceOrder.clear();
m_contracts.clear();
+ m_errors.clear();
}
bool CompilerStack::addSource(string const& _name, string const& _content, bool _isLibrary)
@@ -93,8 +95,10 @@ void CompilerStack::setSource(string const& _sourceCode)
addSource("", _sourceCode);
}
-void CompilerStack::parse()
+bool CompilerStack::parse()
{
+ m_errors.clear();
+
for (auto& sourcePair: m_sources)
{
sourcePair.second.scanner->reset();
@@ -116,6 +120,7 @@ void CompilerStack::parse()
resolver.resolveNamesAndTypes(*contract);
m_contracts[contract->name()].contract = contract;
}
+
InterfaceHandler interfaceHandler;
for (Source const* source: m_sourceOrder)
for (ASTPointer<ASTNode> const& node: source->ast->nodes())
@@ -123,18 +128,22 @@ void CompilerStack::parse()
{
m_globalContext->setCurrentContract(*contract);
resolver.updateDeclaration(*m_globalContext->currentThis());
- resolver.checkTypeRequirements(*contract);
+ TypeChecker typeChecker;
+ bool typesFine = typeChecker.checkTypeRequirements(*contract);
+ if (!typesFine)
+ m_errors += typeChecker.errors();
contract->setDevDocumentation(interfaceHandler.devDocumentation(*contract));
contract->setUserDocumentation(interfaceHandler.userDocumentation(*contract));
m_contracts[contract->name()].contract = contract;
}
- m_parseSuccessful = true;
+ m_parseSuccessful = m_errors.empty();
+ return m_parseSuccessful;
}
-void CompilerStack::parse(string const& _sourceCode)
+bool CompilerStack::parse(string const& _sourceCode)
{
setSource(_sourceCode);
- parse();
+ return parse();
}
vector<string> CompilerStack::contractNames() const
@@ -148,17 +157,18 @@ vector<string> CompilerStack::contractNames() const
}
-void CompilerStack::compile(bool _optimize, unsigned _runs)
+bool CompilerStack::compile(bool _optimize, unsigned _runs)
{
if (!m_parseSuccessful)
- parse();
+ if (!parse())
+ return false;
map<ContractDefinition const*, eth::Assembly const*> compiledContracts;
for (Source const* source: m_sourceOrder)
for (ASTPointer<ASTNode> const& node: source->ast->nodes())
if (ContractDefinition* contract = dynamic_cast<ContractDefinition*>(node.get()))
{
- if (!contract->isFullyImplemented())
+ if (!contract->annotation().isFullyImplemented)
continue;
shared_ptr<Compiler> compiler = make_shared<Compiler>(_optimize, _runs);
compiler->compileContract(*contract, compiledContracts);
@@ -172,13 +182,12 @@ void CompilerStack::compile(bool _optimize, unsigned _runs)
cloneCompiler.compileClone(*contract, compiledContracts);
compiledContract.cloneObject = cloneCompiler.assembledObject();
}
+ return true;
}
-eth::LinkerObject const& CompilerStack::compile(string const& _sourceCode, bool _optimize)
+bool CompilerStack::compile(string const& _sourceCode, bool _optimize)
{
- parse(_sourceCode);
- compile(_optimize);
- return object();
+ return parse(_sourceCode) && compile(_optimize);
}
void CompilerStack::link(const std::map<string, h160>& _libraries)
@@ -317,12 +326,6 @@ size_t CompilerStack::functionEntryPoint(
return 0;
}
-eth::LinkerObject CompilerStack::staticCompile(std::string const& _sourceCode, bool _optimize)
-{
- CompilerStack stack;
- return stack.compile(_sourceCode, _optimize);
-}
-
tuple<int, int, int, int> CompilerStack::positionFromSourceLocation(SourceLocation const& _sourceLocation) const
{
int startLine;
diff --git a/libsolidity/CompilerStack.h b/libsolidity/CompilerStack.h
index 99e8af1a..e541ea47 100644
--- a/libsolidity/CompilerStack.h
+++ b/libsolidity/CompilerStack.h
@@ -54,6 +54,7 @@ class SourceUnit;
class Compiler;
class GlobalContext;
class InterfaceHandler;
+struct Error;
enum class DocumentationType: uint8_t
{
@@ -79,22 +80,28 @@ public:
/// Adds a source object (e.g. file) to the parser. After this, parse has to be called again.
/// @returns true if a source object by the name already existed and was replaced.
- void addSources(StringMap const& _nameContents, bool _isLibrary = false) { for (auto const& i: _nameContents) addSource(i.first, i.second, _isLibrary); }
+ void addSources(StringMap const& _nameContents, bool _isLibrary = false)
+ {
+ for (auto const& i: _nameContents) addSource(i.first, i.second, _isLibrary);
+ }
bool addSource(std::string const& _name, std::string const& _content, bool _isLibrary = false);
void setSource(std::string const& _sourceCode);
/// Parses all source units that were added
- void parse();
+ /// @returns false on error.
+ bool parse();
/// Sets the given source code as the only source unit apart from standard sources and parses it.
- void parse(std::string const& _sourceCode);
+ /// @returns false on error.
+ bool parse(std::string const& _sourceCode);
/// Returns a list of the contract names in the sources.
std::vector<std::string> contractNames() const;
std::string defaultContractName() const;
/// Compiles the source units that were previously added and parsed.
- void compile(bool _optimize = false, unsigned _runs = 200);
+ /// @returns false on error.
+ bool compile(bool _optimize = false, unsigned _runs = 200);
/// Parses and compiles the given source code.
- /// @returns the compiled linker object
- eth::LinkerObject const& compile(std::string const& _sourceCode, bool _optimize = false);
+ /// @returns false on error.
+ bool compile(std::string const& _sourceCode, bool _optimize = false);
/// Inserts the given addresses into the linker objects of all compiled contracts.
void link(std::map<std::string, h160> const& _libraries);
@@ -150,15 +157,14 @@ public:
FunctionDefinition const& _function
) const;
- /// Compile the given @a _sourceCode to bytecode. If a scanner is provided, it is used for
- /// scanning the source code - this is useful for printing exception information.
- static eth::LinkerObject staticCompile(std::string const& _sourceCode, bool _optimize = false);
-
/// Helper function for logs printing. Do only use in error cases, it's quite expensive.
/// line and columns are numbered starting from 1 with following order:
/// start line, start column, end line, end column
std::tuple<int, int, int, int> positionFromSourceLocation(SourceLocation const& _sourceLocation) const;
+ /// @returns the list of errors that occured during parsing and type checking.
+ std::vector<std::shared_ptr<Error const>> const& errors() const { return m_errors; }
+
private:
/**
* Information pertaining to one source unit, filled gradually during parsing and compilation.
@@ -198,6 +204,7 @@ private:
std::shared_ptr<GlobalContext> m_globalContext;
std::vector<Source const*> m_sourceOrder;
std::map<std::string const, Contract> m_contracts;
+ std::vector<std::shared_ptr<Error const>> m_errors;
};
}
diff --git a/libsolidity/CompilerUtils.cpp b/libsolidity/CompilerUtils.cpp
index e370c6be..b9b554e8 100644
--- a/libsolidity/CompilerUtils.cpp
+++ b/libsolidity/CompilerUtils.cpp
@@ -600,7 +600,7 @@ void CompilerUtils::pushZeroValue(const Type& _type)
void CompilerUtils::moveToStackVariable(VariableDeclaration const& _variable)
{
unsigned const stackPosition = m_context.baseToCurrentStackOffset(m_context.baseStackOffsetOfVariable(_variable));
- unsigned const size = _variable.type()->sizeOnStack();
+ unsigned const size = _variable.annotation().type->sizeOnStack();
solAssert(stackPosition >= size, "Variable size and position mismatch.");
// move variable starting from its top end in the stack
if (stackPosition - size + 1 > 16)
diff --git a/libsolidity/CompilerUtils.h b/libsolidity/CompilerUtils.h
index 0875ddd2..568a6307 100644
--- a/libsolidity/CompilerUtils.h
+++ b/libsolidity/CompilerUtils.h
@@ -170,7 +170,7 @@ unsigned CompilerUtils::sizeOnStack(std::vector<T> const& _variables)
{
unsigned size = 0;
for (T const& variable: _variables)
- size += variable->type()->sizeOnStack();
+ size += variable->annotation().type->sizeOnStack();
return size;
}
diff --git a/libsolidity/ConstantEvaluator.cpp b/libsolidity/ConstantEvaluator.cpp
new file mode 100644
index 00000000..5936b3d4
--- /dev/null
+++ b/libsolidity/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/ConstantEvaluator.h>
+#include <libsolidity/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/ConstantEvaluator.h b/libsolidity/ConstantEvaluator.h
new file mode 100644
index 00000000..cf3d2094
--- /dev/null
+++ b/libsolidity/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/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/Exceptions.h b/libsolidity/Exceptions.h
index 4bb6644b..645b368f 100644
--- a/libsolidity/Exceptions.h
+++ b/libsolidity/Exceptions.h
@@ -32,12 +32,16 @@ namespace dev
namespace solidity
{
-struct ParserError: virtual Exception {};
-struct TypeError: virtual Exception {};
-struct DeclarationError: virtual Exception {};
+struct Error: virtual Exception {};
+
+struct ParserError: virtual Error {};
+struct TypeError: virtual Error {};
+struct DeclarationError: virtual Error {};
+struct DocstringParsingError: virtual Error {};
+
struct CompilerError: virtual Exception {};
struct InternalCompilerError: virtual Exception {};
-struct DocstringParsingError: virtual Exception {};
+struct FatalError: virtual Exception {};
using errorSourceLocationInfo = std::pair<std::string, SourceLocation>;
diff --git a/libsolidity/ExpressionCompiler.cpp b/libsolidity/ExpressionCompiler.cpp
index 7db0dde9..65706602 100644
--- a/libsolidity/ExpressionCompiler.cpp
+++ b/libsolidity/ExpressionCompiler.cpp
@@ -48,12 +48,12 @@ void ExpressionCompiler::appendStateVariableInitialization(VariableDeclaration c
{
if (!_varDecl.value())
return;
- TypePointer type = _varDecl.value()->type();
+ TypePointer type = _varDecl.value()->annotation().type;
solAssert(!!type, "Type information not available.");
CompilerContext::LocationSetter locationSetter(m_context, _varDecl);
_varDecl.value()->accept(*this);
- if (_varDecl.type()->dataStoredIn(DataLocation::Storage))
+ if (_varDecl.annotation().type->dataStoredIn(DataLocation::Storage))
{
// reference type, only convert value to mobile type and do final conversion in storeValue.
utils().convertType(*type, *type->mobileType());
@@ -61,8 +61,8 @@ void ExpressionCompiler::appendStateVariableInitialization(VariableDeclaration c
}
else
{
- utils().convertType(*type, *_varDecl.type());
- type = _varDecl.type();
+ utils().convertType(*type, *_varDecl.annotation().type);
+ type = _varDecl.annotation().type;
}
StorageItem(m_context, _varDecl).storeValue(*type, _varDecl.location(), true);
}
@@ -71,10 +71,10 @@ void ExpressionCompiler::appendConstStateVariableAccessor(VariableDeclaration co
{
solAssert(_varDecl.isConstant(), "");
_varDecl.value()->accept(*this);
- utils().convertType(*_varDecl.value()->type(), *_varDecl.type());
+ utils().convertType(*_varDecl.value()->annotation().type, *_varDecl.annotation().type);
// append return
- m_context << eth::dupInstruction(_varDecl.type()->sizeOnStack() + 1);
+ m_context << eth::dupInstruction(_varDecl.annotation().type->sizeOnStack() + 1);
m_context.appendJump(eth::AssemblyItem::JumpType::OutOfFunction);
}
@@ -90,7 +90,7 @@ void ExpressionCompiler::appendStateVariableAccessor(VariableDeclaration const&
auto const& location = m_context.storageLocationOfVariable(_varDecl);
m_context << location.first << u256(location.second);
- TypePointer returnType = _varDecl.type();
+ TypePointer returnType = _varDecl.annotation().type;
for (size_t i = 0; i < paramTypes.size(); ++i)
{
@@ -179,11 +179,11 @@ bool ExpressionCompiler::visit(Assignment const& _assignment)
{
CompilerContext::LocationSetter locationSetter(m_context, _assignment);
_assignment.rightHandSide().accept(*this);
- TypePointer type = _assignment.rightHandSide().type();
- if (!_assignment.type()->dataStoredIn(DataLocation::Storage))
+ TypePointer type = _assignment.rightHandSide().annotation().type;
+ if (!_assignment.annotation().type->dataStoredIn(DataLocation::Storage))
{
- utils().convertType(*type, *_assignment.type());
- type = _assignment.type();
+ utils().convertType(*type, *_assignment.annotation().type);
+ type = _assignment.annotation().type;
}
else
{
@@ -197,9 +197,9 @@ bool ExpressionCompiler::visit(Assignment const& _assignment)
Token::Value op = _assignment.assignmentOperator();
if (op != Token::Assign) // compound assignment
{
- solAssert(_assignment.type()->isValueType(), "Compound operators not implemented for non-value types.");
+ solAssert(_assignment.annotation().type->isValueType(), "Compound operators not implemented for non-value types.");
unsigned lvalueSize = m_currentLValue->sizeOnStack();
- unsigned itemSize = _assignment.type()->sizeOnStack();
+ unsigned itemSize = _assignment.annotation().type->sizeOnStack();
if (lvalueSize > 0)
{
utils().copyToStackTop(lvalueSize + itemSize, itemSize);
@@ -207,7 +207,7 @@ bool ExpressionCompiler::visit(Assignment const& _assignment)
// value lvalue_ref value lvalue_ref
}
m_currentLValue->retrieveValue(_assignment.location(), true);
- appendOrdinaryBinaryOperatorCode(Token::AssignmentToBinaryOp(op), *_assignment.type());
+ appendOrdinaryBinaryOperatorCode(Token::AssignmentToBinaryOp(op), *_assignment.annotation().type);
if (lvalueSize > 0)
{
solAssert(itemSize + lvalueSize <= 16, "Stack too deep, try removing local variables.");
@@ -228,9 +228,9 @@ bool ExpressionCompiler::visit(UnaryOperation const& _unaryOperation)
// the operator should know how to convert itself and to which types it applies, so
// put this code together with "Type::acceptsBinary/UnaryOperator" into a class that
// represents the operator
- if (_unaryOperation.type()->category() == Type::Category::IntegerConstant)
+ if (_unaryOperation.annotation().type->category() == Type::Category::IntegerConstant)
{
- m_context << _unaryOperation.type()->literalValue(nullptr);
+ m_context << _unaryOperation.annotation().type->literalValue(nullptr);
return false;
}
@@ -259,7 +259,7 @@ bool ExpressionCompiler::visit(UnaryOperation const& _unaryOperation)
if (!_unaryOperation.isPrefixOperation())
{
// store value for later
- solAssert(_unaryOperation.type()->sizeOnStack() == 1, "Stack size != 1 not implemented.");
+ solAssert(_unaryOperation.annotation().type->sizeOnStack() == 1, "Stack size != 1 not implemented.");
m_context << eth::Instruction::DUP1;
if (m_currentLValue->sizeOnStack() > 0)
for (unsigned i = 1 + m_currentLValue->sizeOnStack(); i > 0; --i)
@@ -275,7 +275,7 @@ bool ExpressionCompiler::visit(UnaryOperation const& _unaryOperation)
for (unsigned i = m_currentLValue->sizeOnStack(); i > 0; --i)
m_context << eth::swapInstruction(i);
m_currentLValue->storeValue(
- *_unaryOperation.type(), _unaryOperation.location(),
+ *_unaryOperation.annotation().type, _unaryOperation.location(),
!_unaryOperation.isPrefixOperation());
m_currentLValue.reset();
break;
@@ -297,7 +297,8 @@ bool ExpressionCompiler::visit(BinaryOperation const& _binaryOperation)
CompilerContext::LocationSetter locationSetter(m_context, _binaryOperation);
Expression const& leftExpression = _binaryOperation.leftExpression();
Expression const& rightExpression = _binaryOperation.rightExpression();
- Type const& commonType = _binaryOperation.commonType();
+ solAssert(!!_binaryOperation.annotation().commonType, "");
+ Type const& commonType = *_binaryOperation.annotation().commonType;
Token::Value const c_op = _binaryOperation.getOperator();
if (c_op == Token::And || c_op == Token::Or) // special case: short-circuiting
@@ -312,22 +313,22 @@ bool ExpressionCompiler::visit(BinaryOperation const& _binaryOperation)
// for commutative operators, push the literal as late as possible to allow improved optimization
auto isLiteral = [](Expression const& _e)
{
- return dynamic_cast<Literal const*>(&_e) || _e.type()->category() == Type::Category::IntegerConstant;
+ return dynamic_cast<Literal const*>(&_e) || _e.annotation().type->category() == Type::Category::IntegerConstant;
};
bool swap = m_optimize && Token::isCommutativeOp(c_op) && isLiteral(rightExpression) && !isLiteral(leftExpression);
if (swap)
{
leftExpression.accept(*this);
- utils().convertType(*leftExpression.type(), commonType, cleanupNeeded);
+ utils().convertType(*leftExpression.annotation().type, commonType, cleanupNeeded);
rightExpression.accept(*this);
- utils().convertType(*rightExpression.type(), commonType, cleanupNeeded);
+ utils().convertType(*rightExpression.annotation().type, commonType, cleanupNeeded);
}
else
{
rightExpression.accept(*this);
- utils().convertType(*rightExpression.type(), commonType, cleanupNeeded);
+ utils().convertType(*rightExpression.annotation().type, commonType, cleanupNeeded);
leftExpression.accept(*this);
- utils().convertType(*leftExpression.type(), commonType, cleanupNeeded);
+ utils().convertType(*leftExpression.annotation().type, commonType, cleanupNeeded);
}
if (Token::isCompareOp(c_op))
appendCompareOperatorCode(c_op, commonType);
@@ -343,25 +344,25 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
{
CompilerContext::LocationSetter locationSetter(m_context, _functionCall);
using Location = FunctionType::Location;
- if (_functionCall.isTypeConversion())
+ if (_functionCall.annotation().isTypeConversion)
{
solAssert(_functionCall.arguments().size() == 1, "");
solAssert(_functionCall.names().empty(), "");
Expression const& firstArgument = *_functionCall.arguments().front();
firstArgument.accept(*this);
- utils().convertType(*firstArgument.type(), *_functionCall.type());
+ utils().convertType(*firstArgument.annotation().type, *_functionCall.annotation().type);
return false;
}
FunctionTypePointer functionType;
- if (_functionCall.isStructConstructorCall())
+ if (_functionCall.annotation().isStructConstructorCall)
{
- auto const& type = dynamic_cast<TypeType const&>(*_functionCall.expression().type());
+ auto const& type = dynamic_cast<TypeType const&>(*_functionCall.expression().annotation().type);
auto const& structType = dynamic_cast<StructType const&>(*type.actualType());
functionType = structType.constructorType();
}
else
- functionType = dynamic_pointer_cast<FunctionType const>(_functionCall.expression().type());
+ functionType = dynamic_pointer_cast<FunctionType const>(_functionCall.expression().annotation().type);
TypePointers const& parameterTypes = functionType->parameterTypes();
vector<ASTPointer<Expression const>> const& callArguments = _functionCall.arguments();
@@ -385,9 +386,9 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
solAssert(found, "");
}
- if (_functionCall.isStructConstructorCall())
+ if (_functionCall.annotation().isStructConstructorCall)
{
- TypeType const& type = dynamic_cast<TypeType const&>(*_functionCall.expression().type());
+ TypeType const& type = dynamic_cast<TypeType const&>(*_functionCall.expression().annotation().type);
auto const& structType = dynamic_cast<StructType const&>(*type.actualType());
m_context << max(u256(32u), structType.memorySize());
@@ -397,7 +398,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
for (unsigned i = 0; i < arguments.size(); ++i)
{
arguments[i]->accept(*this);
- utils().convertType(*arguments[i]->type(), *functionType->parameterTypes()[i]);
+ utils().convertType(*arguments[i]->annotation().type, *functionType->parameterTypes()[i]);
utils().storeInMemoryDynamic(*functionType->parameterTypes()[i]);
}
m_context << eth::Instruction::POP;
@@ -416,7 +417,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
for (unsigned i = 0; i < arguments.size(); ++i)
{
arguments[i]->accept(*this);
- utils().convertType(*arguments[i]->type(), *function.parameterTypes()[i]);
+ utils().convertType(*arguments[i]->annotation().type, *function.parameterTypes()[i]);
}
_functionCall.expression().accept(*this);
@@ -449,7 +450,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
for (auto const& arg: arguments)
{
arg->accept(*this);
- argumentTypes.push_back(arg->type());
+ argumentTypes.push_back(arg->annotation().type);
}
ContractDefinition const& contract =
dynamic_cast<ContractType const&>(*function.returnParameterTypes().front()).contractDefinition();
@@ -481,7 +482,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
_functionCall.expression().accept(*this);
arguments.front()->accept(*this);
- utils().convertType(*arguments.front()->type(), IntegerType(256), true);
+ utils().convertType(*arguments.front()->annotation().type, IntegerType(256), true);
// Note that function is not the original function, but the ".gas" function.
// Its values of gasSet and valueSet is equal to the original function's though.
unsigned stackDepth = (function.gasSet() ? 1 : 0) + (function.valueSet() ? 1 : 0);
@@ -505,7 +506,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
m_context << u256(0); // do not send gas (there still is the stipend)
arguments.front()->accept(*this);
utils().convertType(
- *arguments.front()->type(),
+ *arguments.front()->annotation().type,
*function.parameterTypes().front(), true
);
appendExternalFunctionCall(
@@ -525,7 +526,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
break;
case Location::Suicide:
arguments.front()->accept(*this);
- utils().convertType(*arguments.front()->type(), *function.parameterTypes().front(), true);
+ utils().convertType(*arguments.front()->annotation().type, *function.parameterTypes().front(), true);
m_context << eth::Instruction::SUICIDE;
break;
case Location::SHA3:
@@ -534,7 +535,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
for (auto const& arg: arguments)
{
arg->accept(*this);
- argumentTypes.push_back(arg->type());
+ argumentTypes.push_back(arg->annotation().type);
}
utils().fetchFreeMemoryPointer();
utils().encodeToMemory(argumentTypes, TypePointers(), function.padArguments(), true);
@@ -552,12 +553,12 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
for (unsigned arg = logNumber; arg > 0; --arg)
{
arguments[arg]->accept(*this);
- utils().convertType(*arguments[arg]->type(), *function.parameterTypes()[arg], true);
+ utils().convertType(*arguments[arg]->annotation().type, *function.parameterTypes()[arg], true);
}
arguments.front()->accept(*this);
utils().fetchFreeMemoryPointer();
utils().encodeToMemory(
- {arguments.front()->type()},
+ {arguments.front()->annotation().type},
{function.parameterTypes().front()},
false,
true);
@@ -577,7 +578,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
++numIndexed;
arguments[arg - 1]->accept(*this);
utils().convertType(
- *arguments[arg - 1]->type(),
+ *arguments[arg - 1]->annotation().type,
*function.parameterTypes()[arg - 1],
true
);
@@ -596,7 +597,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
if (!event.parameters()[arg]->isIndexed())
{
arguments[arg]->accept(*this);
- nonIndexedArgTypes.push_back(arguments[arg]->type());
+ nonIndexedArgTypes.push_back(arguments[arg]->annotation().type);
nonIndexedParamTypes.push_back(function.parameterTypes()[arg]);
}
utils().fetchFreeMemoryPointer();
@@ -609,7 +610,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
case Location::BlockHash:
{
arguments[0]->accept(*this);
- utils().convertType(*arguments[0]->type(), *function.parameterTypes()[0], true);
+ utils().convertType(*arguments[0]->annotation().type, *function.parameterTypes()[0], true);
m_context << eth::Instruction::BLOCKHASH;
break;
}
@@ -644,24 +645,24 @@ void ExpressionCompiler::endVisit(MemberAccess const& _memberAccess)
{
CompilerContext::LocationSetter locationSetter(m_context, _memberAccess);
ASTString const& member = _memberAccess.memberName();
- switch (_memberAccess.expression().type()->category())
+ switch (_memberAccess.expression().annotation().type->category())
{
case Type::Category::Contract:
{
bool alsoSearchInteger = false;
- ContractType const& type = dynamic_cast<ContractType const&>(*_memberAccess.expression().type());
+ ContractType const& type = dynamic_cast<ContractType const&>(*_memberAccess.expression().annotation().type);
if (type.isSuper())
{
- solAssert(!!_memberAccess.referencedDeclaration(), "Referenced declaration not resolved.");
+ solAssert(!!_memberAccess.annotation().referencedDeclaration, "Referenced declaration not resolved.");
m_context << m_context.superFunctionEntryLabel(
- dynamic_cast<FunctionDefinition const&>(*_memberAccess.referencedDeclaration()),
+ dynamic_cast<FunctionDefinition const&>(*_memberAccess.annotation().referencedDeclaration),
type.contractDefinition()
).pushTag();
}
else
{
// ordinary contract type
- if (Declaration const* declaration = _memberAccess.referencedDeclaration())
+ if (Declaration const* declaration = _memberAccess.annotation().referencedDeclaration)
{
u256 identifier;
if (auto const* variable = dynamic_cast<VariableDeclaration const*>(declaration))
@@ -684,7 +685,7 @@ void ExpressionCompiler::endVisit(MemberAccess const& _memberAccess)
if (member == "balance")
{
utils().convertType(
- *_memberAccess.expression().type(),
+ *_memberAccess.expression().annotation().type,
IntegerType(0, IntegerType::Modifier::Address),
true
);
@@ -692,7 +693,7 @@ void ExpressionCompiler::endVisit(MemberAccess const& _memberAccess)
}
else if ((set<string>{"send", "call", "callcode"}).count(member))
utils().convertType(
- *_memberAccess.expression().type(),
+ *_memberAccess.expression().annotation().type,
IntegerType(0, IntegerType::Modifier::Address),
true
);
@@ -700,7 +701,7 @@ void ExpressionCompiler::endVisit(MemberAccess const& _memberAccess)
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Invalid member access to integer."));
break;
case Type::Category::Function:
- solAssert(!!_memberAccess.expression().type()->memberType(member),
+ solAssert(!!_memberAccess.expression().annotation().type->memberType(member),
"Invalid member access to function.");
break;
case Type::Category::Magic:
@@ -735,7 +736,7 @@ void ExpressionCompiler::endVisit(MemberAccess const& _memberAccess)
break;
case Type::Category::Struct:
{
- StructType const& type = dynamic_cast<StructType const&>(*_memberAccess.expression().type());
+ StructType const& type = dynamic_cast<StructType const&>(*_memberAccess.expression().annotation().type);
switch (type.location())
{
case DataLocation::Storage:
@@ -748,7 +749,7 @@ void ExpressionCompiler::endVisit(MemberAccess const& _memberAccess)
case DataLocation::Memory:
{
m_context << type.memoryOffsetOfMember(member) << eth::Instruction::ADD;
- setLValue<MemoryItem>(_memberAccess, *_memberAccess.type());
+ setLValue<MemoryItem>(_memberAccess, *_memberAccess.annotation().type);
break;
}
default:
@@ -758,13 +759,13 @@ void ExpressionCompiler::endVisit(MemberAccess const& _memberAccess)
}
case Type::Category::Enum:
{
- EnumType const& type = dynamic_cast<EnumType const&>(*_memberAccess.expression().type());
+ EnumType const& type = dynamic_cast<EnumType const&>(*_memberAccess.expression().annotation().type);
m_context << type.memberValue(_memberAccess.memberName());
break;
}
case Type::Category::TypeType:
{
- TypeType const& type = dynamic_cast<TypeType const&>(*_memberAccess.expression().type());
+ TypeType const& type = dynamic_cast<TypeType const&>(*_memberAccess.expression().annotation().type);
solAssert(
!type.members().membersByName(_memberAccess.memberName()).empty(),
"Invalid member access to " + type.toString(false)
@@ -772,12 +773,12 @@ void ExpressionCompiler::endVisit(MemberAccess const& _memberAccess)
if (dynamic_cast<ContractType const*>(type.actualType().get()))
{
- auto const& funType = dynamic_cast<FunctionType const&>(*_memberAccess.type());
+ auto const& funType = dynamic_cast<FunctionType const&>(*_memberAccess.annotation().type);
if (funType.location() != FunctionType::Location::Internal)
m_context << funType.externalIdentifier();
else
{
- auto const* function = dynamic_cast<FunctionDefinition const*>(_memberAccess.referencedDeclaration());
+ auto const* function = dynamic_cast<FunctionDefinition const*>(_memberAccess.annotation().referencedDeclaration);
solAssert(!!function, "Function not found in member access");
m_context << m_context.functionEntryLabel(*function).pushTag();
}
@@ -789,7 +790,7 @@ void ExpressionCompiler::endVisit(MemberAccess const& _memberAccess)
case Type::Category::Array:
{
solAssert(member == "length", "Illegal array member.");
- auto const& type = dynamic_cast<ArrayType const&>(*_memberAccess.expression().type());
+ auto const& type = dynamic_cast<ArrayType const&>(*_memberAccess.expression().annotation().type);
if (!type.isDynamicallySized())
{
utils().popStackElement(type);
@@ -820,7 +821,7 @@ bool ExpressionCompiler::visit(IndexAccess const& _indexAccess)
CompilerContext::LocationSetter locationSetter(m_context, _indexAccess);
_indexAccess.baseExpression().accept(*this);
- Type const& baseType = *_indexAccess.baseExpression().type();
+ Type const& baseType = *_indexAccess.baseExpression().annotation().type;
if (baseType.category() == Type::Category::Mapping)
{
@@ -834,7 +835,7 @@ bool ExpressionCompiler::visit(IndexAccess const& _indexAccess)
// stack: base index mem
// note: the following operations must not allocate memory!
utils().encodeToMemory(
- TypePointers{_indexAccess.indexExpression()->type()},
+ TypePointers{_indexAccess.indexExpression()->annotation().type},
TypePointers{keyType},
false,
true
@@ -876,7 +877,7 @@ bool ExpressionCompiler::visit(IndexAccess const& _indexAccess)
setLValueToStorageItem(_indexAccess);
break;
case DataLocation::Memory:
- setLValue<MemoryItem>(_indexAccess, *_indexAccess.type(), !arrayType.isByteArray());
+ setLValue<MemoryItem>(_indexAccess, *_indexAccess.annotation().type, !arrayType.isByteArray());
break;
case DataLocation::CallData:
//@todo if we implement this, the value in calldata has to be added to the base offset
@@ -900,14 +901,14 @@ bool ExpressionCompiler::visit(IndexAccess const& _indexAccess)
void ExpressionCompiler::endVisit(Identifier const& _identifier)
{
CompilerContext::LocationSetter locationSetter(m_context, _identifier);
- Declaration const* declaration = &_identifier.referencedDeclaration();
+ Declaration const* declaration = _identifier.annotation().referencedDeclaration;
if (MagicVariableDeclaration const* magicVar = dynamic_cast<MagicVariableDeclaration const*>(declaration))
{
- switch (magicVar->type()->category())
+ switch (magicVar->type(_identifier.annotation().contractScope)->category())
{
case Type::Category::Contract:
// "this" or "super"
- if (!dynamic_cast<ContractType const&>(*magicVar->type()).isSuper())
+ if (!dynamic_cast<ContractType const&>(*magicVar->type(_identifier.annotation().contractScope)).isSuper())
m_context << eth::Instruction::ADDRESS;
break;
case Type::Category::Integer:
@@ -927,7 +928,7 @@ void ExpressionCompiler::endVisit(Identifier const& _identifier)
else
{
variable->value()->accept(*this);
- utils().convertType(*variable->value()->type(), *variable->type());
+ utils().convertType(*variable->value()->annotation().type, *variable->annotation().type);
}
}
else if (auto contract = dynamic_cast<ContractDefinition const*>(declaration))
@@ -953,7 +954,7 @@ void ExpressionCompiler::endVisit(Identifier const& _identifier)
void ExpressionCompiler::endVisit(Literal const& _literal)
{
CompilerContext::LocationSetter locationSetter(m_context, _literal);
- TypePointer type = _literal.type();
+ TypePointer type = _literal.annotation().type;
switch (type->category())
{
case Type::Category::IntegerConstant:
@@ -1141,7 +1142,7 @@ void ExpressionCompiler::appendExternalFunctionCall(
bool manualFunctionId =
(funKind == FunctionKind::Bare || funKind == FunctionKind::BareCallCode) &&
!_arguments.empty() &&
- _arguments.front()->type()->mobileType()->calldataEncodedSize(false) ==
+ _arguments.front()->annotation().type->mobileType()->calldataEncodedSize(false) ==
CompilerUtils::dataStartOffset;
if (manualFunctionId)
{
@@ -1149,7 +1150,7 @@ void ExpressionCompiler::appendExternalFunctionCall(
// function identifier.
_arguments.front()->accept(*this);
utils().convertType(
- *_arguments.front()->type(),
+ *_arguments.front()->annotation().type,
IntegerType(8 * CompilerUtils::dataStartOffset),
true
);
@@ -1161,7 +1162,7 @@ void ExpressionCompiler::appendExternalFunctionCall(
for (size_t i = manualFunctionId ? 1 : 0; i < _arguments.size(); ++i)
{
_arguments[i]->accept(*this);
- argumentTypes.push_back(_arguments[i]->type());
+ argumentTypes.push_back(_arguments[i]->annotation().type);
}
// Copy function identifier to memory.
@@ -1266,16 +1267,16 @@ void ExpressionCompiler::appendExpressionCopyToMemory(Type const& _expectedType,
{
solAssert(_expectedType.isValueType(), "Not implemented for non-value types.");
_expression.accept(*this);
- utils().convertType(*_expression.type(), _expectedType, true);
+ utils().convertType(*_expression.annotation().type, _expectedType, true);
utils().storeInMemoryDynamic(_expectedType);
}
void ExpressionCompiler::setLValueFromDeclaration(Declaration const& _declaration, Expression const& _expression)
{
if (m_context.isLocalVariable(&_declaration))
- setLValue<StackVariable>(_expression, _declaration);
+ setLValue<StackVariable>(_expression, dynamic_cast<VariableDeclaration const&>(_declaration));
else if (m_context.isStateVariable(&_declaration))
- setLValue<StorageItem>(_expression, _declaration);
+ setLValue<StorageItem>(_expression, dynamic_cast<VariableDeclaration const&>(_declaration));
else
BOOST_THROW_EXCEPTION(InternalCompilerError()
<< errinfo_sourceLocation(_expression.location())
@@ -1284,7 +1285,7 @@ void ExpressionCompiler::setLValueFromDeclaration(Declaration const& _declaratio
void ExpressionCompiler::setLValueToStorageItem(Expression const& _expression)
{
- setLValue<StorageItem>(_expression, *_expression.type());
+ setLValue<StorageItem>(_expression, *_expression.annotation().type);
}
CompilerUtils ExpressionCompiler::utils()
diff --git a/libsolidity/ExpressionCompiler.h b/libsolidity/ExpressionCompiler.h
index 02df4b87..d8fef5af 100644
--- a/libsolidity/ExpressionCompiler.h
+++ b/libsolidity/ExpressionCompiler.h
@@ -127,7 +127,7 @@ void ExpressionCompiler::setLValue(Expression const& _expression, _Arguments con
{
solAssert(!m_currentLValue, "Current LValue not reset before trying to set new one.");
std::unique_ptr<_LValueType> lvalue(new _LValueType(m_context, _arguments...));
- if (_expression.lvalueRequested())
+ if (_expression.annotation().lValueRequested)
m_currentLValue = move(lvalue);
else
lvalue->retrieveValue(_expression.location(), true);
diff --git a/libsolidity/InterfaceHandler.cpp b/libsolidity/InterfaceHandler.cpp
index fc79b493..50006caf 100644
--- a/libsolidity/InterfaceHandler.cpp
+++ b/libsolidity/InterfaceHandler.cpp
@@ -96,7 +96,7 @@ string InterfaceHandler::abiInterface(ContractDefinition const& _contractDef)
{
Json::Value input;
input["name"] = p->name();
- input["type"] = p->type()->toString(true);
+ input["type"] = p->annotation().type->toString(true);
input["indexed"] = p->isIndexed();
params.append(input);
}
diff --git a/libsolidity/LValue.cpp b/libsolidity/LValue.cpp
index eb7cc073..81aaeb4d 100644
--- a/libsolidity/LValue.cpp
+++ b/libsolidity/LValue.cpp
@@ -31,8 +31,8 @@ using namespace dev;
using namespace solidity;
-StackVariable::StackVariable(CompilerContext& _compilerContext, Declaration const& _declaration):
- LValue(_compilerContext, *_declaration.type()),
+StackVariable::StackVariable(CompilerContext& _compilerContext, VariableDeclaration const& _declaration):
+ LValue(_compilerContext, *_declaration.annotation().type),
m_baseStackOffset(m_context.baseStackOffsetOfVariable(_declaration)),
m_size(m_dataType.sizeOnStack())
{
@@ -131,8 +131,8 @@ void MemoryItem::setToZero(SourceLocation const&, bool _removeReference) const
m_context << eth::Instruction::POP;
}
-StorageItem::StorageItem(CompilerContext& _compilerContext, Declaration const& _declaration):
- StorageItem(_compilerContext, *_declaration.type())
+StorageItem::StorageItem(CompilerContext& _compilerContext, VariableDeclaration const& _declaration):
+ StorageItem(_compilerContext, *_declaration.annotation().type)
{
auto const& location = m_context.storageLocationOfVariable(_declaration);
m_context << location.first << u256(location.second);
diff --git a/libsolidity/LValue.h b/libsolidity/LValue.h
index f02d8ad1..cbbfb102 100644
--- a/libsolidity/LValue.h
+++ b/libsolidity/LValue.h
@@ -35,6 +35,7 @@ class Declaration;
class Type;
class ArrayType;
class CompilerContext;
+class VariableDeclaration;
/**
* Abstract class used to retrieve, delete and store data in lvalues/variables.
@@ -76,7 +77,7 @@ protected:
class StackVariable: public LValue
{
public:
- StackVariable(CompilerContext& _compilerContext, Declaration const& _declaration);
+ StackVariable(CompilerContext& _compilerContext, VariableDeclaration const& _declaration);
virtual unsigned sizeOnStack() const override { return 0; }
virtual void retrieveValue(SourceLocation const& _location, bool _remove = false) const override;
@@ -129,7 +130,7 @@ class StorageItem: public LValue
{
public:
/// Constructs the LValue and pushes the location of @a _declaration onto the stack.
- StorageItem(CompilerContext& _compilerContext, Declaration const& _declaration);
+ StorageItem(CompilerContext& _compilerContext, VariableDeclaration const& _declaration);
/// Constructs the LValue and assumes that the storage reference is already on the stack.
StorageItem(CompilerContext& _compilerContext, Type const& _type);
virtual unsigned sizeOnStack() const override { return 2; }
diff --git a/libsolidity/NameAndTypeResolver.cpp b/libsolidity/NameAndTypeResolver.cpp
index c3b49abd..c3e31728 100644
--- a/libsolidity/NameAndTypeResolver.cpp
+++ b/libsolidity/NameAndTypeResolver.cpp
@@ -22,6 +22,7 @@
#include <libsolidity/NameAndTypeResolver.h>
#include <libsolidity/AST.h>
+#include <libsolidity/TypeChecker.h>
#include <libsolidity/Exceptions.h>
using namespace std;
@@ -31,7 +32,9 @@ namespace dev
namespace solidity
{
-NameAndTypeResolver::NameAndTypeResolver(vector<Declaration const*> const& _globals)
+NameAndTypeResolver::NameAndTypeResolver(
+ vector<Declaration const*> const& _globals
+)
{
for (Declaration const* declaration: _globals)
m_scopes[nullptr].registerDeclaration(*declaration);
@@ -54,8 +57,8 @@ void NameAndTypeResolver::resolveNamesAndTypes(ContractDefinition& _contract)
linearizeBaseContracts(_contract);
std::vector<ContractDefinition const*> properBases(
- ++_contract.linearizedBaseContracts().begin(),
- _contract.linearizedBaseContracts().end()
+ ++_contract.annotation().linearizedBaseContracts.begin(),
+ _contract.annotation().linearizedBaseContracts.end()
);
for (ContractDefinition const* base: properBases)
@@ -108,13 +111,6 @@ void NameAndTypeResolver::resolveNamesAndTypes(ContractDefinition& _contract)
}
}
-void NameAndTypeResolver::checkTypeRequirements(ContractDefinition& _contract)
-{
- for (ASTPointer<StructDefinition> const& structDef: _contract.definedStructs())
- structDef->checkValidityOfMembers();
- _contract.checkTypeRequirements();
-}
-
void NameAndTypeResolver::updateDeclaration(Declaration const& _declaration)
{
m_scopes[nullptr].registerDeclaration(_declaration, false, true);
@@ -187,23 +183,23 @@ void NameAndTypeResolver::linearizeBaseContracts(ContractDefinition& _contract)
list<list<ContractDefinition const*>> input(1, {});
for (ASTPointer<InheritanceSpecifier> const& baseSpecifier: _contract.baseContracts())
{
- ASTPointer<Identifier> baseName = baseSpecifier->name();
- auto base = dynamic_cast<ContractDefinition const*>(&baseName->referencedDeclaration());
+ Identifier const& baseName = baseSpecifier->name();
+ auto base = dynamic_cast<ContractDefinition const*>(baseName.annotation().referencedDeclaration);
if (!base)
- BOOST_THROW_EXCEPTION(baseName->createTypeError("Contract expected."));
+ BOOST_THROW_EXCEPTION(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->linearizedBaseContracts();
+ vector<ContractDefinition const*> const& basesBases = base->annotation().linearizedBaseContracts;
if (basesBases.empty())
- BOOST_THROW_EXCEPTION(baseName->createTypeError("Definition of base has to precede definition of derived contract"));
+ BOOST_THROW_EXCEPTION(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())
BOOST_THROW_EXCEPTION(_contract.createTypeError("Linearization of inheritance graph impossible"));
- _contract.setLinearizedBaseContracts(result);
+ _contract.annotation().linearizedBaseContracts = result;
}
template <class _T>
@@ -404,139 +400,5 @@ void DeclarationRegistrationHelper::registerDeclaration(Declaration& _declaratio
enterNewSubScope(_declaration);
}
-ReferencesResolver::ReferencesResolver(
- ASTNode& _root,
- NameAndTypeResolver& _resolver,
- ContractDefinition const* _currentContract,
- ParameterList const* _returnParameters,
- bool _resolveInsideCode,
- bool _allowLazyTypes
-):
- m_resolver(_resolver),
- m_currentContract(_currentContract),
- m_returnParameters(_returnParameters),
- m_resolveInsideCode(_resolveInsideCode),
- m_allowLazyTypes(_allowLazyTypes)
-{
- _root.accept(*this);
-}
-
-void ReferencesResolver::endVisit(VariableDeclaration& _variable)
-{
- // endVisit because the internal type needs resolving if it is a user defined type
- // or mapping
- if (_variable.typeName())
- {
- TypePointer type = _variable.typeName()->toType();
- 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.
- if (auto ref = dynamic_cast<ReferenceType const*>(type.get()))
- {
- if (_variable.isExternalCallableParameter())
- {
- // 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)."
- ));
- type = ref->copyForLocation(DataLocation::CallData, true);
- }
- else if (_variable.isCallableParameter() && _variable.scope()->isPublic())
- {
- // force locations of public or external function (return) parameters to memory
- if (loc == VariableDeclaration::Location::Storage)
- BOOST_THROW_EXCEPTION(_variable.createTypeError(
- "Location has to be memory for publicly visible functions "
- "(remove the \"storage\" keyword)."
- ));
- 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."
- ));
-
- _variable.setType(type);
-
- if (!_variable.type())
- BOOST_THROW_EXCEPTION(_variable.typeName()->createTypeError("Invalid type name"));
- }
- else if (!m_allowLazyTypes)
- BOOST_THROW_EXCEPTION(_variable.createTypeError("Explicit type needed."));
- // otherwise we have a "var"-declaration whose type is resolved by the first assignment
-}
-
-bool ReferencesResolver::visit(Return& _return)
-{
- _return.setFunctionReturnParameters(m_returnParameters);
- return true;
-}
-
-bool ReferencesResolver::visit(Mapping&)
-{
- return true;
-}
-
-bool ReferencesResolver::visit(UserDefinedTypeName& _typeName)
-{
- auto declarations = m_resolver.nameFromCurrentScope(_typeName.name());
- if (declarations.empty())
- BOOST_THROW_EXCEPTION(
- DeclarationError() <<
- errinfo_sourceLocation(_typeName.location()) <<
- errinfo_comment("Undeclared identifier.")
- );
- else if (declarations.size() > 1)
- BOOST_THROW_EXCEPTION(
- DeclarationError() <<
- errinfo_sourceLocation(_typeName.location()) <<
- errinfo_comment("Duplicate identifier.")
- );
- else
- _typeName.setReferencedDeclaration(**declarations.begin());
- return false;
-}
-
-bool ReferencesResolver::visit(Identifier& _identifier)
-{
- auto declarations = m_resolver.nameFromCurrentScope(_identifier.name());
- if (declarations.empty())
- BOOST_THROW_EXCEPTION(
- DeclarationError() <<
- errinfo_sourceLocation(_identifier.location()) <<
- errinfo_comment("Undeclared identifier.")
- );
- else if (declarations.size() == 1)
- _identifier.setReferencedDeclaration(*declarations.front(), m_currentContract);
- else
- _identifier.setOverloadedDeclarations(m_resolver.cleanedDeclarations(_identifier, declarations));
- return false;
-}
-
}
}
diff --git a/libsolidity/NameAndTypeResolver.h b/libsolidity/NameAndTypeResolver.h
index 101787ea..d28671ed 100644
--- a/libsolidity/NameAndTypeResolver.h
+++ b/libsolidity/NameAndTypeResolver.h
@@ -25,9 +25,10 @@
#include <map>
#include <list>
#include <boost/noncopyable.hpp>
-
#include <libsolidity/DeclarationContainer.h>
+#include <libsolidity/ReferencesResolver.h>
#include <libsolidity/ASTVisitor.h>
+#include <libsolidity/ASTAnnotations.h>
namespace dev
{
@@ -42,13 +43,11 @@ namespace solidity
class NameAndTypeResolver: private boost::noncopyable
{
public:
- explicit NameAndTypeResolver(std::vector<Declaration const*> const& _globals);
+ NameAndTypeResolver(std::vector<Declaration const*> const& _globals);
/// Registers all declarations found in the source unit.
void registerDeclarations(SourceUnit& _sourceUnit);
/// Resolves all names and types referenced from the given contract.
void resolveNamesAndTypes(ContractDefinition& _contract);
- /// Check all type requirements in the given contract.
- void checkTypeRequirements(ContractDefinition& _contract);
/// Updates the given global declaration (used for "this"). Not to be used with declarations
/// that create their own scope.
void updateDeclaration(Declaration const& _declaration);
@@ -125,36 +124,5 @@ private:
VariableScope* m_currentFunction;
};
-/**
- * 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 ASTVisitor
-{
-public:
- ReferencesResolver(
- ASTNode& _root,
- NameAndTypeResolver& _resolver,
- ContractDefinition const* _currentContract,
- ParameterList const* _returnParameters,
- bool _resolveInsideCode = false,
- bool _allowLazyTypes = true
- );
-
-private:
- virtual void endVisit(VariableDeclaration& _variable) override;
- virtual bool visit(Block&) override { return m_resolveInsideCode; }
- virtual bool visit(Identifier& _identifier) override;
- virtual bool visit(UserDefinedTypeName& _typeName) override;
- virtual bool visit(Mapping&) override;
- virtual bool visit(Return& _return) override;
-
- NameAndTypeResolver& m_resolver;
- ContractDefinition const* m_currentContract;
- ParameterList const* m_returnParameters;
- bool m_resolveInsideCode;
- bool m_allowLazyTypes;
-};
-
}
}
diff --git a/libsolidity/ReferencesResolver.cpp b/libsolidity/ReferencesResolver.cpp
new file mode 100644
index 00000000..623ac8f7
--- /dev/null
+++ b/libsolidity/ReferencesResolver.cpp
@@ -0,0 +1,225 @@
+/*
+ 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/ReferencesResolver.h>
+#include <libsolidity/AST.h>
+#include <libsolidity/NameAndTypeResolver.h>
+#include <libsolidity/Exceptions.h>
+#include <libsolidity/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)
+{
+ auto declarations = m_resolver.nameFromCurrentScope(_typeName.name());
+ if (declarations.empty())
+ BOOST_THROW_EXCEPTION(
+ DeclarationError() <<
+ errinfo_sourceLocation(_typeName.location()) <<
+ errinfo_comment("Undeclared identifier.")
+ );
+ else if (declarations.size() > 1)
+ BOOST_THROW_EXCEPTION(
+ DeclarationError() <<
+ errinfo_sourceLocation(_typeName.location()) <<
+ errinfo_comment("Duplicate identifier.")
+ );
+ Declaration const* declaration = *declarations.begin();
+ _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(
+ 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.
+ if (auto ref = dynamic_cast<ReferenceType const*>(type.get()))
+ {
+ if (_variable.isExternalCallableParameter())
+ {
+ // 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)."
+ ));
+ type = ref->copyForLocation(DataLocation::CallData, true);
+ }
+ else if (_variable.isCallableParameter() && _variable.scope()->isPublic())
+ {
+ // force locations of public or external function (return) parameters to memory
+ if (loc == VariableDeclaration::Location::Storage)
+ BOOST_THROW_EXCEPTION(_variable.createTypeError(
+ "Location has to be memory for publicly visible functions "
+ "(remove the \"storage\" keyword)."
+ ));
+ 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/ReferencesResolver.h b/libsolidity/ReferencesResolver.h
new file mode 100644
index 00000000..b8a55dc2
--- /dev/null
+++ b/libsolidity/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/ASTVisitor.h>
+#include <libsolidity/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/TypeChecker.cpp b/libsolidity/TypeChecker.cpp
new file mode 100644
index 00000000..f453e2fa
--- /dev/null
+++ b/libsolidity/TypeChecker.cpp
@@ -0,0 +1,1143 @@
+/*
+ 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/TypeChecker.h>
+#include <memory>
+#include <boost/range/adaptor/reversed.hpp>
+#include <libsolidity/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 m_errors.empty();
+}
+
+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<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<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<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(f->name())].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(v->name())].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)
+{
+ 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)->externalType()))
+ typeError(*var, "Internal type is not allowed for public and 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.
+
+ // This only infers the type from its type name.
+ // If an explicit type is required, it throws, otherwise it returns TypePointer();
+ TypePointer varType = _variable.annotation().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 && !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 (varType)
+ {
+ if (_variable.value())
+ expectType(*_variable.value(), *varType);
+ }
+ else
+ {
+ // Infer type from value.
+ if (!_variable.value())
+ fatalTypeError(_variable, "Assignment necessary for type detection.");
+ _variable.value()->accept(*this);
+
+ TypePointer const& valueType = type(*_variable.value());
+ solAssert(!!valueType, "");
+ if (
+ valueType->category() == Type::Category::IntegerConstant &&
+ !dynamic_pointer_cast<IntegerConstantType const>(valueType)->integerType()
+ )
+ fatalTypeError(*_variable.value(), "Invalid integer constant " + valueType->toString() + ".");
+ else if (valueType->category() == Type::Category::Void)
+ fatalTypeError(_variable, "Variable cannot have void type.");
+ varType = valueType->mobileType();
+ }
+ solAssert(!!varType, "");
+ _variable.annotation().type = 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).externalType()
+ )
+ 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 (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)->externalType())
+ 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.");
+ else if (params->parameters().size() != 1)
+ typeError(_return, "Different number of arguments in return statement than in returns declaration.");
+ else
+ {
+ // this could later be changed such that the paramaters type is an anonymous struct type,
+ // but for now, we only allow one return parameter
+ 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() +
+ "."
+ );
+ }
+}
+
+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 (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(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<VoidType>();
+ return false;
+ }
+ else
+ {
+ // @todo actually the return type should be an anonymous struct,
+ // but we change it to the type of the first return value until we have anonymous
+ // structs and tuples
+ if (functionType->returnParameterTypes().empty())
+ _functionCall.annotation().type = make_shared<VoidType>();
+ else
+ _functionCall.annotation().type = functionType->returnParameterTypes().front();
+ }
+
+ //@todo would be nice to create a struct type from the arguments
+ // and then ask if that is implicitly convertible to the struct represented by the
+ // function parameters
+ 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)
+ if (
+ !functionType->takesArbitraryParameters() &&
+ !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;
+ auto const& bases = contract->annotation().linearizedBaseContracts;
+ solAssert(!bases.empty(), "Linearized base contracts not yet available.");
+ if (find(bases.begin(), bases.end(), scopeContract) != bases.end())
+ 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.");
+}
+
+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.accept(*this);
+ if (!_expression.annotation().isLValue)
+ typeError(_expression, "Expression has to be an lvalue.");
+ _expression.annotation().lValueRequested = true;
+}
+
+void TypeChecker::typeError(ASTNode const& _node, string const& _description)
+{
+ auto err = make_shared<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/TypeChecker.h b/libsolidity/TypeChecker.h
new file mode 100644
index 00000000..cc539e22
--- /dev/null
+++ b/libsolidity/TypeChecker.h
@@ -0,0 +1,114 @@
+/*
+ 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/TypeChecker.h>
+#include <libsolidity/Types.h>
+#include <libsolidity/ASTAnnotations.h>
+#include <libsolidity/ASTForward.h>
+#include <libsolidity/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:
+ /// Performs type checking on the given contract and all of its sub-nodes.
+ /// @returns true iff all checks passed.
+ bool checkTypeRequirements(ContractDefinition const& _contract);
+
+ /// @returns the list of errors found during type checking.
+ std::vector<std::shared_ptr<Error const>> const& errors() const { return m_errors; }
+
+ /// @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;
+
+ /// 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);
+
+private:
+ 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 void endVisit(ExpressionStatement const& _statement) override;
+ virtual bool visit(Assignment const& _assignment) 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;
+
+ /// @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);
+
+ std::vector<std::shared_ptr<Error const>> m_errors;
+};
+
+}
+}
diff --git a/libsolidity/Types.cpp b/libsolidity/Types.cpp
index beb5becd..68e8e91d 100644
--- a/libsolidity/Types.cpp
+++ b/libsolidity/Types.cpp
@@ -157,55 +157,6 @@ TypePointer Type::fromElementaryTypeName(string const& _name)
return fromElementaryTypeName(Token::fromIdentifierOrKeyword(_name));
}
-TypePointer Type::fromUserDefinedTypeName(UserDefinedTypeName const& _typeName)
-{
- Declaration const* declaration = _typeName.referencedDeclaration();
- if (StructDefinition const* structDef = dynamic_cast<StructDefinition const*>(declaration))
- return make_shared<StructType>(*structDef);
- else if (EnumDefinition const* enumDef = dynamic_cast<EnumDefinition const*>(declaration))
- return make_shared<EnumType>(*enumDef);
- else if (FunctionDefinition const* function = dynamic_cast<FunctionDefinition const*>(declaration))
- return make_shared<FunctionType>(*function);
- else if (ContractDefinition const* contract = dynamic_cast<ContractDefinition const*>(declaration))
- return make_shared<ContractType>(*contract);
- return TypePointer();
-}
-
-TypePointer Type::fromMapping(ElementaryTypeName& _keyType, TypeName& _valueType)
-{
- TypePointer keyType = _keyType.toType();
- if (!keyType)
- BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Error resolving type name."));
- TypePointer valueType = _valueType.toType();
- if (!valueType)
- BOOST_THROW_EXCEPTION(_valueType.createTypeError("Invalid type name."));
- // Convert value type to storage reference.
- valueType = ReferenceType::copyForLocationIfReference(DataLocation::Storage, valueType);
- // Convert key type to memory.
- keyType = ReferenceType::copyForLocationIfReference(DataLocation::Memory, keyType);
- return make_shared<MappingType>(keyType, valueType);
-}
-
-TypePointer Type::fromArrayTypeName(TypeName& _baseTypeName, Expression* _length)
-{
- TypePointer baseType = _baseTypeName.toType();
- if (!baseType)
- BOOST_THROW_EXCEPTION(_baseTypeName.createTypeError("Invalid type name."));
- if (baseType->storageBytes() == 0)
- BOOST_THROW_EXCEPTION(_baseTypeName.createTypeError("Illegal base type of storage size zero for array."));
- if (_length)
- {
- if (!_length->type())
- _length->checkTypeRequirements(nullptr);
- auto const* length = dynamic_cast<IntegerConstantType const*>(_length->type().get());
- if (!length)
- BOOST_THROW_EXCEPTION(_length->createTypeError("Invalid array length."));
- return make_shared<ArrayType>(DataLocation::Storage, baseType, length->literalValue(nullptr));
- }
- else
- return make_shared<ArrayType>(DataLocation::Storage, baseType);
-}
-
TypePointer Type::forLiteral(Literal const& _literal)
{
switch (_literal.token())
@@ -686,7 +637,7 @@ bool ContractType::isImplicitlyConvertibleTo(Type const& _convertTo) const
return dynamic_cast<IntegerType const&>(_convertTo).isAddress();
if (_convertTo.category() == Category::Contract)
{
- auto const& bases = contractDefinition().linearizedBaseContracts();
+ auto const& bases = contractDefinition().annotation().linearizedBaseContracts;
if (m_super && bases.size() <= 1)
return false;
return find(m_super ? ++bases.begin() : bases.begin(), bases.end(),
@@ -944,7 +895,7 @@ MemberList const& ContractType::members() const
if (m_super)
{
// add the most derived of all functions which are visible in derived contracts
- for (ContractDefinition const* base: m_contract.linearizedBaseContracts())
+ for (ContractDefinition const* base: m_contract.annotation().linearizedBaseContracts)
for (ASTPointer<FunctionDefinition> const& function: base->definedFunctions())
{
if (!function->isVisibleInDerivedContracts())
@@ -998,13 +949,13 @@ shared_ptr<FunctionType const> const& ContractType::constructorType() const
vector<tuple<VariableDeclaration const*, u256, unsigned>> ContractType::stateVariables() const
{
vector<VariableDeclaration const*> variables;
- for (ContractDefinition const* contract: boost::adaptors::reverse(m_contract.linearizedBaseContracts()))
+ for (ContractDefinition const* contract: boost::adaptors::reverse(m_contract.annotation().linearizedBaseContracts))
for (ASTPointer<VariableDeclaration> const& variable: contract->stateVariables())
if (!variable->isConstant())
variables.push_back(variable.get());
TypePointers types;
for (auto variable: variables)
- types.push_back(variable->type());
+ types.push_back(variable->annotation().type);
StorageOffsets offsets;
offsets.computeOffsets(types);
@@ -1082,7 +1033,7 @@ MemberList const& StructType::members() const
MemberList::MemberMap members;
for (ASTPointer<VariableDeclaration> const& variable: m_struct.members())
{
- TypePointer type = variable->type();
+ TypePointer type = variable->annotation().type;
// Skip all mapping members if we are not in storage.
if (location() != DataLocation::Storage && !type->canLiveOutsideStorage())
continue;
@@ -1147,7 +1098,7 @@ set<string> StructType::membersMissingInMemory() const
{
set<string> missing;
for (ASTPointer<VariableDeclaration> const& variable: m_struct.members())
- if (!variable->type()->canLiveOutsideStorage())
+ if (!variable->annotation().type->canLiveOutsideStorage())
missing.insert(variable->name());
return missing;
}
@@ -1211,14 +1162,14 @@ FunctionType::FunctionType(FunctionDefinition const& _function, bool _isInternal
for (ASTPointer<VariableDeclaration> const& var: _function.parameters())
{
paramNames.push_back(var->name());
- params.push_back(var->type());
+ params.push_back(var->annotation().type);
}
retParams.reserve(_function.returnParameters().size());
retParamNames.reserve(_function.returnParameters().size());
for (ASTPointer<VariableDeclaration> const& var: _function.returnParameters())
{
retParamNames.push_back(var->name());
- retParams.push_back(var->type());
+ retParams.push_back(var->annotation().type);
}
swap(params, m_parameterTypes);
swap(paramNames, m_parameterNames);
@@ -1231,7 +1182,7 @@ FunctionType::FunctionType(VariableDeclaration const& _varDecl):
{
TypePointers paramTypes;
vector<string> paramNames;
- auto returnType = _varDecl.type();
+ auto returnType = _varDecl.annotation().type;
while (true)
{
@@ -1293,7 +1244,7 @@ FunctionType::FunctionType(const EventDefinition& _event):
for (ASTPointer<VariableDeclaration> const& var: _event.parameters())
{
paramNames.push_back(var->name());
- params.push_back(var->type());
+ params.push_back(var->annotation().type);
}
swap(params, m_parameterTypes);
swap(paramNames, m_parameterNames);
@@ -1662,7 +1613,7 @@ MemberList const& TypeType::members() const
));
else if (m_currentContract != nullptr)
{
- vector<ContractDefinition const*> currentBases = m_currentContract->linearizedBaseContracts();
+ auto const& currentBases = m_currentContract->annotation().linearizedBaseContracts;
if (find(currentBases.begin(), currentBases.end(), &contract) != currentBases.end())
// We are accessing the type of a base contract, so add all public and protected
// members. Note that this does not add inherited functions on purpose.
@@ -1687,7 +1638,7 @@ ModifierType::ModifierType(const ModifierDefinition& _modifier)
TypePointers params;
params.reserve(_modifier.parameters().size());
for (ASTPointer<VariableDeclaration> const& var: _modifier.parameters())
- params.push_back(var->type());
+ params.push_back(var->annotation().type);
swap(params, m_parameterTypes);
}