diff options
Diffstat (limited to 'libsolidity')
29 files changed, 2035 insertions, 1587 deletions
diff --git a/libsolidity/AST.cpp b/libsolidity/AST.cpp index daa84016..4cd6cc0f 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,21 @@ #include <libdevcore/SHA3.h> using namespace std; +using namespace dev; +using namespace dev::solidity; -namespace dev +ASTNode::ASTNode(SourceLocation const& _location): + m_location(_location) { -namespace solidity -{ - -TypeError ASTNode::createTypeError(string const& _description) const -{ - return TypeError() << errinfo_sourceLocation(location()) << errinfo_comment(_description); } -TypePointer ContractDefinition::type(ContractDefinition const* _currentContract) const +ASTNode::~ASTNode() { - return make_shared<TypeType>(make_shared<ContractType>(*this), _currentContract); } -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 +74,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 +105,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 +125,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,45 +183,9 @@ 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); -} - -void InheritanceSpecifier::checkTypeRequirements() -{ - 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." - )); + return make_shared<TypeType>(make_shared<ContractType>(*this), m_currentContract); } TypePointer StructDefinition::type(ContractDefinition const*) const @@ -499,38 +193,11 @@ TypePointer StructDefinition::type(ContractDefinition const*) const return make_shared<TypeType>(make_shared<StructType>(*this)); } -void StructDefinition::checkMemberTypes() const -{ - for (ASTPointer<VariableDeclaration> const& member: members()) - if (!member->type()->canBeStored()) - BOOST_THROW_EXCEPTION(member->createTypeError("Type cannot be used in struct.")); -} - -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 +210,25 @@ TypePointer FunctionDefinition::type(ContractDefinition const*) const return make_shared<FunctionType>(*this); } -void FunctionDefinition::checkTypeRequirements() -{ - 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(); -} - string FunctionDefinition::externalSignature() const { return FunctionType(*this).externalSignature(name()); } -bool VariableDeclaration::isLValue() const +TypePointer ModifierDefinition::type(ContractDefinition const*) const { - // External function parameters and constant declared variables are Read-Only - return !isExternalCallableParameter() && !m_isConstant; + return make_shared<ModifierType>(*this); } -void VariableDeclaration::checkTypeRequirements() +TypePointer EventDefinition::type(ContractDefinition const*) 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); + return make_shared<FunctionType>(*this); +} - 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 +257,13 @@ bool VariableDeclaration::isExternalCallableParameter() const return false; } -TypePointer ModifierDefinition::type(ContractDefinition const*) const +bool VariableDeclaration::canHaveAutoType() 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() -{ - 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; -} - -void NewExpression::checkTypeRequirements(TypePointers 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 - ); -} - -void MemberAccess::checkTypeRequirements(TypePointers const* _argumentTypes) -{ - 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; -} - -void IndexAccess::checkTypeRequirements(TypePointers 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() + - ")" - )); - } -} - -void Identifier::checkTypeRequirements(TypePointers const* _argumentTypes) -{ - 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.")); -} - -Declaration const& Identifier::referencedDeclaration() const -{ - solAssert(!!m_referencedDeclaration, "Identifier not resolved."); - return *m_referencedDeclaration; -} - -void Identifier::overloadResolution(TypePointers const& _argumentTypes) -{ - 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.")); -} - -void ElementaryTypeNameExpression::checkTypeRequirements(TypePointers const*) -{ - m_type = make_shared<TypeType>(Type::fromElementaryTypeName(m_typeToken)); + auto const* callable = dynamic_cast<CallableDeclaration const*>(scope()); + return (!!callable && !isCallableParameter()); } -void Literal::checkTypeRequirements(TypePointers const*) +TypePointer VariableDeclaration::type(ContractDefinition const*) const { - m_type = Type::forLiteral(*this); - if (!m_type) - BOOST_THROW_EXCEPTION(createTypeError("Invalid literal value.")); -} - -} + return annotation().type; } diff --git a/libsolidity/AST.h b/libsolidity/AST.h index de0ef3ef..08d26b70 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 { @@ -41,6 +42,7 @@ namespace solidity class ASTVisitor; class ASTConstVisitor; +struct ASTAnnotation; /** @@ -51,9 +53,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 +78,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 + ASTAnnotation& annotation() const { return const_cast<ASTAnnotation&>(m_annotation); } + ///@{ ///@name equality operators /// Equality relies on the fact that nodes cannot be copied. @@ -86,6 +90,8 @@ public: private: SourceLocation m_location; + + ASTAnnotation m_annotation; }; /** @@ -151,12 +157,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 +213,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 +226,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 +244,6 @@ public: ): Declaration(_location, _name), Documented(_documentation), - ImplementationOptional(true), m_baseContracts(_baseContracts), m_definedStructs(_definedStructs), m_definedEnums(_definedEnums), @@ -261,24 +267,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 +286,9 @@ 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; - - std::vector<std::pair<FixedHash<4>, FunctionTypePointer>> const& interfaceFunctionList() const; + virtual TypePointer type(ContractDefinition const* m_currentContract) const override; +private: std::vector<ASTPointer<InheritanceSpecifier>> m_baseContracts; std::vector<ASTPointer<StructDefinition>> m_definedStructs; std::vector<ASTPointer<EnumDefinition>> m_definedEnums; @@ -327,18 +311,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; @@ -347,41 +332,38 @@ private: class StructDefinition: public Declaration { public: - StructDefinition(SourceLocation const& _location, - ASTPointer<ASTString> const& _name, - std::vector<ASTPointer<VariableDeclaration>> const& _members): + StructDefinition( + SourceLocation const& _location, + ASTPointer<ASTString> const& _name, + std::vector<ASTPointer<VariableDeclaration>> const& _members + ): Declaration(_location, _name), m_members(_members) {} virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTConstVisitor& _visitor) const override; 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; @@ -392,14 +374,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; }; /** @@ -488,17 +470,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; @@ -537,28 +517,27 @@ 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; + protected: Visibility defaultVisibility() const override { return Visibility::Internal; } @@ -569,8 +548,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 }; /** @@ -597,9 +574,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; @@ -611,8 +586,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; @@ -621,9 +599,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; @@ -653,12 +628,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; @@ -678,7 +648,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; @@ -696,11 +666,6 @@ public: explicit TypeName(SourceLocation const& _location): ASTNode(_location) {} 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; }; /** @@ -717,7 +682,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; } @@ -732,19 +696,14 @@ 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; } private: ASTPointer<ASTString> m_name; - - Declaration const* m_referencedDeclaration; }; /** @@ -758,7 +717,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; } @@ -779,7 +737,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(); } @@ -802,11 +759,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; }; /** @@ -820,8 +772,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; }; @@ -837,8 +787,6 @@ public: virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTConstVisitor& _visitor) const override; - - virtual void checkTypeRequirements() override { } }; /** @@ -854,7 +802,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; } @@ -884,7 +831,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; } @@ -900,19 +846,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(); } @@ -936,7 +884,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 @@ -945,27 +892,20 @@ 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(); } 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; }; /** @@ -977,7 +917,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 {}; }; /** @@ -992,7 +931,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(); } @@ -1011,7 +949,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; } @@ -1033,32 +970,6 @@ 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; }; /// Assignment, can also be a compound assignment. @@ -1066,16 +977,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; } @@ -1103,7 +1019,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; } @@ -1130,21 +1045,15 @@ 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; } 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; }; /** @@ -1158,18 +1067,11 @@ 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; - private: ASTPointer<Expression> m_expression; std::vector<ASTPointer<Expression>> m_arguments; @@ -1186,15 +1088,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; }; /** @@ -1210,18 +1108,10 @@ 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; 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; }; /** @@ -1235,7 +1125,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(); } @@ -1265,44 +1154,11 @@ 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; } - 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; }; /** @@ -1320,7 +1176,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; } @@ -1354,7 +1209,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..9564d2d0 --- /dev/null +++ b/libsolidity/ASTAnnotations.h @@ -0,0 +1,78 @@ +/* + 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> + +namespace dev +{ +namespace solidity +{ + +class ASTNode; +class ContractDefinition; +class Declaration; +class ParameterList; +class Type; +using TypePointer = std::shared_ptr<Type const>; + +struct ASTAnnotation +{ + ///@TODO save space here - we do not need all members for all types. + + /// For expression: Inferred type. - For type declaration: Declared type. - For variable declaration: Type of variable. + TypePointer type; + /// For expression: Whether it is an LValue (i.e. something that can be assigned to). + bool isLValue = false; + /// For expression: Whether the expression is used in a context where the LValue is actually required. + bool lValueRequested = false; + /// For expressions: Types of arguments if the expression is a function that is called - used + /// for overload resolution. + std::shared_ptr<std::vector<TypePointer>> argumentTypes; + /// For contract: Whether all functions are implemented. + bool isFullyImplemented = true; + /// For contract: List of all (direct and indirect) base contracts in order from derived to + /// base, including the contract itself. + std::vector<ContractDefinition const*> linearizedBaseContracts; + /// For member access and Identifer: Referenced declaration, set during overload resolution stage. + Declaration const* referencedDeclaration = nullptr; + /// For Identifier: List of possible declarations it could refer to. + std::vector<Declaration const*> overloadedDeclarations; + /// For function call: Whether this is an explicit type conversion. + bool isTypeConversion = false; + /// For function call: Whether this is a struct constructor call. + bool isStructConstructorCall = false; + /// For Return statement: Reference to the return parameters of the function. + ParameterList const* functionReturnParameters = nullptr; + /// For Identifier: Stores a reference to the current contract. + /// This is needed because types of base contracts change depending on the context. + ContractDefinition const* contractScope = nullptr; + /// For BinaryOperation: The common type that is used for the operation, not necessarily the result type (e.g. for + /// comparisons, this is always bool). + TypePointer commonType; +}; + +} +} 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..b8139187 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> @@ -123,7 +124,12 @@ void CompilerStack::parse() { m_globalContext->setCurrentContract(*contract); resolver.updateDeclaration(*m_globalContext->currentThis()); - resolver.checkTypeRequirements(*contract); + TypeChecker typeChecker; + bool typesFine = typeChecker.checkTypeRequirements(*contract); + if (!typesFine) + BOOST_THROW_EXCEPTION(*typeChecker.errors().front()); + //@todo extract warnings and errors + // store whether we had an error contract->setDevDocumentation(interfaceHandler.devDocumentation(*contract)); contract->setUserDocumentation(interfaceHandler.userDocumentation(*contract)); m_contracts[contract->name()].contract = contract; @@ -153,12 +159,14 @@ void CompilerStack::compile(bool _optimize, unsigned _runs) if (!m_parseSuccessful) parse(); + //@todo do not compile or error (also in other places) + 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); diff --git a/libsolidity/CompilerStack.h b/libsolidity/CompilerStack.h index 99e8af1a..4d9c1ec5 100644 --- a/libsolidity/CompilerStack.h +++ b/libsolidity/CompilerStack.h @@ -79,7 +79,9 @@ 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 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..d5cb7eb5 100644 --- a/libsolidity/Exceptions.h +++ b/libsolidity/Exceptions.h @@ -38,6 +38,7 @@ struct DeclarationError: virtual Exception {}; 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 1b9f2150..1fa70797 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 << u256(max(32u, structType.calldataEncodedSize(true))); @@ -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,7 +1267,7 @@ 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); } @@ -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..4bd38b49 100644 --- a/libsolidity/LValue.cpp +++ b/libsolidity/LValue.cpp @@ -32,7 +32,7 @@ using namespace solidity; StackVariable::StackVariable(CompilerContext& _compilerContext, Declaration const& _declaration): - LValue(_compilerContext, *_declaration.type()), + LValue(_compilerContext, *_declaration.annotation().type), m_baseStackOffset(m_context.baseStackOffsetOfVariable(_declaration)), m_size(m_dataType.sizeOnStack()) { @@ -132,7 +132,7 @@ void MemoryItem::setToZero(SourceLocation const&, bool _removeReference) const } StorageItem::StorageItem(CompilerContext& _compilerContext, Declaration const& _declaration): - StorageItem(_compilerContext, *_declaration.type()) + StorageItem(_compilerContext, *_declaration.annotation().type) { auto const& location = m_context.storageLocationOfVariable(_declaration); m_context << location.first << u256(location.second); 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..b2cdacc4 --- /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 (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..1bb1381b --- /dev/null +++ b/libsolidity/TypeChecker.cpp @@ -0,0 +1,1147 @@ +/* + 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(move(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(move(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(move(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() ? + _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(); + _modifier.annotation().argumentTypes = make_shared<TypePointers>(); + for (ASTPointer<Expression> const& argument: arguments) + { + argument->accept(*this); + _modifier.annotation().argumentTypes->push_back(type(*argument)); + } + _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) +{ + ASTAnnotation& 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(move(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..90e2a01b --- /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<Exception 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<Exception 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); } |