diff options
36 files changed, 2460 insertions, 1814 deletions
diff --git a/libsolidity/AST.cpp b/libsolidity/AST.cpp index daa84016..00b51c42 100644 --- a/libsolidity/AST.cpp +++ b/libsolidity/AST.cpp @@ -22,7 +22,6 @@ #include <algorithm> #include <functional> -#include <boost/range/adaptor/reversed.hpp> #include <libsolidity/Utils.h> #include <libsolidity/AST.h> #include <libsolidity/ASTVisitor.h> @@ -32,83 +31,29 @@ #include <libdevcore/SHA3.h> using namespace std; +using namespace dev; +using namespace dev::solidity; -namespace dev -{ -namespace solidity +ASTNode::ASTNode(SourceLocation const& _location): + m_location(_location) { +} -TypeError ASTNode::createTypeError(string const& _description) const +ASTNode::~ASTNode() { - return TypeError() << errinfo_sourceLocation(location()) << errinfo_comment(_description); + delete m_annotation; } -TypePointer ContractDefinition::type(ContractDefinition const* _currentContract) const +ASTAnnotation& ASTNode::annotation() const { - return make_shared<TypeType>(make_shared<ContractType>(*this), _currentContract); + if (!m_annotation) + m_annotation = new ASTAnnotation(); + return *m_annotation; } -void ContractDefinition::checkTypeRequirements() +TypeError ASTNode::createTypeError(string const& _description) const { - for (ASTPointer<InheritanceSpecifier> const& baseSpecifier: baseContracts()) - baseSpecifier->checkTypeRequirements(); - - checkDuplicateFunctions(); - checkIllegalOverrides(); - checkAbstractFunctions(); - checkAbstractConstructors(); - - FunctionDefinition const* functionDefinition = constructor(); - if (functionDefinition && !functionDefinition->returnParameters().empty()) - BOOST_THROW_EXCEPTION(functionDefinition->returnParameterList()->createTypeError( - "Non-empty \"returns\" directive for constructor." - )); - - FunctionDefinition const* fallbackFunction = nullptr; - for (ASTPointer<FunctionDefinition> const& function: definedFunctions()) - { - if (function->name().empty()) - { - if (fallbackFunction) - BOOST_THROW_EXCEPTION(DeclarationError() << errinfo_comment("Only one fallback function is allowed.")); - else - { - fallbackFunction = function.get(); - if (!fallbackFunction->parameters().empty()) - BOOST_THROW_EXCEPTION(fallbackFunction->parameterList().createTypeError("Fallback function cannot take parameters.")); - } - } - if (!function->isFullyImplemented()) - setFullyImplemented(false); - } - - for (ASTPointer<VariableDeclaration> const& variable: m_stateVariables) - variable->checkTypeRequirements(); - - for (ASTPointer<EventDefinition> const& event: events()) - event->checkTypeRequirements(); - - for (ASTPointer<ModifierDefinition> const& modifier: functionModifiers()) - modifier->checkTypeRequirements(); - - for (ASTPointer<FunctionDefinition> const& function: definedFunctions()) - function->checkTypeRequirements(); - - checkExternalTypeClashes(); - // check for hash collisions in function signatures - set<FixedHash<4>> hashes; - for (auto const& it: interfaceFunctionList()) - { - FixedHash<4> const& hash = it.first; - if (hashes.count(hash)) - BOOST_THROW_EXCEPTION(createTypeError( - string("Function signature hash collision for ") + it.second->externalSignature() - )); - hashes.insert(hash); - } - - if (isLibrary()) - checkLibraryRequirements(); + return TypeError() << errinfo_sourceLocation(location()) << errinfo_comment(_description); } map<FixedHash<4>, FunctionTypePointer> ContractDefinition::interfaceFunctions() const @@ -137,227 +82,20 @@ FunctionDefinition const* ContractDefinition::constructor() const FunctionDefinition const* ContractDefinition::fallbackFunction() const { - for (ContractDefinition const* contract: linearizedBaseContracts()) + for (ContractDefinition const* contract: annotation().linearizedBaseContracts) for (ASTPointer<FunctionDefinition> const& f: contract->definedFunctions()) if (f->name().empty()) return f.get(); return nullptr; } -void ContractDefinition::checkDuplicateFunctions() const -{ - /// Checks that two functions with the same name defined in this contract have different - /// argument types and that there is at most one constructor. - map<string, vector<FunctionDefinition const*>> functions; - for (ASTPointer<FunctionDefinition> const& function: definedFunctions()) - functions[function->name()].push_back(function.get()); - - if (functions[name()].size() > 1) - { - SecondarySourceLocation ssl; - auto it = functions[name()].begin(); - ++it; - for (; it != functions[name()].end(); ++it) - ssl.append("Another declaration is here:", (*it)->location()); - - BOOST_THROW_EXCEPTION( - DeclarationError() << - errinfo_sourceLocation(functions[name()].front()->location()) << - errinfo_comment("More than one constructor defined.") << - errinfo_secondarySourceLocation(ssl) - ); - } - for (auto const& it: functions) - { - vector<FunctionDefinition const*> const& overloads = it.second; - for (size_t i = 0; i < overloads.size(); ++i) - for (size_t j = i + 1; j < overloads.size(); ++j) - if (FunctionType(*overloads[i]).hasEqualArgumentTypes(FunctionType(*overloads[j]))) - BOOST_THROW_EXCEPTION( - DeclarationError() << - errinfo_sourceLocation(overloads[j]->location()) << - errinfo_comment("Function with same name and arguments defined twice.") << - errinfo_secondarySourceLocation(SecondarySourceLocation().append( - "Other declaration is here:", overloads[i]->location()) - ) - - ); - } -} - -void ContractDefinition::checkAbstractFunctions() -{ - // Mapping from name to function definition (exactly one per argument type equality class) and - // flag to indicate whether it is fully implemented. - using FunTypeAndFlag = std::pair<FunctionTypePointer, bool>; - map<string, vector<FunTypeAndFlag>> functions; - - // Search from base to derived - for (ContractDefinition const* contract: boost::adaptors::reverse(linearizedBaseContracts())) - for (ASTPointer<FunctionDefinition> const& function: contract->definedFunctions()) - { - auto& overloads = functions[function->name()]; - FunctionTypePointer funType = make_shared<FunctionType>(*function); - auto it = find_if(overloads.begin(), overloads.end(), [&](FunTypeAndFlag const& _funAndFlag) - { - return funType->hasEqualArgumentTypes(*_funAndFlag.first); - }); - if (it == overloads.end()) - overloads.push_back(make_pair(funType, function->isFullyImplemented())); - else if (it->second) - { - if (!function->isFullyImplemented()) - BOOST_THROW_EXCEPTION(function->createTypeError("Redeclaring an already implemented function as abstract")); - } - else if (function->isFullyImplemented()) - it->second = true; - } - - // Set to not fully implemented if at least one flag is false. - for (auto const& it: functions) - for (auto const& funAndFlag: it.second) - if (!funAndFlag.second) - { - setFullyImplemented(false); - return; - } -} - -void ContractDefinition::checkAbstractConstructors() -{ - set<ContractDefinition const*> argumentsNeeded; - // check that we get arguments for all base constructors that need it. - // If not mark the contract as abstract (not fully implemented) - - vector<ContractDefinition const*> const& bases = linearizedBaseContracts(); - for (ContractDefinition const* contract: bases) - if (FunctionDefinition const* constructor = contract->constructor()) - if (contract != this && !constructor->parameters().empty()) - argumentsNeeded.insert(contract); - - for (ContractDefinition const* contract: bases) - { - if (FunctionDefinition const* constructor = contract->constructor()) - for (auto const& modifier: constructor->modifiers()) - { - auto baseContract = dynamic_cast<ContractDefinition const*>( - &modifier->name()->referencedDeclaration() - ); - if (baseContract) - argumentsNeeded.erase(baseContract); - } - - - for (ASTPointer<InheritanceSpecifier> const& base: contract->baseContracts()) - { - auto baseContract = dynamic_cast<ContractDefinition const*>( - &base->name()->referencedDeclaration() - ); - solAssert(baseContract, ""); - if (!base->arguments().empty()) - argumentsNeeded.erase(baseContract); - } - } - if (!argumentsNeeded.empty()) - setFullyImplemented(false); -} - -void ContractDefinition::checkIllegalOverrides() const -{ - // TODO unify this at a later point. for this we need to put the constness and the access specifier - // into the types - map<string, vector<FunctionDefinition const*>> functions; - map<string, ModifierDefinition const*> modifiers; - - // We search from derived to base, so the stored item causes the error. - for (ContractDefinition const* contract: linearizedBaseContracts()) - { - for (ASTPointer<FunctionDefinition> const& function: contract->definedFunctions()) - { - if (function->isConstructor()) - continue; // constructors can neither be overridden nor override anything - string const& name = function->name(); - if (modifiers.count(name)) - BOOST_THROW_EXCEPTION(modifiers[name]->createTypeError("Override changes function to modifier.")); - FunctionType functionType(*function); - // function should not change the return type - for (FunctionDefinition const* overriding: functions[name]) - { - FunctionType overridingType(*overriding); - if (!overridingType.hasEqualArgumentTypes(functionType)) - continue; - if ( - overriding->visibility() != function->visibility() || - overriding->isDeclaredConst() != function->isDeclaredConst() || - overridingType != functionType - ) - BOOST_THROW_EXCEPTION(overriding->createTypeError("Override changes extended function signature.")); - } - functions[name].push_back(function.get()); - } - for (ASTPointer<ModifierDefinition> const& modifier: contract->functionModifiers()) - { - string const& name = modifier->name(); - ModifierDefinition const*& override = modifiers[name]; - if (!override) - override = modifier.get(); - else if (ModifierType(*override) != ModifierType(*modifier)) - BOOST_THROW_EXCEPTION(override->createTypeError("Override changes modifier signature.")); - if (!functions[name].empty()) - BOOST_THROW_EXCEPTION(override->createTypeError("Override changes modifier to function.")); - } - } -} - -void ContractDefinition::checkExternalTypeClashes() const -{ - map<string, vector<pair<Declaration const*, shared_ptr<FunctionType>>>> externalDeclarations; - for (ContractDefinition const* contract: linearizedBaseContracts()) - { - for (ASTPointer<FunctionDefinition> const& f: contract->definedFunctions()) - if (f->isPartOfExternalInterface()) - { - auto functionType = make_shared<FunctionType>(*f); - externalDeclarations[functionType->externalSignature(f->name())].push_back( - make_pair(f.get(), functionType) - ); - } - for (ASTPointer<VariableDeclaration> const& v: contract->stateVariables()) - if (v->isPartOfExternalInterface()) - { - auto functionType = make_shared<FunctionType>(*v); - externalDeclarations[functionType->externalSignature(v->name())].push_back( - make_pair(v.get(), functionType) - ); - } - } - for (auto const& it: externalDeclarations) - for (size_t i = 0; i < it.second.size(); ++i) - for (size_t j = i + 1; j < it.second.size(); ++j) - if (!it.second[i].second->hasEqualArgumentTypes(*it.second[j].second)) - BOOST_THROW_EXCEPTION(it.second[j].first->createTypeError( - "Function overload clash during conversion to external types for arguments." - )); -} - -void ContractDefinition::checkLibraryRequirements() const -{ - solAssert(m_isLibrary, ""); - if (!m_baseContracts.empty()) - BOOST_THROW_EXCEPTION(createTypeError("Library is not allowed to inherit.")); - - for (auto const& var: m_stateVariables) - if (!var->isConstant()) - BOOST_THROW_EXCEPTION(var->createTypeError("Library cannot have non-constant state variables")); -} - vector<ASTPointer<EventDefinition>> const& ContractDefinition::interfaceEvents() const { if (!m_interfaceEvents) { set<string> eventsSeen; m_interfaceEvents.reset(new vector<ASTPointer<EventDefinition>>()); - for (ContractDefinition const* contract: linearizedBaseContracts()) + for (ContractDefinition const* contract: annotation().linearizedBaseContracts) for (ASTPointer<EventDefinition> const& e: contract->events()) if (eventsSeen.count(e->name()) == 0) { @@ -375,7 +113,7 @@ vector<pair<FixedHash<4>, FunctionTypePointer>> const& ContractDefinition::inter set<string> functionsSeen; set<string> signaturesSeen; m_interfaceFunctionList.reset(new vector<pair<FixedHash<4>, FunctionTypePointer>>()); - for (ContractDefinition const* contract: linearizedBaseContracts()) + for (ContractDefinition const* contract: annotation().linearizedBaseContracts) { for (ASTPointer<FunctionDefinition> const& f: contract->definedFunctions()) { @@ -395,7 +133,7 @@ vector<pair<FixedHash<4>, FunctionTypePointer>> const& ContractDefinition::inter if (functionsSeen.count(v->name()) == 0 && v->isPartOfExternalInterface()) { FunctionType ftype(*v); - solAssert(v->type().get(), ""); + solAssert(!!v->annotation().type.get(), ""); functionsSeen.insert(v->name()); FixedHash<4> hash(dev::sha3(ftype.externalSignature(v->name()))); m_interfaceFunctionList->push_back(make_pair(hash, make_shared<FunctionType>(*v))); @@ -453,84 +191,35 @@ vector<Declaration const*> const& ContractDefinition::inheritableMembers() const return *m_inheritableMembers; } -TypePointer EnumValue::type(ContractDefinition const*) const +TypePointer ContractDefinition::type(ContractDefinition const* m_currentContract) const { - EnumDefinition const* parentDef = dynamic_cast<EnumDefinition const*>(scope()); - solAssert(parentDef, "Enclosing Scope of EnumValue was not set"); - return make_shared<EnumType>(*parentDef); + return make_shared<TypeType>(make_shared<ContractType>(*this), m_currentContract); } -void InheritanceSpecifier::checkTypeRequirements() +ContractDefinitionAnnotation& ContractDefinition::annotation() const { - m_baseName->checkTypeRequirements(nullptr); - for (ASTPointer<Expression> const& argument: m_arguments) - argument->checkTypeRequirements(nullptr); - - ContractDefinition const* base = dynamic_cast<ContractDefinition const*>(&m_baseName->referencedDeclaration()); - solAssert(base, "Base contract not available."); - - if (base->isLibrary()) - BOOST_THROW_EXCEPTION(createTypeError("Libraries cannot be inherited from.")); - - TypePointers parameterTypes = ContractType(*base).constructorType()->parameterTypes(); - if (!m_arguments.empty() && parameterTypes.size() != m_arguments.size()) - BOOST_THROW_EXCEPTION(createTypeError( - "Wrong argument count for constructor call: " + - toString(m_arguments.size()) + - " arguments given but expected " + - toString(parameterTypes.size()) + - "." - )); - - for (size_t i = 0; i < m_arguments.size(); ++i) - if (!m_arguments[i]->type()->isImplicitlyConvertibleTo(*parameterTypes[i])) - BOOST_THROW_EXCEPTION(m_arguments[i]->createTypeError( - "Invalid type for argument in constructor call. " - "Invalid implicit conversion from " + - m_arguments[i]->type()->toString() + - " to " + - parameterTypes[i]->toString() + - " requested." - )); + if (!m_annotation) + m_annotation = new ContractDefinitionAnnotation(); + return static_cast<ContractDefinitionAnnotation&>(*m_annotation); } -TypePointer StructDefinition::type(ContractDefinition const*) const +TypeNameAnnotation& TypeName::annotation() const { - return make_shared<TypeType>(make_shared<StructType>(*this)); + if (!m_annotation) + m_annotation = new TypeNameAnnotation(); + return static_cast<TypeNameAnnotation&>(*m_annotation); } -void StructDefinition::checkMemberTypes() const +TypePointer StructDefinition::type(ContractDefinition const*) const { - for (ASTPointer<VariableDeclaration> const& member: members()) - if (!member->type()->canBeStored()) - BOOST_THROW_EXCEPTION(member->createTypeError("Type cannot be used in struct.")); + return make_shared<TypeType>(make_shared<StructType>(*this)); } -void StructDefinition::checkRecursion() const +TypePointer EnumValue::type(ContractDefinition const*) const { - using StructPointer = StructDefinition const*; - using StructPointersSet = set<StructPointer>; - function<void(StructPointer,StructPointersSet const&)> check = [&](StructPointer _struct, StructPointersSet const& _parents) - { - if (_parents.count(_struct)) - BOOST_THROW_EXCEPTION( - ParserError() << - errinfo_sourceLocation(_struct->location()) << - errinfo_comment("Recursive struct definition.") - ); - set<StructDefinition const*> parents = _parents; - parents.insert(_struct); - for (ASTPointer<VariableDeclaration> const& member: _struct->members()) - if (member->type()->category() == Type::Category::Struct) - { - auto const& typeName = dynamic_cast<UserDefinedTypeName const&>(*member->typeName()); - check( - &dynamic_cast<StructDefinition const&>(*typeName.referencedDeclaration()), - parents - ); - } - }; - check(this, StructPointersSet{}); + auto parentDef = dynamic_cast<EnumDefinition const*>(scope()); + solAssert(parentDef, "Enclosing Scope of EnumValue was not set"); + return make_shared<EnumType>(*parentDef); } TypePointer EnumDefinition::type(ContractDefinition const*) const @@ -543,92 +232,32 @@ TypePointer FunctionDefinition::type(ContractDefinition const*) const return make_shared<FunctionType>(*this); } -void FunctionDefinition::checkTypeRequirements() +string FunctionDefinition::externalSignature() const { - for (ASTPointer<VariableDeclaration> const& var: parameters() + returnParameters()) - { - if (!var->type()->canLiveOutsideStorage()) - BOOST_THROW_EXCEPTION(var->createTypeError("Type is required to live outside storage.")); - if (visibility() >= Visibility::Public && !(var->type()->externalType())) - BOOST_THROW_EXCEPTION(var->createTypeError("Internal type is not allowed for public and external functions.")); - } - for (ASTPointer<ModifierInvocation> const& modifier: m_functionModifiers) - modifier->checkTypeRequirements(isConstructor() ? - dynamic_cast<ContractDefinition const&>(*scope()).linearizedBaseContracts() : - vector<ContractDefinition const*>()); - if (m_body) - m_body->checkTypeRequirements(); + return FunctionType(*this).externalSignature(name()); } -string FunctionDefinition::externalSignature() const +TypePointer ModifierDefinition::type(ContractDefinition const*) const { - return FunctionType(*this).externalSignature(name()); + return make_shared<ModifierType>(*this); } -bool VariableDeclaration::isLValue() const +TypePointer EventDefinition::type(ContractDefinition const*) const { - // External function parameters and constant declared variables are Read-Only - return !isExternalCallableParameter() && !m_isConstant; + return make_shared<FunctionType>(*this); } -void VariableDeclaration::checkTypeRequirements() +UserDefinedTypeNameAnnotation& UserDefinedTypeName::annotation() const { - // Variables can be declared without type (with "var"), in which case the first assignment - // sets the type. - // Note that assignments before the first declaration are legal because of the special scoping - // rules inherited from JavaScript. - if (m_isConstant) - { - if (!dynamic_cast<ContractDefinition const*>(scope())) - BOOST_THROW_EXCEPTION(createTypeError("Illegal use of \"constant\" specifier.")); - if (!m_value) - BOOST_THROW_EXCEPTION(createTypeError("Uninitialized \"constant\" variable.")); - if (m_type && !m_type->isValueType()) - { - // TODO: const is implemented only for uint, bytesXX, string and enums types. - bool constImplemented = false; - if (auto arrayType = dynamic_cast<ArrayType const*>(m_type.get())) - constImplemented = arrayType->isByteArray(); - if (!constImplemented) - BOOST_THROW_EXCEPTION(createTypeError( - "Illegal use of \"constant\" specifier. \"constant\" " - "is not yet implemented for this type." - )); - } - } - if (m_type) - { - if (m_value) - m_value->expectType(*m_type); - } - else - { - if (!m_value) - // This feature might be extended in the future. - BOOST_THROW_EXCEPTION(createTypeError("Assignment necessary for type detection.")); - m_value->checkTypeRequirements(nullptr); + if (!m_annotation) + m_annotation = new UserDefinedTypeNameAnnotation(); + return static_cast<UserDefinedTypeNameAnnotation&>(*m_annotation); +} - TypePointer const& type = m_value->type(); - if ( - type->category() == Type::Category::IntegerConstant && - !dynamic_pointer_cast<IntegerConstantType const>(type)->integerType() - ) - BOOST_THROW_EXCEPTION(m_value->createTypeError("Invalid integer constant " + type->toString() + ".")); - else if (type->category() == Type::Category::Void) - BOOST_THROW_EXCEPTION(createTypeError("Variable cannot have void type.")); - m_type = type->mobileType(); - } - solAssert(!!m_type, ""); - if (!m_isStateVariable) - { - if (m_type->dataStoredIn(DataLocation::Memory) || m_type->dataStoredIn(DataLocation::CallData)) - if (!m_type->canLiveOutsideStorage()) - BOOST_THROW_EXCEPTION(createTypeError( - "Type " + m_type->toString() + " is only valid in storage." - )); - } - else if (visibility() >= Visibility::Public && !FunctionType(*this).externalType()) - BOOST_THROW_EXCEPTION(createTypeError("Internal type is not allowed for public state variables.")); +bool VariableDeclaration::isLValue() const +{ + // External function parameters and constant declared variables are Read-Only + return !isExternalCallableParameter() && !m_isConstant; } bool VariableDeclaration::isCallableParameter() const @@ -657,578 +286,62 @@ bool VariableDeclaration::isExternalCallableParameter() const return false; } -TypePointer ModifierDefinition::type(ContractDefinition const*) const -{ - return make_shared<ModifierType>(*this); -} - -void ModifierDefinition::checkTypeRequirements() -{ - m_body->checkTypeRequirements(); -} - -void ModifierInvocation::checkTypeRequirements(vector<ContractDefinition const*> const& _bases) -{ - TypePointers argumentTypes; - for (ASTPointer<Expression> const& argument: m_arguments) - { - argument->checkTypeRequirements(nullptr); - argumentTypes.push_back(argument->type()); - } - m_modifierName->checkTypeRequirements(&argumentTypes); - - auto const* declaration = &m_modifierName->referencedDeclaration(); - vector<ASTPointer<VariableDeclaration>> emptyParameterList; - vector<ASTPointer<VariableDeclaration>> const* parameters = nullptr; - if (auto modifier = dynamic_cast<ModifierDefinition const*>(declaration)) - parameters = &modifier->parameters(); - else - // check parameters for Base constructors - for (ContractDefinition const* base: _bases) - if (declaration == base) - { - if (auto referencedConstructor = base->constructor()) - parameters = &referencedConstructor->parameters(); - else - parameters = &emptyParameterList; - break; - } - if (!parameters) - BOOST_THROW_EXCEPTION(createTypeError("Referenced declaration is neither modifier nor base class.")); - if (parameters->size() != m_arguments.size()) - BOOST_THROW_EXCEPTION(createTypeError( - "Wrong argument count for modifier invocation: " + - toString(m_arguments.size()) + - " arguments given but expected " + - toString(parameters->size()) + - "." - )); - for (size_t i = 0; i < m_arguments.size(); ++i) - if (!m_arguments[i]->type()->isImplicitlyConvertibleTo(*(*parameters)[i]->type())) - BOOST_THROW_EXCEPTION(m_arguments[i]->createTypeError( - "Invalid type for argument in modifier invocation. " - "Invalid implicit conversion from " + - m_arguments[i]->type()->toString() + - " to " + - (*parameters)[i]->type()->toString() + - " requested." - )); -} - -void EventDefinition::checkTypeRequirements() -{ - int numIndexed = 0; - for (ASTPointer<VariableDeclaration> const& var: parameters()) - { - if (var->isIndexed()) - numIndexed++; - if (numIndexed > 3) - BOOST_THROW_EXCEPTION(createTypeError("More than 3 indexed arguments for event.")); - if (!var->type()->canLiveOutsideStorage()) - BOOST_THROW_EXCEPTION(var->createTypeError("Type is required to live outside storage.")); - if (!var->type()->externalType()) - BOOST_THROW_EXCEPTION(var->createTypeError("Internal type is not allowed as event parameter type.")); - } -} - -void Block::checkTypeRequirements() -{ - for (shared_ptr<Statement> const& statement: m_statements) - statement->checkTypeRequirements(); -} - -void IfStatement::checkTypeRequirements() -{ - m_condition->expectType(BoolType()); - m_trueBody->checkTypeRequirements(); - if (m_falseBody) - m_falseBody->checkTypeRequirements(); -} - -void WhileStatement::checkTypeRequirements() -{ - m_condition->expectType(BoolType()); - m_body->checkTypeRequirements(); -} - -void ForStatement::checkTypeRequirements() -{ - if (m_initExpression) - m_initExpression->checkTypeRequirements(); - if (m_condExpression) - m_condExpression->expectType(BoolType()); - if (m_loopExpression) - m_loopExpression->checkTypeRequirements(); - m_body->checkTypeRequirements(); -} - -void Return::checkTypeRequirements() +bool VariableDeclaration::canHaveAutoType() const { - if (!m_expression) - return; - if (!m_returnParameters) - BOOST_THROW_EXCEPTION(createTypeError("Return arguments not allowed.")); - if (m_returnParameters->parameters().size() != 1) - BOOST_THROW_EXCEPTION(createTypeError( - "Different number of arguments in return statement " - "than in returns declaration." - )); - // this could later be changed such that the paramaters type is an anonymous struct type, - // but for now, we only allow one return parameter - m_expression->expectType(*m_returnParameters->parameters().front()->type()); -} - -void VariableDeclarationStatement::checkTypeRequirements() -{ - m_variable->checkTypeRequirements(); -} - -void Assignment::checkTypeRequirements(TypePointers const*) -{ - m_leftHandSide->checkTypeRequirements(nullptr); - m_leftHandSide->requireLValue(); - if (m_leftHandSide->type()->category() == Type::Category::Mapping) - BOOST_THROW_EXCEPTION(createTypeError("Mappings cannot be assigned to.")); - m_type = m_leftHandSide->type(); - if (m_assigmentOperator == Token::Assign) - m_rightHandSide->expectType(*m_type); - else - { - // compound assignment - m_rightHandSide->checkTypeRequirements(nullptr); - TypePointer resultType = m_type->binaryOperatorResult(Token::AssignmentToBinaryOp(m_assigmentOperator), - m_rightHandSide->type()); - if (!resultType || *resultType != *m_type) - BOOST_THROW_EXCEPTION(createTypeError( - "Operator " + - string(Token::toString(m_assigmentOperator)) + - " not compatible with types " + - m_type->toString() + - " and " + - m_rightHandSide->type()->toString() - )); - } -} - -void ExpressionStatement::checkTypeRequirements() -{ - m_expression->checkTypeRequirements(nullptr); - if (m_expression->type()->category() == Type::Category::IntegerConstant) - if (!dynamic_pointer_cast<IntegerConstantType const>(m_expression->type())->integerType()) - BOOST_THROW_EXCEPTION(m_expression->createTypeError("Invalid integer constant.")); -} - -void Expression::expectType(Type const& _expectedType) -{ - checkTypeRequirements(nullptr); - Type const& currentType = *type(); - if (!currentType.isImplicitlyConvertibleTo(_expectedType)) - BOOST_THROW_EXCEPTION( - createTypeError( - "Type " + - currentType.toString() + - " is not implicitly convertible to expected type " + - _expectedType.toString() + - "." - ) - ); -} - -void Expression::requireLValue() -{ - if (!isLValue()) - BOOST_THROW_EXCEPTION(createTypeError("Expression has to be an lvalue.")); - m_lvalueRequested = true; -} - -void UnaryOperation::checkTypeRequirements(TypePointers const*) -{ - // Inc, Dec, Add, Sub, Not, BitNot, Delete - m_subExpression->checkTypeRequirements(nullptr); - if (m_operator == Token::Value::Inc || m_operator == Token::Value::Dec || m_operator == Token::Value::Delete) - m_subExpression->requireLValue(); - m_type = m_subExpression->type()->unaryOperatorResult(m_operator); - if (!m_type) - BOOST_THROW_EXCEPTION(createTypeError("Unary operator not compatible with type.")); -} - -void BinaryOperation::checkTypeRequirements(TypePointers const*) -{ - m_left->checkTypeRequirements(nullptr); - m_right->checkTypeRequirements(nullptr); - m_commonType = m_left->type()->binaryOperatorResult(m_operator, m_right->type()); - if (!m_commonType) - BOOST_THROW_EXCEPTION(createTypeError( - "Operator " + string(Token::toString(m_operator)) + - " not compatible with types " + - m_left->type()->toString() + - " and " + - m_right->type()->toString() - )); - m_type = Token::isCompareOp(m_operator) ? make_shared<BoolType>() : m_commonType; -} - -void FunctionCall::checkTypeRequirements(TypePointers const*) -{ - bool isPositionalCall = m_names.empty(); - - // we need to check arguments' type first as they will be forwarded to - // m_expression->checkTypeRequirements - TypePointers argumentTypes; - for (ASTPointer<Expression> const& argument: m_arguments) - { - argument->checkTypeRequirements(nullptr); - // only store them for positional calls - if (isPositionalCall) - argumentTypes.push_back(argument->type()); - } - - m_expression->checkTypeRequirements(isPositionalCall ? &argumentTypes : nullptr); - - TypePointer const& expressionType = m_expression->type(); - FunctionTypePointer functionType; - if (isTypeConversion()) - { - TypeType const& type = dynamic_cast<TypeType const&>(*expressionType); - if (m_arguments.size() != 1) - BOOST_THROW_EXCEPTION(createTypeError("Exactly one argument expected for explicit type conversion.")); - if (!isPositionalCall) - BOOST_THROW_EXCEPTION(createTypeError("Type conversion cannot allow named arguments.")); - m_type = type.actualType(); - auto argType = m_arguments.front()->type(); - if (auto argRefType = dynamic_cast<ReferenceType const*>(argType.get())) - // do not change the data location when converting - // (data location cannot yet be specified for type conversions) - m_type = ReferenceType::copyForLocationIfReference(argRefType->location(), m_type); - if (!argType->isExplicitlyConvertibleTo(*m_type)) - BOOST_THROW_EXCEPTION(createTypeError("Explicit type conversion not allowed.")); - - return; - } - - /// For error message: Struct members that were removed during conversion to memory. - set<string> membersRemovedForStructConstructor; - if (isStructConstructorCall()) - { - TypeType const& type = dynamic_cast<TypeType const&>(*expressionType); - auto const& structType = dynamic_cast<StructType const&>(*type.actualType()); - functionType = structType.constructorType(); - membersRemovedForStructConstructor = structType.membersMissingInMemory(); - } - else - functionType = dynamic_pointer_cast<FunctionType const>(expressionType); - - if (!functionType) - BOOST_THROW_EXCEPTION(createTypeError("Type is not callable.")); - - //@todo would be nice to create a struct type from the arguments - // and then ask if that is implicitly convertible to the struct represented by the - // function parameters - TypePointers const& parameterTypes = functionType->parameterTypes(); - if (!functionType->takesArbitraryParameters() && parameterTypes.size() != m_arguments.size()) - { - string msg = - "Wrong argument count for function call: " + - toString(m_arguments.size()) + - " arguments given but expected " + - toString(parameterTypes.size()) + - "."; - // Extend error message in case we try to construct a struct with mapping member. - if (isStructConstructorCall() && !membersRemovedForStructConstructor.empty()) - { - msg += " Members that have to be skipped in memory:"; - for (auto const& member: membersRemovedForStructConstructor) - msg += " " + member; - } - BOOST_THROW_EXCEPTION(createTypeError(msg)); - } - - if (isPositionalCall) - { - // call by positional arguments - for (size_t i = 0; i < m_arguments.size(); ++i) - if ( - !functionType->takesArbitraryParameters() && - !m_arguments[i]->type()->isImplicitlyConvertibleTo(*parameterTypes[i]) - ) - BOOST_THROW_EXCEPTION(m_arguments[i]->createTypeError( - "Invalid type for argument in function call. " - "Invalid implicit conversion from " + - m_arguments[i]->type()->toString() + - " to " + - parameterTypes[i]->toString() + - " requested." - )); - } - else - { - // call by named arguments - if (functionType->takesArbitraryParameters()) - BOOST_THROW_EXCEPTION(createTypeError( - "Named arguments cannnot be used for functions that take arbitrary parameters." - )); - auto const& parameterNames = functionType->parameterNames(); - if (parameterNames.size() != m_names.size()) - BOOST_THROW_EXCEPTION(createTypeError("Some argument names are missing.")); - - // check duplicate names - for (size_t i = 0; i < m_names.size(); i++) - for (size_t j = i + 1; j < m_names.size(); j++) - if (*m_names[i] == *m_names[j]) - BOOST_THROW_EXCEPTION(m_arguments[i]->createTypeError("Duplicate named argument.")); - - for (size_t i = 0; i < m_names.size(); i++) { - bool found = false; - for (size_t j = 0; j < parameterNames.size(); j++) { - if (parameterNames[j] == *m_names[i]) { - // check type convertible - if (!m_arguments[i]->type()->isImplicitlyConvertibleTo(*parameterTypes[j])) - BOOST_THROW_EXCEPTION(m_arguments[i]->createTypeError( - "Invalid type for argument in function call. " - "Invalid implicit conversion from " + - m_arguments[i]->type()->toString() + - " to " + - parameterTypes[i]->toString() + - " requested." - )); - - found = true; - break; - } - } - if (!found) - BOOST_THROW_EXCEPTION(createTypeError("Named argument does not match function declaration.")); - } - } - - // @todo actually the return type should be an anonymous struct, - // but we change it to the type of the first return value until we have anonymous - // structs and tuples - if (functionType->returnParameterTypes().empty()) - m_type = make_shared<VoidType>(); - else - m_type = functionType->returnParameterTypes().front(); -} - -bool FunctionCall::isTypeConversion() const -{ - return m_expression->type()->category() == Type::Category::TypeType && !isStructConstructorCall(); -} - -bool FunctionCall::isStructConstructorCall() const -{ - if (auto const* type = dynamic_cast<TypeType const*>(m_expression->type().get())) - return type->actualType()->category() == Type::Category::Struct; - else - return false; + auto const* callable = dynamic_cast<CallableDeclaration const*>(scope()); + return (!!callable && !isCallableParameter()); } -void NewExpression::checkTypeRequirements(TypePointers const*) +TypePointer VariableDeclaration::type(ContractDefinition const*) const { - m_contractName->checkTypeRequirements(nullptr); - m_contract = dynamic_cast<ContractDefinition const*>(&m_contractName->referencedDeclaration()); - - if (!m_contract) - BOOST_THROW_EXCEPTION(createTypeError("Identifier is not a contract.")); - if (!m_contract->isFullyImplemented()) - BOOST_THROW_EXCEPTION(createTypeError("Trying to create an instance of an abstract contract.")); - - auto scopeContract = m_contractName->contractScope(); - auto bases = m_contract->linearizedBaseContracts(); - if (find(bases.begin(), bases.end(), scopeContract) != bases.end()) - BOOST_THROW_EXCEPTION(createTypeError("Circular reference for contract creation: cannot create instance of derived or same contract.")); - - shared_ptr<ContractType const> contractType = make_shared<ContractType>(*m_contract); - TypePointers const& parameterTypes = contractType->constructorType()->parameterTypes(); - m_type = make_shared<FunctionType>( - parameterTypes, - TypePointers{contractType}, - strings(), - strings(), - FunctionType::Location::Creation - ); + return annotation().type; } -void MemberAccess::checkTypeRequirements(TypePointers const* _argumentTypes) +VariableDeclarationAnnotation& VariableDeclaration::annotation() const { - m_expression->checkTypeRequirements(nullptr); - Type const& type = *m_expression->type(); - - MemberList::MemberMap possibleMembers = type.members().membersByName(*m_memberName); - if (possibleMembers.size() > 1 && _argumentTypes) - { - // do override resolution - for (auto it = possibleMembers.begin(); it != possibleMembers.end();) - if ( - it->type->category() == Type::Category::Function && - !dynamic_cast<FunctionType const&>(*it->type).canTakeArguments(*_argumentTypes) - ) - it = possibleMembers.erase(it); - else - ++it; - } - if (possibleMembers.size() == 0) - { - auto storageType = ReferenceType::copyForLocationIfReference( - DataLocation::Storage, - m_expression->type() - ); - if (!storageType->members().membersByName(*m_memberName).empty()) - BOOST_THROW_EXCEPTION(createTypeError( - "Member \"" + - *m_memberName + - "\" is not available in " + - type.toString() + - " outside of storage." - )); - BOOST_THROW_EXCEPTION(createTypeError( - "Member \"" + - *m_memberName + - "\" not found or not visible after argument-dependent lookup in " + - type.toString() - )); - } - else if (possibleMembers.size() > 1) - BOOST_THROW_EXCEPTION(createTypeError( - "Member \"" + - *m_memberName + - "\" not unique after argument-dependent lookup in " + - type.toString() - )); - - m_referencedDeclaration = possibleMembers.front().declaration; - m_type = possibleMembers.front().type; - if (type.category() == Type::Category::Struct) - m_isLValue = true; - else if (type.category() == Type::Category::Array) - { - auto const& arrayType(dynamic_cast<ArrayType const&>(type)); - m_isLValue = ( - *m_memberName == "length" && - arrayType.location() == DataLocation::Storage && - arrayType.isDynamicallySized() - ); - } - else - m_isLValue = false; + if (!m_annotation) + m_annotation = new VariableDeclarationAnnotation(); + return static_cast<VariableDeclarationAnnotation&>(*m_annotation); } -void IndexAccess::checkTypeRequirements(TypePointers const*) +ReturnAnnotation& Return::annotation() const { - m_base->checkTypeRequirements(nullptr); - switch (m_base->type()->category()) - { - case Type::Category::Array: - { - ArrayType const& type = dynamic_cast<ArrayType const&>(*m_base->type()); - if (!m_index) - BOOST_THROW_EXCEPTION(createTypeError("Index expression cannot be omitted.")); - if (type.isString()) - BOOST_THROW_EXCEPTION(createTypeError("Index access for string is not possible.")); - m_index->expectType(IntegerType(256)); - - m_type = type.baseType(); - if (auto integerType = dynamic_cast<IntegerConstantType const*>(m_index->type().get())) - if (!type.isDynamicallySized() && type.length() <= integerType->literalValue(nullptr)) - BOOST_THROW_EXCEPTION(createTypeError("Out of bounds access.")); - - m_isLValue = type.location() != DataLocation::CallData; - break; - } - case Type::Category::Mapping: - { - MappingType const& type = dynamic_cast<MappingType const&>(*m_base->type()); - if (!m_index) - BOOST_THROW_EXCEPTION(createTypeError("Index expression cannot be omitted.")); - m_index->expectType(*type.keyType()); - m_type = type.valueType(); - m_isLValue = true; - break; - } - case Type::Category::TypeType: - { - TypeType const& type = dynamic_cast<TypeType const&>(*m_base->type()); - if (!m_index) - m_type = make_shared<TypeType>(make_shared<ArrayType>(DataLocation::Memory, type.actualType())); - else - { - m_index->checkTypeRequirements(nullptr); - auto length = dynamic_cast<IntegerConstantType const*>(m_index->type().get()); - if (!length) - BOOST_THROW_EXCEPTION(m_index->createTypeError("Integer constant expected.")); - m_type = make_shared<TypeType>(make_shared<ArrayType>( - DataLocation::Memory, - type.actualType(), - length->literalValue(nullptr) - )); - } - break; - } - default: - BOOST_THROW_EXCEPTION(m_base->createTypeError( - "Indexed expression has to be a type, mapping or array (is " + - m_base->type()->toString() + - ")" - )); - } + if (!m_annotation) + m_annotation = new ReturnAnnotation(); + return static_cast<ReturnAnnotation&>(*m_annotation); } -void Identifier::checkTypeRequirements(TypePointers const* _argumentTypes) +ExpressionAnnotation& Expression::annotation() const { - if (!m_referencedDeclaration) - { - if (!_argumentTypes) - BOOST_THROW_EXCEPTION(createTypeError("Unable to determine overloaded type.")); - overloadResolution(*_argumentTypes); - } - solAssert(!!m_referencedDeclaration, "Referenced declaration is null after overload resolution."); - m_isLValue = m_referencedDeclaration->isLValue(); - m_type = m_referencedDeclaration->type(m_contractScope); - if (!m_type) - BOOST_THROW_EXCEPTION(createTypeError("Declaration referenced before type could be determined.")); + if (!m_annotation) + m_annotation = new ExpressionAnnotation(); + return static_cast<ExpressionAnnotation&>(*m_annotation); } -Declaration const& Identifier::referencedDeclaration() const +MemberAccessAnnotation& MemberAccess::annotation() const { - solAssert(!!m_referencedDeclaration, "Identifier not resolved."); - return *m_referencedDeclaration; + if (!m_annotation) + m_annotation = new MemberAccessAnnotation(); + return static_cast<MemberAccessAnnotation&>(*m_annotation); } -void Identifier::overloadResolution(TypePointers const& _argumentTypes) +BinaryOperationAnnotation& BinaryOperation::annotation() const { - solAssert(!m_referencedDeclaration, "Referenced declaration should be null before overload resolution."); - solAssert(!m_overloadedDeclarations.empty(), "No candidates for overload resolution found."); - - vector<Declaration const*> possibles; - if (m_overloadedDeclarations.size() == 1) - m_referencedDeclaration = *m_overloadedDeclarations.begin(); - - for (Declaration const* declaration: m_overloadedDeclarations) - { - TypePointer const& function = declaration->type(); - auto const* functionType = dynamic_cast<FunctionType const*>(function.get()); - if (functionType && functionType->canTakeArguments(_argumentTypes)) - possibles.push_back(declaration); - } - if (possibles.size() == 1) - m_referencedDeclaration = possibles.front(); - else if (possibles.empty()) - BOOST_THROW_EXCEPTION(createTypeError("No matching declaration found after argument-dependent lookup.")); - else - BOOST_THROW_EXCEPTION(createTypeError("No unique declaration found after argument-dependent lookup.")); + if (!m_annotation) + m_annotation = new BinaryOperationAnnotation(); + return static_cast<BinaryOperationAnnotation&>(*m_annotation); } -void ElementaryTypeNameExpression::checkTypeRequirements(TypePointers const*) +FunctionCallAnnotation& FunctionCall::annotation() const { - m_type = make_shared<TypeType>(Type::fromElementaryTypeName(m_typeToken)); + if (!m_annotation) + m_annotation = new FunctionCallAnnotation(); + return static_cast<FunctionCallAnnotation&>(*m_annotation); } -void Literal::checkTypeRequirements(TypePointers const*) +IdentifierAnnotation& Identifier::annotation() const { - m_type = Type::forLiteral(*this); - if (!m_type) - BOOST_THROW_EXCEPTION(createTypeError("Invalid literal value.")); -} - -} + if (!m_annotation) + m_annotation = new IdentifierAnnotation(); + return static_cast<IdentifierAnnotation&>(*m_annotation); } diff --git a/libsolidity/AST.h b/libsolidity/AST.h index ba529a8a..134ca148 100644 --- a/libsolidity/AST.h +++ b/libsolidity/AST.h @@ -33,6 +33,7 @@ #include <libsolidity/Token.h> #include <libsolidity/Types.h> #include <libsolidity/Exceptions.h> +#include <libsolidity/ASTAnnotations.h> namespace dev { @@ -51,9 +52,8 @@ class ASTConstVisitor; class ASTNode: private boost::noncopyable { public: - explicit ASTNode(SourceLocation const& _location): m_location(_location) {} - - virtual ~ASTNode() {} + explicit ASTNode(SourceLocation const& _location); + virtual ~ASTNode(); virtual void accept(ASTVisitor& _visitor) = 0; virtual void accept(ASTConstVisitor& _visitor) const = 0; @@ -77,6 +77,9 @@ public: /// the given description TypeError createTypeError(std::string const& _description) const; + ///@todo make this const-safe by providing a different way to access the annotation + virtual ASTAnnotation& annotation() const; + ///@{ ///@name equality operators /// Equality relies on the fact that nodes cannot be copied. @@ -84,6 +87,10 @@ public: bool operator!=(ASTNode const& _other) const { return !operator==(_other); } ///@} +protected: + /// Annotation - is specialised in derived classes, is created upon request (because of polymorphism). + mutable ASTAnnotation* m_annotation = nullptr; + private: SourceLocation m_location; }; @@ -151,12 +158,14 @@ public: Declaration const* scope() const { return m_scope; } void setScope(Declaration const* _scope) { m_scope = _scope; } + virtual bool isLValue() const { return false; } + virtual bool isPartOfExternalInterface() const { return false; } + /// @returns the type of expressions referencing this declaration. /// The current contract has to be given since this context can change the type, especially of /// contract types. + /// This can only be called once types of variable declarations have already been resolved. virtual TypePointer type(ContractDefinition const* m_currentContract = nullptr) const = 0; - virtual bool isLValue() const { return false; } - virtual bool isPartOfExternalInterface() const { return false; } protected: virtual Visibility defaultVisibility() const { return Visibility::Public; } @@ -205,8 +214,7 @@ public: explicit ImplementationOptional(bool _implemented): m_implemented(_implemented) {} /// @return whether this node is fully implemented or not - bool isFullyImplemented() const { return m_implemented; } - void setFullyImplemented(bool _implemented) { m_implemented = _implemented; } + bool isImplemented() const { return m_implemented; } protected: bool m_implemented; @@ -219,7 +227,7 @@ protected: * document order. It first visits all struct declarations, then all variable declarations and * finally all function declarations. */ -class ContractDefinition: public Declaration, public Documented, public ImplementationOptional +class ContractDefinition: public Declaration, public Documented { public: ContractDefinition( @@ -237,7 +245,6 @@ public: ): Declaration(_location, _name), Documented(_documentation), - ImplementationOptional(true), m_baseContracts(_baseContracts), m_definedStructs(_definedStructs), m_definedEnums(_definedEnums), @@ -261,24 +268,14 @@ public: std::vector<ASTPointer<EventDefinition>> const& interfaceEvents() const; bool isLibrary() const { return m_isLibrary; } - virtual TypePointer type(ContractDefinition const* m_currentContract) const override; - - /// Checks that there are no illegal overrides, that the constructor does not have a "returns" - /// and calls checkTypeRequirements on all its functions. - void checkTypeRequirements(); - /// @returns a map of canonical function signatures to FunctionDefinitions /// as intended for use by the ABI. std::map<FixedHash<4>, FunctionTypePointer> interfaceFunctions() const; + std::vector<std::pair<FixedHash<4>, FunctionTypePointer>> const& interfaceFunctionList() const; /// @returns a list of the inheritable members of this contract std::vector<Declaration const*> const& inheritableMembers() const; - /// List of all (direct and indirect) base contracts in order from derived to base, including - /// the contract itself. Available after name resolution - std::vector<ContractDefinition const*> const& linearizedBaseContracts() const { return m_linearizedBaseContracts; } - void setLinearizedBaseContracts(std::vector<ContractDefinition const*> const& _bases) { m_linearizedBaseContracts = _bases; } - /// Returns the constructor or nullptr if no constructor was specified. FunctionDefinition const* constructor() const; /// Returns the fallback function or nullptr if no fallback function was specified. @@ -290,21 +287,11 @@ public: std::string const& devDocumentation() const; void setDevDocumentation(std::string const& _devDocumentation); -private: - /// Checks that two functions defined in this contract with the same name have different - /// arguments and that there is at most one constructor. - void checkDuplicateFunctions() const; - void checkIllegalOverrides() const; - void checkAbstractFunctions(); - void checkAbstractConstructors(); - /// Checks that different functions with external visibility end up having different - /// external argument types (i.e. different signature). - void checkExternalTypeClashes() const; - /// Checks that all requirements for a library are fulfilled if this is a library. - void checkLibraryRequirements() const; + virtual TypePointer type(ContractDefinition const* m_currentContract) const override; - std::vector<std::pair<FixedHash<4>, FunctionTypePointer>> const& interfaceFunctionList() const; + virtual ContractDefinitionAnnotation& annotation() const override; +private: std::vector<ASTPointer<InheritanceSpecifier>> m_baseContracts; std::vector<ASTPointer<StructDefinition>> m_definedStructs; std::vector<ASTPointer<EnumDefinition>> m_definedEnums; @@ -327,18 +314,19 @@ private: class InheritanceSpecifier: public ASTNode { public: - InheritanceSpecifier(SourceLocation const& _location, ASTPointer<Identifier> const& _baseName, - std::vector<ASTPointer<Expression>> _arguments): + InheritanceSpecifier( + SourceLocation const& _location, + ASTPointer<Identifier> const& _baseName, + std::vector<ASTPointer<Expression>> _arguments + ): ASTNode(_location), m_baseName(_baseName), m_arguments(_arguments) {} virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTConstVisitor& _visitor) const override; - ASTPointer<Identifier> const& name() const { return m_baseName; } + Identifier const& name() const { return *m_baseName; } std::vector<ASTPointer<Expression>> const& arguments() const { return m_arguments; } - void checkTypeRequirements(); - private: ASTPointer<Identifier> m_baseName; std::vector<ASTPointer<Expression>> m_arguments; @@ -359,32 +347,27 @@ public: std::vector<ASTPointer<VariableDeclaration>> const& members() const { return m_members; } - virtual TypePointer type(ContractDefinition const*) const override; - - /// Checks that the members do not include any recursive structs and have valid types - /// (e.g. no functions). - void checkValidityOfMembers() const; + virtual TypePointer type(ContractDefinition const* m_currentContract) const override; private: - void checkMemberTypes() const; - void checkRecursion() const; - std::vector<ASTPointer<VariableDeclaration>> m_members; }; class EnumDefinition: public Declaration { public: - EnumDefinition(SourceLocation const& _location, - ASTPointer<ASTString> const& _name, - std::vector<ASTPointer<EnumValue>> const& _members): + EnumDefinition( + SourceLocation const& _location, + ASTPointer<ASTString> const& _name, + std::vector<ASTPointer<EnumValue>> const& _members + ): Declaration(_location, _name), m_members(_members) {} virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTConstVisitor& _visitor) const override; std::vector<ASTPointer<EnumValue>> const& members() const { return m_members; } - virtual TypePointer type(ContractDefinition const*) const override; + virtual TypePointer type(ContractDefinition const* m_currentContract) const override; private: std::vector<ASTPointer<EnumValue>> m_members; @@ -395,14 +378,14 @@ private: */ class EnumValue: public Declaration { - public: - EnumValue(SourceLocation const& _location, - ASTPointer<ASTString> const& _name): +public: + EnumValue(SourceLocation const& _location, ASTPointer<ASTString> const& _name): Declaration(_location, _name) {} virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTConstVisitor& _visitor) const override; - virtual TypePointer type(ContractDefinition const* = nullptr) const override; + + virtual TypePointer type(ContractDefinition const* m_currentContract) const override; }; /** @@ -491,17 +474,15 @@ public: { return Declaration::isVisibleInContract() && !isConstructor() && !name().empty(); } - virtual TypePointer type(ContractDefinition const*) const override; virtual bool isPartOfExternalInterface() const override { return isPublic() && !m_isConstructor && !name().empty(); } - /// Checks that all parameters have allowed types and calls checkTypeRequirements on the body. - void checkTypeRequirements(); - /// @returns the external signature of the function /// That consists of the name of the function followed by the types of the /// arguments separated by commas all enclosed in parentheses without any spaces. std::string externalSignature() const; + virtual TypePointer type(ContractDefinition const* m_currentContract) const override; + private: bool m_isConstructor; bool m_isDeclaredConst; @@ -540,28 +521,29 @@ public: virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTConstVisitor& _visitor) const override; - TypeName* typeName() { return m_typeName.get(); } + TypeName* typeName() const { return m_typeName.get(); } ASTPointer<Expression> const& value() const { return m_value; } - /// Returns the declared or inferred type. Can be an empty pointer if no type was explicitly - /// declared and there is no assignment to the variable that fixes the type. - TypePointer type(ContractDefinition const* = nullptr) const override { return m_type; } - void setType(std::shared_ptr<Type const> const& _type) { m_type = _type; } - virtual bool isLValue() const override; virtual bool isPartOfExternalInterface() const override { return isPublic(); } - void checkTypeRequirements(); bool isLocalVariable() const { return !!dynamic_cast<FunctionDefinition const*>(scope()); } /// @returns true if this variable is a parameter or return parameter of a function. bool isCallableParameter() const; /// @returns true if this variable is a parameter (not return parameter) of an external function. bool isExternalCallableParameter() const; + /// @returns true if the type of the variable does not need to be specified, i.e. it is declared + /// in the body of a function or modifier. + bool canHaveAutoType() const; bool isStateVariable() const { return m_isStateVariable; } bool isIndexed() const { return m_isIndexed; } bool isConstant() const { return m_isConstant; } Location referenceLocation() const { return m_location; } + virtual TypePointer type(ContractDefinition const* m_currentContract) const override; + + virtual VariableDeclarationAnnotation& annotation() const override; + protected: Visibility defaultVisibility() const override { return Visibility::Internal; } @@ -572,8 +554,6 @@ private: bool m_isIndexed; ///< Whether this is an indexed variable (used by events). bool m_isConstant; ///< Whether the variable is a compile-time constant. Location m_location; ///< Location of the variable if it is of reference type. - - std::shared_ptr<Type const> m_type; ///< derived type, initially empty }; /** @@ -600,9 +580,7 @@ public: Block const& body() const { return *m_body; } - virtual TypePointer type(ContractDefinition const* = nullptr) const override; - - void checkTypeRequirements(); + virtual TypePointer type(ContractDefinition const* m_currentContract) const override; private: ASTPointer<Block> m_body; @@ -614,8 +592,11 @@ private: class ModifierInvocation: public ASTNode { public: - ModifierInvocation(SourceLocation const& _location, ASTPointer<Identifier> const& _name, - std::vector<ASTPointer<Expression>> _arguments): + ModifierInvocation( + SourceLocation const& _location, + ASTPointer<Identifier> const& _name, + std::vector<ASTPointer<Expression>> _arguments + ): ASTNode(_location), m_modifierName(_name), m_arguments(_arguments) {} virtual void accept(ASTVisitor& _visitor) override; @@ -624,9 +605,6 @@ public: ASTPointer<Identifier> const& name() const { return m_modifierName; } std::vector<ASTPointer<Expression>> const& arguments() const { return m_arguments; } - /// @param _bases is the list of base contracts for base constructor calls. For modifiers an empty vector should be passed. - void checkTypeRequirements(std::vector<ContractDefinition const*> const& _bases); - private: ASTPointer<Identifier> m_modifierName; std::vector<ASTPointer<Expression>> m_arguments; @@ -656,12 +634,7 @@ public: bool isAnonymous() const { return m_anonymous; } - virtual TypePointer type(ContractDefinition const* = nullptr) const override - { - return std::make_shared<FunctionType>(*this); - } - - void checkTypeRequirements(); + virtual TypePointer type(ContractDefinition const* m_currentContract) const override; private: bool m_anonymous = false; @@ -681,7 +654,7 @@ public: virtual void accept(ASTConstVisitor&) const override { BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("MagicVariableDeclaration used inside real AST.")); } - virtual TypePointer type(ContractDefinition const* = nullptr) const override { return m_type; } + virtual TypePointer type(ContractDefinition const*) const override { return m_type; } private: std::shared_ptr<Type const> m_type; @@ -700,10 +673,7 @@ public: virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTConstVisitor& _visitor) const override; - /// Retrieve the element of the type hierarchy this node refers to. Can return an empty shared - /// pointer until the types have been resolved using the @ref NameAndTypeResolver. - /// If it returns an empty shared pointer after that, this indicates that the type was not found. - virtual std::shared_ptr<Type const> toType() = 0; + virtual TypeNameAnnotation& annotation() const override; }; /** @@ -720,7 +690,6 @@ public: } virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTConstVisitor& _visitor) const override; - virtual std::shared_ptr<Type const> toType() override { return Type::fromElementaryTypeName(m_type); } Token::Value typeName() const { return m_type; } @@ -735,19 +704,16 @@ class UserDefinedTypeName: public TypeName { public: UserDefinedTypeName(SourceLocation const& _location, ASTPointer<ASTString> const& _name): - TypeName(_location), m_name(_name), m_referencedDeclaration(nullptr) {} + TypeName(_location), m_name(_name) {} virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTConstVisitor& _visitor) const override; - virtual std::shared_ptr<Type const> toType() override { return Type::fromUserDefinedTypeName(*this); } ASTString const& name() const { return *m_name; } - void setReferencedDeclaration(Declaration const& _referencedDeclaration) { m_referencedDeclaration = &_referencedDeclaration; } - Declaration const* referencedDeclaration() const { return m_referencedDeclaration; } + + virtual UserDefinedTypeNameAnnotation& annotation() const override; private: ASTPointer<ASTString> m_name; - - Declaration const* m_referencedDeclaration; }; /** @@ -761,7 +727,6 @@ public: TypeName(_location), m_keyType(_keyType), m_valueType(_valueType) {} virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTConstVisitor& _visitor) const override; - virtual TypePointer toType() override { return Type::fromMapping(*m_keyType, *m_valueType); } ElementaryTypeName const& keyType() const { return *m_keyType; } TypeName const& valueType() const { return *m_valueType; } @@ -782,7 +747,6 @@ public: TypeName(_location), m_baseType(_baseType), m_length(_length) {} virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTConstVisitor& _visitor) const override; - virtual std::shared_ptr<Type const> toType() override { return Type::fromArrayTypeName(*m_baseType, m_length.get()); } TypeName const& baseType() const { return *m_baseType; } Expression const* length() const { return m_length.get(); } @@ -805,11 +769,6 @@ class Statement: public ASTNode { public: explicit Statement(SourceLocation const& _location): ASTNode(_location) {} - - /// Check all type requirements, throws exception if some requirement is not met. - /// This includes checking that operators are applicable to their arguments but also that - /// the number of function call arguments matches the number of formal parameters and so forth. - virtual void checkTypeRequirements() = 0; }; /** @@ -823,8 +782,6 @@ public: virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTConstVisitor& _visitor) const override; - virtual void checkTypeRequirements() override; - private: std::vector<ASTPointer<Statement>> m_statements; }; @@ -840,8 +797,6 @@ public: virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTConstVisitor& _visitor) const override; - - virtual void checkTypeRequirements() override { } }; /** @@ -857,7 +812,6 @@ public: m_condition(_condition), m_trueBody(_trueBody), m_falseBody(_falseBody) {} virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTConstVisitor& _visitor) const override; - virtual void checkTypeRequirements() override; Expression const& condition() const { return *m_condition; } Statement const& trueStatement() const { return *m_trueBody; } @@ -887,7 +841,6 @@ public: BreakableStatement(_location), m_condition(_condition), m_body(_body) {} virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTConstVisitor& _visitor) const override; - virtual void checkTypeRequirements() override; Expression const& condition() const { return *m_condition; } Statement const& body() const { return *m_body; } @@ -903,19 +856,21 @@ private: class ForStatement: public BreakableStatement { public: - ForStatement(SourceLocation const& _location, - ASTPointer<Statement> const& _initExpression, - ASTPointer<Expression> const& _conditionExpression, - ASTPointer<ExpressionStatement> const& _loopExpression, - ASTPointer<Statement> const& _body): + ForStatement( + SourceLocation const& _location, + ASTPointer<Statement> const& _initExpression, + ASTPointer<Expression> const& _conditionExpression, + ASTPointer<ExpressionStatement> const& _loopExpression, + ASTPointer<Statement> const& _body + ): BreakableStatement(_location), m_initExpression(_initExpression), m_condExpression(_conditionExpression), m_loopExpression(_loopExpression), - m_body(_body) {} + m_body(_body) + {} virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTConstVisitor& _visitor) const override; - virtual void checkTypeRequirements() override; Statement const* initializationExpression() const { return m_initExpression.get(); } Expression const* condition() const { return m_condExpression.get(); } @@ -939,7 +894,6 @@ public: Continue(SourceLocation const& _location): Statement(_location) {} virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTConstVisitor& _visitor) const override; - virtual void checkTypeRequirements() override {} }; class Break: public Statement @@ -948,27 +902,22 @@ public: Break(SourceLocation const& _location): Statement(_location) {} virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTConstVisitor& _visitor) const override; - virtual void checkTypeRequirements() override {} }; class Return: public Statement { public: Return(SourceLocation const& _location, ASTPointer<Expression> _expression): - Statement(_location), m_expression(_expression), m_returnParameters(nullptr) {} + Statement(_location), m_expression(_expression) {} virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTConstVisitor& _visitor) const override; - virtual void checkTypeRequirements() override; - void setFunctionReturnParameters(ParameterList const* _parameters) { m_returnParameters = _parameters; } - ParameterList const* functionReturnParameters() const { return m_returnParameters; } Expression const* expression() const { return m_expression.get(); } + virtual ReturnAnnotation& annotation() const override; + private: ASTPointer<Expression> m_expression; ///< value to return, optional - - /// Pointer to the parameter list of the function, filled by the @ref NameAndTypeResolver. - ParameterList const* m_returnParameters; }; /** @@ -980,7 +929,6 @@ public: Throw(SourceLocation const& _location): Statement(_location) {} virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTConstVisitor& _visitor) const override; - virtual void checkTypeRequirements() override {}; }; /** @@ -995,7 +943,6 @@ public: Statement(_location), m_variable(_variable) {} virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTConstVisitor& _visitor) const override; - virtual void checkTypeRequirements() override; VariableDeclaration const& declaration() const { return *m_variable; } Expression const* expression() const { return m_variable->value().get(); } @@ -1014,7 +961,6 @@ public: Statement(_location), m_expression(_expression) {} virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTConstVisitor& _visitor) const override; - virtual void checkTypeRequirements() override; Expression const& expression() const { return *m_expression; } @@ -1036,32 +982,8 @@ class Expression: public ASTNode { public: Expression(SourceLocation const& _location): ASTNode(_location) {} - /// Performs type checking after which m_type should be set. - /// @arg _argumentTypes if set, provides the argument types for the case that this expression - /// is used in the context of a call, used for function overload resolution. - virtual void checkTypeRequirements(TypePointers const* _argumentTypes) = 0; - - std::shared_ptr<Type const> const& type() const { return m_type; } - bool isLValue() const { return m_isLValue; } - - /// Helper function, infer the type via @ref checkTypeRequirements and then check that it - /// is implicitly convertible to @a _expectedType. If not, throw exception. - void expectType(Type const& _expectedType); - /// Checks that this expression is an lvalue and also registers that an address and - /// not a value is generated during compilation. Can be called after checkTypeRequirements() - /// by an enclosing expression. - void requireLValue(); - /// Returns true if @a requireLValue was previously called on this expression. - bool lvalueRequested() const { return m_lvalueRequested; } -protected: - //! Inferred type of the expression, only filled after a call to checkTypeRequirements(). - std::shared_ptr<Type const> m_type; - //! If this expression is an lvalue (i.e. something that can be assigned to). - //! This is set during calls to @a checkTypeRequirements() - bool m_isLValue = false; - //! Whether the outer expression requested the address (true) or the value (false) of this expression. - bool m_lvalueRequested = false; + ExpressionAnnotation& annotation() const override; }; /// Assignment, can also be a compound assignment. @@ -1069,16 +991,21 @@ protected: class Assignment: public Expression { public: - Assignment(SourceLocation const& _location, ASTPointer<Expression> const& _leftHandSide, - Token::Value _assignmentOperator, ASTPointer<Expression> const& _rightHandSide): - Expression(_location), m_leftHandSide(_leftHandSide), - m_assigmentOperator(_assignmentOperator), m_rightHandSide(_rightHandSide) + Assignment( + SourceLocation const& _location, + ASTPointer<Expression> const& _leftHandSide, + Token::Value _assignmentOperator, + ASTPointer<Expression> const& _rightHandSide + ): + Expression(_location), + m_leftHandSide(_leftHandSide), + m_assigmentOperator(_assignmentOperator), + m_rightHandSide(_rightHandSide) { solAssert(Token::isAssignmentOp(_assignmentOperator), ""); } virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTConstVisitor& _visitor) const override; - virtual void checkTypeRequirements(TypePointers const* _argumentTypes) override; Expression const& leftHandSide() const { return *m_leftHandSide; } Token::Value assignmentOperator() const { return m_assigmentOperator; } @@ -1106,7 +1033,6 @@ public: } virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTConstVisitor& _visitor) const override; - virtual void checkTypeRequirements(TypePointers const* _argumentTypes) override; Token::Value getOperator() const { return m_operator; } bool isPrefixOperation() const { return m_isPrefix; } @@ -1133,21 +1059,17 @@ public: } virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTConstVisitor& _visitor) const override; - virtual void checkTypeRequirements(TypePointers const* _argumentTypes) override; Expression const& leftExpression() const { return *m_left; } Expression const& rightExpression() const { return *m_right; } Token::Value getOperator() const { return m_operator; } - Type const& commonType() const { return *m_commonType; } + + BinaryOperationAnnotation& annotation() const override; private: ASTPointer<Expression> m_left; Token::Value m_operator; ASTPointer<Expression> m_right; - - /// The common type that is used for the operation, not necessarily the result type (e.g. for - /// comparisons, this is always bool). - std::shared_ptr<Type const> m_commonType; }; /** @@ -1161,17 +1083,12 @@ public: Expression(_location), m_expression(_expression), m_arguments(_arguments), m_names(_names) {} virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTConstVisitor& _visitor) const override; - virtual void checkTypeRequirements(TypePointers const* _argumentTypes) override; Expression const& expression() const { return *m_expression; } std::vector<ASTPointer<Expression const>> arguments() const { return {m_arguments.begin(), m_arguments.end()}; } std::vector<ASTPointer<ASTString>> const& names() const { return m_names; } - /// @returns true if this is not an actual function call, but an explicit type conversion. - /// Returns false for struct constructor calls. - bool isTypeConversion() const; - /// @return true if this is a constructor call for a struct, i.e. StructName(...). - bool isStructConstructorCall() const; + virtual FunctionCallAnnotation& annotation() const override; private: ASTPointer<Expression> m_expression; @@ -1189,15 +1106,11 @@ public: Expression(_location), m_contractName(_contractName) {} virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTConstVisitor& _visitor) const override; - virtual void checkTypeRequirements(TypePointers const* _argumentTypes) override; - /// Returns the referenced contract. Can only be called after type checking. - ContractDefinition const* contract() const { solAssert(m_contract, ""); return m_contract; } + Identifier const& contractName() const { return *m_contractName; } private: ASTPointer<Identifier> m_contractName; - - ContractDefinition const* m_contract = nullptr; }; /** @@ -1213,18 +1126,12 @@ public: virtual void accept(ASTConstVisitor& _visitor) const override; Expression const& expression() const { return *m_expression; } ASTString const& memberName() const { return *m_memberName; } - /// @returns the declaration referenced by this expression. Might return nullptr even if the - /// expression is valid, e.g. if the member does not correspond to an AST node. - Declaration const* referencedDeclaration() const { return m_referencedDeclaration; } - virtual void checkTypeRequirements(TypePointers const* _argumentTypes) override; + + virtual MemberAccessAnnotation& annotation() const override; private: ASTPointer<Expression> m_expression; ASTPointer<ASTString> m_memberName; - - /// Pointer to the referenced declaration, this is sometimes needed to resolve function over - /// loads in the type-checking phase. - Declaration const* m_referencedDeclaration = nullptr; }; /** @@ -1238,7 +1145,6 @@ public: Expression(_location), m_base(_base), m_index(_index) {} virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTConstVisitor& _visitor) const override; - virtual void checkTypeRequirements(TypePointers const* _argumentTypes) override; Expression const& baseExpression() const { return *m_base; } Expression const* indexExpression() const { return m_index.get(); } @@ -1268,44 +1174,13 @@ public: PrimaryExpression(_location), m_name(_name) {} virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTConstVisitor& _visitor) const override; - virtual void checkTypeRequirements(TypePointers const* _argumentTypes) override; ASTString const& name() const { return *m_name; } - void setReferencedDeclaration( - Declaration const& _referencedDeclaration, - ContractDefinition const* _currentContract = nullptr - ) - { - m_referencedDeclaration = &_referencedDeclaration; - m_contractScope = _currentContract; - } - Declaration const& referencedDeclaration() const; - - /// Stores a set of possible declarations referenced by this identifier. Has to be resolved - /// providing argument types using overloadResolution before the referenced declaration - /// is accessed. - void setOverloadedDeclarations(std::vector<Declaration const*> const& _declarations) - { - m_overloadedDeclarations = _declarations; - } - - /// Tries to find exactly one of the possible referenced declarations provided the given - /// argument types in a call context. - void overloadResolution(TypePointers const& _argumentTypes); - - ContractDefinition const* contractScope() const { return m_contractScope; } + virtual IdentifierAnnotation& annotation() const override; private: ASTPointer<ASTString> m_name; - - /// Declaration the name refers to. - Declaration const* m_referencedDeclaration = nullptr; - /// Stores a reference to the current contract. This is needed because types of base contracts - /// change depending on the context. - ContractDefinition const* m_contractScope = nullptr; - /// A vector of overloaded declarations, right now only FunctionDefinition has overloaded declarations. - std::vector<Declaration const*> m_overloadedDeclarations; }; /** @@ -1323,7 +1198,6 @@ public: } virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTConstVisitor& _visitor) const override; - virtual void checkTypeRequirements(TypePointers const* _argumentTypes) override; Token::Value typeToken() const { return m_typeToken; } @@ -1357,7 +1231,6 @@ public: PrimaryExpression(_location), m_token(_token), m_value(_value), m_subDenomination(_sub) {} virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTConstVisitor& _visitor) const override; - virtual void checkTypeRequirements(TypePointers const* _argumentTypes) override; Token::Value token() const { return m_token; } /// @returns the non-parsed value of the literal diff --git a/libsolidity/ASTAnnotations.cpp b/libsolidity/ASTAnnotations.cpp new file mode 100644 index 00000000..4253e16d --- /dev/null +++ b/libsolidity/ASTAnnotations.cpp @@ -0,0 +1,28 @@ +/* + This file is part of cpp-ethereum. + + cpp-ethereum is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + cpp-ethereum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>. +*/ +/** + * @author Christian <c@ethdev.com> + * @date 2015 + * Object containing the type and other annotations for the AST nodes. + */ + +#include <libsolidity/ASTAnnotations.h> + +using namespace std; +using namespace dev; +using namespace dev::solidity; + diff --git a/libsolidity/ASTAnnotations.h b/libsolidity/ASTAnnotations.h new file mode 100644 index 00000000..195f11c8 --- /dev/null +++ b/libsolidity/ASTAnnotations.h @@ -0,0 +1,123 @@ +/* + This file is part of cpp-ethereum. + + cpp-ethereum is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + cpp-ethereum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>. +*/ +/** + * @author Christian <c@ethdev.com> + * @date 2015 + * Object containing the type and other annotations for the AST nodes. + */ + +#pragma once + +#include <map> +#include <memory> +#include <vector> +#include <libsolidity/ASTForward.h> + +namespace dev +{ +namespace solidity +{ + +class Type; +using TypePointer = std::shared_ptr<Type const>; + +struct ASTAnnotation +{ + virtual ~ASTAnnotation() {} +}; + +struct ContractDefinitionAnnotation: ASTAnnotation +{ + /// Whether all functions are implemented. + bool isFullyImplemented = true; + /// List of all (direct and indirect) base contracts in order from derived to + /// base, including the contract itself. + std::vector<ContractDefinition const*> linearizedBaseContracts; +}; + +struct VariableDeclarationAnnotation: ASTAnnotation +{ + /// Type of variable (type of identifier referencing this variable). + TypePointer type; +}; + +struct ReturnAnnotation: ASTAnnotation +{ + /// Reference to the return parameters of the function. + ParameterList const* functionReturnParameters = nullptr; +}; + +struct TypeNameAnnotation: ASTAnnotation +{ + /// Type declared by this type name, i.e. type of a variable where this type name is used. + /// Set during reference resolution stage. + TypePointer type; +}; + +struct UserDefinedTypeNameAnnotation: TypeNameAnnotation +{ + /// Referenced declaration, set during reference resolution stage. + Declaration const* referencedDeclaration = nullptr; +}; + +struct ExpressionAnnotation: ASTAnnotation +{ + /// Inferred type of the expression. + TypePointer type; + /// Whether it is an LValue (i.e. something that can be assigned to). + bool isLValue = false; + /// Whether the expression is used in a context where the LValue is actually required. + bool lValueRequested = false; + /// Types of arguments if the expression is a function that is called - used + /// for overload resolution. + std::shared_ptr<std::vector<TypePointer>> argumentTypes; +}; + +struct IdentifierAnnotation: ExpressionAnnotation +{ + /// Stores a reference to the current contract. + /// This is needed because types of base contracts change depending on the context. + ContractDefinition const* contractScope = nullptr; + /// Referenced declaration, set at latest during overload resolution stage. + Declaration const* referencedDeclaration = nullptr; + /// List of possible declarations it could refer to. + std::vector<Declaration const*> overloadedDeclarations; +}; + +struct MemberAccessAnnotation: ExpressionAnnotation +{ + /// Referenced declaration, set at latest during overload resolution stage. + Declaration const* referencedDeclaration = nullptr; +}; + +struct BinaryOperationAnnotation: ExpressionAnnotation +{ + /// The common type that is used for the operation, not necessarily the result type (which + /// e.g. for comparisons is bool). + TypePointer commonType; +}; + +struct FunctionCallAnnotation: ExpressionAnnotation +{ + /// Whether this is an explicit type conversion. + bool isTypeConversion = false; + /// Whether this is a struct constructor call. + bool isStructConstructorCall = false; +}; + +} +} diff --git a/libsolidity/ASTJsonConverter.cpp b/libsolidity/ASTJsonConverter.cpp index 96e5cf2a..3edcb77e 100644 --- a/libsolidity/ASTJsonConverter.cpp +++ b/libsolidity/ASTJsonConverter.cpp @@ -232,19 +232,19 @@ bool ASTJsonConverter::visit(UnaryOperation const& _node) bool ASTJsonConverter::visit(BinaryOperation const& _node) { - addJsonNode("BinaryOperation", - { make_pair("operator", Token::toString(_node.getOperator())), - make_pair("type", type(_node))}, - true); + addJsonNode("BinaryOperation", { + make_pair("operator", Token::toString(_node.getOperator())), + make_pair("type", type(_node)) + }, true); return true; } bool ASTJsonConverter::visit(FunctionCall const& _node) { - addJsonNode("FunctionCall", - { make_pair("type_conversion", boost::lexical_cast<std::string>(_node.isTypeConversion())), - make_pair("type", type(_node)) }, - true); + addJsonNode("FunctionCall", { + make_pair("type_conversion", boost::lexical_cast<std::string>(_node.annotation().isTypeConversion)), + make_pair("type", type(_node)) + }, true); return true; } @@ -441,7 +441,7 @@ void ASTJsonConverter::process() string ASTJsonConverter::type(Expression const& _expression) { - return (_expression.type()) ? _expression.type()->toString() : "Unknown"; + return _expression.annotation().type ? _expression.annotation().type->toString() : "Unknown"; } } diff --git a/libsolidity/ASTJsonConverter.h b/libsolidity/ASTJsonConverter.h index 77230d92..a44df876 100644 --- a/libsolidity/ASTJsonConverter.h +++ b/libsolidity/ASTJsonConverter.h @@ -27,6 +27,7 @@ #include <libsolidity/ASTVisitor.h> #include <libsolidity/Exceptions.h> #include <libsolidity/Utils.h> +#include <libsolidity/ASTAnnotations.h> #include <json/json.h> namespace dev @@ -41,7 +42,7 @@ class ASTJsonConverter: public ASTConstVisitor { public: /// Create a converter to JSON for the given abstract syntax tree. - ASTJsonConverter(ASTNode const& _ast); + explicit ASTJsonConverter(ASTNode const& _ast); /// Output the json representation of the AST to _stream. void print(std::ostream& _stream); Json::Value const& json(); diff --git a/libsolidity/ASTPrinter.cpp b/libsolidity/ASTPrinter.cpp index 8ed45d87..5762051d 100644 --- a/libsolidity/ASTPrinter.cpp +++ b/libsolidity/ASTPrinter.cpp @@ -62,7 +62,7 @@ bool ASTPrinter::visit(ContractDefinition const& _node) bool ASTPrinter::visit(InheritanceSpecifier const& _node) { - writeLine("InheritanceSpecifier \"" + _node.name()->name() + "\""); + writeLine("InheritanceSpecifier \"" + _node.name().name() + "\""); printSourcePart(_node); return goDeeper(); } @@ -530,8 +530,8 @@ void ASTPrinter::printSourcePart(ASTNode const& _node) void ASTPrinter::printType(Expression const& _expression) { - if (_expression.type()) - *m_ostream << indentation() << " Type: " << _expression.type()->toString() << "\n"; + if (_expression.annotation().type) + *m_ostream << indentation() << " Type: " << _expression.annotation().type->toString() << "\n"; else *m_ostream << indentation() << " Type unknown.\n"; } diff --git a/libsolidity/AST_accept.h b/libsolidity/AST_accept.h index 1388466c..dfa9c425 100644 --- a/libsolidity/AST_accept.h +++ b/libsolidity/AST_accept.h @@ -147,12 +147,6 @@ void StructDefinition::accept(ASTConstVisitor& _visitor) const _visitor.endVisit(*this); } -void StructDefinition::checkValidityOfMembers() const -{ - checkMemberTypes(); - checkRecursion(); -} - void ParameterList::accept(ASTVisitor& _visitor) { if (_visitor.visit(*this)) diff --git a/libsolidity/Compiler.cpp b/libsolidity/Compiler.cpp index d6582653..cb74072d 100644 --- a/libsolidity/Compiler.cpp +++ b/libsolidity/Compiler.cpp @@ -53,7 +53,7 @@ void Compiler::compileContract( std::map<const ContractDefinition*, eth::Assembly const*> const& _contracts ) { - m_context = CompilerContext(); // clear it just in case + m_context = CompilerContext(); { CompilerContext::LocationSetter locationSetterRunTime(m_context, _contract); initializeContext(_contract, _contracts); @@ -105,9 +105,9 @@ void Compiler::initializeContext( map<ContractDefinition const*, eth::Assembly const*> const& _compiledContracts ) { - CompilerUtils(m_context).initialiseFreeMemoryPointer(); m_context.setCompiledContracts(_compiledContracts); - m_context.setInheritanceHierarchy(_contract.linearizedBaseContracts()); + m_context.setInheritanceHierarchy(_contract.annotation().linearizedBaseContracts); + CompilerUtils(m_context).initialiseFreeMemoryPointer(); registerStateVariables(_contract); m_context.resetVisitedNodes(&_contract); } @@ -115,14 +115,14 @@ void Compiler::initializeContext( void Compiler::appendInitAndConstructorCode(ContractDefinition const& _contract) { // Determine the arguments that are used for the base constructors. - std::vector<ContractDefinition const*> const& bases = _contract.linearizedBaseContracts(); + std::vector<ContractDefinition const*> const& bases = _contract.annotation().linearizedBaseContracts; for (ContractDefinition const* contract: bases) { if (FunctionDefinition const* constructor = contract->constructor()) for (auto const& modifier: constructor->modifiers()) { auto baseContract = dynamic_cast<ContractDefinition const*>( - &modifier->name()->referencedDeclaration()); + modifier->name()->annotation().referencedDeclaration); if (baseContract) if (m_baseArguments.count(baseContract->constructor()) == 0) m_baseArguments[baseContract->constructor()] = &modifier->arguments(); @@ -131,7 +131,8 @@ void Compiler::appendInitAndConstructorCode(ContractDefinition const& _contract) for (ASTPointer<InheritanceSpecifier> const& base: contract->baseContracts()) { ContractDefinition const* baseContract = dynamic_cast<ContractDefinition const*>( - &base->name()->referencedDeclaration()); + base->name().annotation().referencedDeclaration + ); solAssert(baseContract, ""); if (m_baseArguments.count(baseContract->constructor()) == 0) @@ -187,13 +188,13 @@ void Compiler::appendConstructor(FunctionDefinition const& _constructor) { unsigned argumentSize = 0; for (ASTPointer<VariableDeclaration> const& var: _constructor.parameters()) - if (var->type()->isDynamicallySized()) + if (var->annotation().type->isDynamicallySized()) { argumentSize = 0; break; } else - argumentSize += var->type()->calldataEncodedSize(); + argumentSize += var->annotation().type->calldataEncodedSize(); CompilerUtils(m_context).fetchFreeMemoryPointer(); if (argumentSize == 0) @@ -418,7 +419,7 @@ bool Compiler::visit(FunctionDefinition const& _function) for (ASTPointer<VariableDeclaration const> const& variable: _function.parameters()) { m_context.addVariable(*variable, parametersSize); - parametersSize -= variable->type()->sizeOnStack(); + parametersSize -= variable->annotation().type->sizeOnStack(); } for (ASTPointer<VariableDeclaration const> const& variable: _function.returnParameters()) @@ -594,9 +595,9 @@ bool Compiler::visit(Return const& _return) //@todo modifications are needed to make this work with functions returning multiple values if (Expression const* expression = _return.expression()) { - solAssert(_return.functionReturnParameters(), "Invalid return parameters pointer."); - VariableDeclaration const& firstVariable = *_return.functionReturnParameters()->parameters().front(); - compileExpression(*expression, firstVariable.type()); + solAssert(_return.annotation().functionReturnParameters, "Invalid return parameters pointer."); + VariableDeclaration const& firstVariable = *_return.annotation().functionReturnParameters->parameters().front(); + compileExpression(*expression, firstVariable.annotation().type); CompilerUtils(m_context).moveToStackVariable(firstVariable); } for (unsigned i = 0; i < m_stackCleanupForReturn; ++i) @@ -619,7 +620,7 @@ bool Compiler::visit(VariableDeclarationStatement const& _variableDeclarationSta CompilerContext::LocationSetter locationSetter(m_context, _variableDeclarationStatement); if (Expression const* expression = _variableDeclarationStatement.expression()) { - compileExpression(*expression, _variableDeclarationStatement.declaration().type()); + compileExpression(*expression, _variableDeclarationStatement.declaration().annotation().type); CompilerUtils(m_context).moveToStackVariable(_variableDeclarationStatement.declaration()); } checker.check(); @@ -632,7 +633,7 @@ bool Compiler::visit(ExpressionStatement const& _expressionStatement) CompilerContext::LocationSetter locationSetter(m_context, _expressionStatement); Expression const& expression = _expressionStatement.expression(); compileExpression(expression); - CompilerUtils(m_context).popStackElement(*expression.type()); + CompilerUtils(m_context).popStackElement(*expression.annotation().type); checker.check(); return false; } @@ -672,7 +673,7 @@ void Compiler::appendModifierOrFunctionCode() ASTPointer<ModifierInvocation> const& modifierInvocation = m_currentFunction->modifiers()[m_modifierDepth]; // constructor call should be excluded - if (dynamic_cast<ContractDefinition const*>(&modifierInvocation->name()->referencedDeclaration())) + if (dynamic_cast<ContractDefinition const*>(modifierInvocation->name()->annotation().referencedDeclaration)) { ++m_modifierDepth; appendModifierOrFunctionCode(); @@ -688,7 +689,7 @@ void Compiler::appendModifierOrFunctionCode() m_context.addVariable(*modifier.parameters()[i]); compileExpression( *modifierInvocation->arguments()[i], - modifier.parameters()[i]->type() + modifier.parameters()[i]->annotation().type ); } for (VariableDeclaration const* localVariable: modifier.localVariables()) @@ -710,7 +711,7 @@ void Compiler::appendStackVariableInitialisation(VariableDeclaration const& _var { CompilerContext::LocationSetter location(m_context, _variable); m_context.addVariable(_variable); - CompilerUtils(m_context).pushZeroValue(*_variable.type()); + CompilerUtils(m_context).pushZeroValue(*_variable.annotation().type); } void Compiler::compileExpression(Expression const& _expression, TypePointer const& _targetType) @@ -718,7 +719,7 @@ void Compiler::compileExpression(Expression const& _expression, TypePointer cons ExpressionCompiler expressionCompiler(m_context, m_optimize); expressionCompiler.compile(_expression); if (_targetType) - CompilerUtils(m_context).convertType(*_expression.type(), *_targetType); + CompilerUtils(m_context).convertType(*_expression.annotation().type, *_targetType); } eth::Assembly Compiler::cloneRuntime() diff --git a/libsolidity/Compiler.h b/libsolidity/Compiler.h index c7dd7d90..7b7cffcf 100644 --- a/libsolidity/Compiler.h +++ b/libsolidity/Compiler.h @@ -37,10 +37,8 @@ public: explicit Compiler(bool _optimize = false, unsigned _runs = 200): m_optimize(_optimize), m_optimizeRuns(_runs), - m_context(), m_returnTag(m_context.newTag()) - { - } + { } void compileContract( ContractDefinition const& _contract, @@ -71,7 +69,8 @@ public: eth::AssemblyItem functionEntryLabel(FunctionDefinition const& _function) const; private: - /// Registers the non-function objects inside the contract with the context. + /// Registers the non-function objects inside the contract with the context and stores the basic + /// information about the contract like the AST annotations. void initializeContext( ContractDefinition const& _contract, std::map<ContractDefinition const*, eth::Assembly const*> const& _compiledContracts diff --git a/libsolidity/CompilerContext.h b/libsolidity/CompilerContext.h index 39f4e75f..46ebfcf8 100644 --- a/libsolidity/CompilerContext.h +++ b/libsolidity/CompilerContext.h @@ -29,6 +29,7 @@ #include <libevmasm/Assembly.h> #include <libsolidity/ASTForward.h> #include <libsolidity/Types.h> +#include <libsolidity/ASTAnnotations.h> #include <libdevcore/Common.h> namespace dev { diff --git a/libsolidity/CompilerStack.cpp b/libsolidity/CompilerStack.cpp index 70bb1b4c..e6b87264 100644 --- a/libsolidity/CompilerStack.cpp +++ b/libsolidity/CompilerStack.cpp @@ -27,6 +27,7 @@ #include <libsolidity/Parser.h> #include <libsolidity/GlobalContext.h> #include <libsolidity/NameAndTypeResolver.h> +#include <libsolidity/TypeChecker.h> #include <libsolidity/Compiler.h> #include <libsolidity/CompilerStack.h> #include <libsolidity/InterfaceHandler.h> @@ -76,6 +77,7 @@ void CompilerStack::reset(bool _keepSources, bool _addStandardSources) m_globalContext.reset(); m_sourceOrder.clear(); m_contracts.clear(); + m_errors.clear(); } bool CompilerStack::addSource(string const& _name, string const& _content, bool _isLibrary) @@ -93,8 +95,10 @@ void CompilerStack::setSource(string const& _sourceCode) addSource("", _sourceCode); } -void CompilerStack::parse() +bool CompilerStack::parse() { + m_errors.clear(); + for (auto& sourcePair: m_sources) { sourcePair.second.scanner->reset(); @@ -116,6 +120,7 @@ void CompilerStack::parse() resolver.resolveNamesAndTypes(*contract); m_contracts[contract->name()].contract = contract; } + InterfaceHandler interfaceHandler; for (Source const* source: m_sourceOrder) for (ASTPointer<ASTNode> const& node: source->ast->nodes()) @@ -123,18 +128,22 @@ void CompilerStack::parse() { m_globalContext->setCurrentContract(*contract); resolver.updateDeclaration(*m_globalContext->currentThis()); - resolver.checkTypeRequirements(*contract); + TypeChecker typeChecker; + bool typesFine = typeChecker.checkTypeRequirements(*contract); + if (!typesFine) + m_errors += typeChecker.errors(); contract->setDevDocumentation(interfaceHandler.devDocumentation(*contract)); contract->setUserDocumentation(interfaceHandler.userDocumentation(*contract)); m_contracts[contract->name()].contract = contract; } - m_parseSuccessful = true; + m_parseSuccessful = m_errors.empty(); + return m_parseSuccessful; } -void CompilerStack::parse(string const& _sourceCode) +bool CompilerStack::parse(string const& _sourceCode) { setSource(_sourceCode); - parse(); + return parse(); } vector<string> CompilerStack::contractNames() const @@ -148,17 +157,18 @@ vector<string> CompilerStack::contractNames() const } -void CompilerStack::compile(bool _optimize, unsigned _runs) +bool CompilerStack::compile(bool _optimize, unsigned _runs) { if (!m_parseSuccessful) - parse(); + if (!parse()) + return false; map<ContractDefinition const*, eth::Assembly const*> compiledContracts; for (Source const* source: m_sourceOrder) for (ASTPointer<ASTNode> const& node: source->ast->nodes()) if (ContractDefinition* contract = dynamic_cast<ContractDefinition*>(node.get())) { - if (!contract->isFullyImplemented()) + if (!contract->annotation().isFullyImplemented) continue; shared_ptr<Compiler> compiler = make_shared<Compiler>(_optimize, _runs); compiler->compileContract(*contract, compiledContracts); @@ -172,13 +182,12 @@ void CompilerStack::compile(bool _optimize, unsigned _runs) cloneCompiler.compileClone(*contract, compiledContracts); compiledContract.cloneObject = cloneCompiler.assembledObject(); } + return true; } -eth::LinkerObject const& CompilerStack::compile(string const& _sourceCode, bool _optimize) +bool CompilerStack::compile(string const& _sourceCode, bool _optimize) { - parse(_sourceCode); - compile(_optimize); - return object(); + return parse(_sourceCode) && compile(_optimize); } void CompilerStack::link(const std::map<string, h160>& _libraries) @@ -317,12 +326,6 @@ size_t CompilerStack::functionEntryPoint( return 0; } -eth::LinkerObject CompilerStack::staticCompile(std::string const& _sourceCode, bool _optimize) -{ - CompilerStack stack; - return stack.compile(_sourceCode, _optimize); -} - tuple<int, int, int, int> CompilerStack::positionFromSourceLocation(SourceLocation const& _sourceLocation) const { int startLine; diff --git a/libsolidity/CompilerStack.h b/libsolidity/CompilerStack.h index 99e8af1a..e541ea47 100644 --- a/libsolidity/CompilerStack.h +++ b/libsolidity/CompilerStack.h @@ -54,6 +54,7 @@ class SourceUnit; class Compiler; class GlobalContext; class InterfaceHandler; +struct Error; enum class DocumentationType: uint8_t { @@ -79,22 +80,28 @@ public: /// Adds a source object (e.g. file) to the parser. After this, parse has to be called again. /// @returns true if a source object by the name already existed and was replaced. - void addSources(StringMap const& _nameContents, bool _isLibrary = false) { for (auto const& i: _nameContents) addSource(i.first, i.second, _isLibrary); } + void addSources(StringMap const& _nameContents, bool _isLibrary = false) + { + for (auto const& i: _nameContents) addSource(i.first, i.second, _isLibrary); + } bool addSource(std::string const& _name, std::string const& _content, bool _isLibrary = false); void setSource(std::string const& _sourceCode); /// Parses all source units that were added - void parse(); + /// @returns false on error. + bool parse(); /// Sets the given source code as the only source unit apart from standard sources and parses it. - void parse(std::string const& _sourceCode); + /// @returns false on error. + bool parse(std::string const& _sourceCode); /// Returns a list of the contract names in the sources. std::vector<std::string> contractNames() const; std::string defaultContractName() const; /// Compiles the source units that were previously added and parsed. - void compile(bool _optimize = false, unsigned _runs = 200); + /// @returns false on error. + bool compile(bool _optimize = false, unsigned _runs = 200); /// Parses and compiles the given source code. - /// @returns the compiled linker object - eth::LinkerObject const& compile(std::string const& _sourceCode, bool _optimize = false); + /// @returns false on error. + bool compile(std::string const& _sourceCode, bool _optimize = false); /// Inserts the given addresses into the linker objects of all compiled contracts. void link(std::map<std::string, h160> const& _libraries); @@ -150,15 +157,14 @@ public: FunctionDefinition const& _function ) const; - /// Compile the given @a _sourceCode to bytecode. If a scanner is provided, it is used for - /// scanning the source code - this is useful for printing exception information. - static eth::LinkerObject staticCompile(std::string const& _sourceCode, bool _optimize = false); - /// Helper function for logs printing. Do only use in error cases, it's quite expensive. /// line and columns are numbered starting from 1 with following order: /// start line, start column, end line, end column std::tuple<int, int, int, int> positionFromSourceLocation(SourceLocation const& _sourceLocation) const; + /// @returns the list of errors that occured during parsing and type checking. + std::vector<std::shared_ptr<Error const>> const& errors() const { return m_errors; } + private: /** * Information pertaining to one source unit, filled gradually during parsing and compilation. @@ -198,6 +204,7 @@ private: std::shared_ptr<GlobalContext> m_globalContext; std::vector<Source const*> m_sourceOrder; std::map<std::string const, Contract> m_contracts; + std::vector<std::shared_ptr<Error const>> m_errors; }; } diff --git a/libsolidity/CompilerUtils.cpp b/libsolidity/CompilerUtils.cpp index e370c6be..b9b554e8 100644 --- a/libsolidity/CompilerUtils.cpp +++ b/libsolidity/CompilerUtils.cpp @@ -600,7 +600,7 @@ void CompilerUtils::pushZeroValue(const Type& _type) void CompilerUtils::moveToStackVariable(VariableDeclaration const& _variable) { unsigned const stackPosition = m_context.baseToCurrentStackOffset(m_context.baseStackOffsetOfVariable(_variable)); - unsigned const size = _variable.type()->sizeOnStack(); + unsigned const size = _variable.annotation().type->sizeOnStack(); solAssert(stackPosition >= size, "Variable size and position mismatch."); // move variable starting from its top end in the stack if (stackPosition - size + 1 > 16) diff --git a/libsolidity/CompilerUtils.h b/libsolidity/CompilerUtils.h index 0875ddd2..568a6307 100644 --- a/libsolidity/CompilerUtils.h +++ b/libsolidity/CompilerUtils.h @@ -170,7 +170,7 @@ unsigned CompilerUtils::sizeOnStack(std::vector<T> const& _variables) { unsigned size = 0; for (T const& variable: _variables) - size += variable->type()->sizeOnStack(); + size += variable->annotation().type->sizeOnStack(); return size; } diff --git a/libsolidity/ConstantEvaluator.cpp b/libsolidity/ConstantEvaluator.cpp new file mode 100644 index 00000000..5936b3d4 --- /dev/null +++ b/libsolidity/ConstantEvaluator.cpp @@ -0,0 +1,59 @@ +/* + This file is part of cpp-ethereum. + + cpp-ethereum is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + cpp-ethereum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>. +*/ +/** + * @author Christian <c@ethdev.com> + * @date 2015 + * Evaluator for types of constant expressions. + */ + +#include <libsolidity/ConstantEvaluator.h> +#include <libsolidity/AST.h> + +using namespace std; +using namespace dev; +using namespace dev::solidity; + + +void ConstantEvaluator::endVisit(UnaryOperation const& _operation) +{ + TypePointer const& subType = _operation.subExpression().annotation().type; + if (!dynamic_cast<IntegerConstantType const*>(subType.get())) + BOOST_THROW_EXCEPTION(_operation.subExpression().createTypeError("Invalid constant expression.")); + TypePointer t = subType->unaryOperatorResult(_operation.getOperator()); + _operation.annotation().type = t; +} + +void ConstantEvaluator::endVisit(BinaryOperation const& _operation) +{ + TypePointer const& leftType = _operation.leftExpression().annotation().type; + TypePointer const& rightType = _operation.rightExpression().annotation().type; + if (!dynamic_cast<IntegerConstantType const*>(leftType.get())) + BOOST_THROW_EXCEPTION(_operation.leftExpression().createTypeError("Invalid constant expression.")); + if (!dynamic_cast<IntegerConstantType const*>(rightType.get())) + BOOST_THROW_EXCEPTION(_operation.rightExpression().createTypeError("Invalid constant expression.")); + TypePointer commonType = leftType->binaryOperatorResult(_operation.getOperator(), rightType); + if (Token::isCompareOp(_operation.getOperator())) + commonType = make_shared<BoolType>(); + _operation.annotation().type = commonType; +} + +void ConstantEvaluator::endVisit(Literal const& _literal) +{ + _literal.annotation().type = Type::forLiteral(_literal); + if (!_literal.annotation().type) + BOOST_THROW_EXCEPTION(_literal.createTypeError("Invalid literal value.")); +} diff --git a/libsolidity/ConstantEvaluator.h b/libsolidity/ConstantEvaluator.h new file mode 100644 index 00000000..cf3d2094 --- /dev/null +++ b/libsolidity/ConstantEvaluator.h @@ -0,0 +1,50 @@ +/* + This file is part of cpp-ethereum. + + cpp-ethereum is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + cpp-ethereum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>. +*/ +/** + * @author Christian <c@ethdev.com> + * @date 2015 + * Evaluator for types of constant expressions. + */ + +#pragma once + +#include <libsolidity/ASTVisitor.h> + +namespace dev +{ +namespace solidity +{ + +class TypeChecker; + +/** + * Small drop-in replacement for TypeChecker to evaluate simple expressions of integer constants. + */ +class ConstantEvaluator: private ASTConstVisitor +{ +public: + ConstantEvaluator(Expression const& _expr) { _expr.accept(*this); } + +private: + virtual void endVisit(BinaryOperation const& _operation); + virtual void endVisit(UnaryOperation const& _operation); + virtual void endVisit(Literal const& _literal); + +}; + +} +} diff --git a/libsolidity/Exceptions.h b/libsolidity/Exceptions.h index 4bb6644b..645b368f 100644 --- a/libsolidity/Exceptions.h +++ b/libsolidity/Exceptions.h @@ -32,12 +32,16 @@ namespace dev namespace solidity { -struct ParserError: virtual Exception {}; -struct TypeError: virtual Exception {}; -struct DeclarationError: virtual Exception {}; +struct Error: virtual Exception {}; + +struct ParserError: virtual Error {}; +struct TypeError: virtual Error {}; +struct DeclarationError: virtual Error {}; +struct DocstringParsingError: virtual Error {}; + struct CompilerError: virtual Exception {}; struct InternalCompilerError: virtual Exception {}; -struct DocstringParsingError: virtual Exception {}; +struct FatalError: virtual Exception {}; using errorSourceLocationInfo = std::pair<std::string, SourceLocation>; diff --git a/libsolidity/ExpressionCompiler.cpp b/libsolidity/ExpressionCompiler.cpp index 7db0dde9..65706602 100644 --- a/libsolidity/ExpressionCompiler.cpp +++ b/libsolidity/ExpressionCompiler.cpp @@ -48,12 +48,12 @@ void ExpressionCompiler::appendStateVariableInitialization(VariableDeclaration c { if (!_varDecl.value()) return; - TypePointer type = _varDecl.value()->type(); + TypePointer type = _varDecl.value()->annotation().type; solAssert(!!type, "Type information not available."); CompilerContext::LocationSetter locationSetter(m_context, _varDecl); _varDecl.value()->accept(*this); - if (_varDecl.type()->dataStoredIn(DataLocation::Storage)) + if (_varDecl.annotation().type->dataStoredIn(DataLocation::Storage)) { // reference type, only convert value to mobile type and do final conversion in storeValue. utils().convertType(*type, *type->mobileType()); @@ -61,8 +61,8 @@ void ExpressionCompiler::appendStateVariableInitialization(VariableDeclaration c } else { - utils().convertType(*type, *_varDecl.type()); - type = _varDecl.type(); + utils().convertType(*type, *_varDecl.annotation().type); + type = _varDecl.annotation().type; } StorageItem(m_context, _varDecl).storeValue(*type, _varDecl.location(), true); } @@ -71,10 +71,10 @@ void ExpressionCompiler::appendConstStateVariableAccessor(VariableDeclaration co { solAssert(_varDecl.isConstant(), ""); _varDecl.value()->accept(*this); - utils().convertType(*_varDecl.value()->type(), *_varDecl.type()); + utils().convertType(*_varDecl.value()->annotation().type, *_varDecl.annotation().type); // append return - m_context << eth::dupInstruction(_varDecl.type()->sizeOnStack() + 1); + m_context << eth::dupInstruction(_varDecl.annotation().type->sizeOnStack() + 1); m_context.appendJump(eth::AssemblyItem::JumpType::OutOfFunction); } @@ -90,7 +90,7 @@ void ExpressionCompiler::appendStateVariableAccessor(VariableDeclaration const& auto const& location = m_context.storageLocationOfVariable(_varDecl); m_context << location.first << u256(location.second); - TypePointer returnType = _varDecl.type(); + TypePointer returnType = _varDecl.annotation().type; for (size_t i = 0; i < paramTypes.size(); ++i) { @@ -179,11 +179,11 @@ bool ExpressionCompiler::visit(Assignment const& _assignment) { CompilerContext::LocationSetter locationSetter(m_context, _assignment); _assignment.rightHandSide().accept(*this); - TypePointer type = _assignment.rightHandSide().type(); - if (!_assignment.type()->dataStoredIn(DataLocation::Storage)) + TypePointer type = _assignment.rightHandSide().annotation().type; + if (!_assignment.annotation().type->dataStoredIn(DataLocation::Storage)) { - utils().convertType(*type, *_assignment.type()); - type = _assignment.type(); + utils().convertType(*type, *_assignment.annotation().type); + type = _assignment.annotation().type; } else { @@ -197,9 +197,9 @@ bool ExpressionCompiler::visit(Assignment const& _assignment) Token::Value op = _assignment.assignmentOperator(); if (op != Token::Assign) // compound assignment { - solAssert(_assignment.type()->isValueType(), "Compound operators not implemented for non-value types."); + solAssert(_assignment.annotation().type->isValueType(), "Compound operators not implemented for non-value types."); unsigned lvalueSize = m_currentLValue->sizeOnStack(); - unsigned itemSize = _assignment.type()->sizeOnStack(); + unsigned itemSize = _assignment.annotation().type->sizeOnStack(); if (lvalueSize > 0) { utils().copyToStackTop(lvalueSize + itemSize, itemSize); @@ -207,7 +207,7 @@ bool ExpressionCompiler::visit(Assignment const& _assignment) // value lvalue_ref value lvalue_ref } m_currentLValue->retrieveValue(_assignment.location(), true); - appendOrdinaryBinaryOperatorCode(Token::AssignmentToBinaryOp(op), *_assignment.type()); + appendOrdinaryBinaryOperatorCode(Token::AssignmentToBinaryOp(op), *_assignment.annotation().type); if (lvalueSize > 0) { solAssert(itemSize + lvalueSize <= 16, "Stack too deep, try removing local variables."); @@ -228,9 +228,9 @@ bool ExpressionCompiler::visit(UnaryOperation const& _unaryOperation) // the operator should know how to convert itself and to which types it applies, so // put this code together with "Type::acceptsBinary/UnaryOperator" into a class that // represents the operator - if (_unaryOperation.type()->category() == Type::Category::IntegerConstant) + if (_unaryOperation.annotation().type->category() == Type::Category::IntegerConstant) { - m_context << _unaryOperation.type()->literalValue(nullptr); + m_context << _unaryOperation.annotation().type->literalValue(nullptr); return false; } @@ -259,7 +259,7 @@ bool ExpressionCompiler::visit(UnaryOperation const& _unaryOperation) if (!_unaryOperation.isPrefixOperation()) { // store value for later - solAssert(_unaryOperation.type()->sizeOnStack() == 1, "Stack size != 1 not implemented."); + solAssert(_unaryOperation.annotation().type->sizeOnStack() == 1, "Stack size != 1 not implemented."); m_context << eth::Instruction::DUP1; if (m_currentLValue->sizeOnStack() > 0) for (unsigned i = 1 + m_currentLValue->sizeOnStack(); i > 0; --i) @@ -275,7 +275,7 @@ bool ExpressionCompiler::visit(UnaryOperation const& _unaryOperation) for (unsigned i = m_currentLValue->sizeOnStack(); i > 0; --i) m_context << eth::swapInstruction(i); m_currentLValue->storeValue( - *_unaryOperation.type(), _unaryOperation.location(), + *_unaryOperation.annotation().type, _unaryOperation.location(), !_unaryOperation.isPrefixOperation()); m_currentLValue.reset(); break; @@ -297,7 +297,8 @@ bool ExpressionCompiler::visit(BinaryOperation const& _binaryOperation) CompilerContext::LocationSetter locationSetter(m_context, _binaryOperation); Expression const& leftExpression = _binaryOperation.leftExpression(); Expression const& rightExpression = _binaryOperation.rightExpression(); - Type const& commonType = _binaryOperation.commonType(); + solAssert(!!_binaryOperation.annotation().commonType, ""); + Type const& commonType = *_binaryOperation.annotation().commonType; Token::Value const c_op = _binaryOperation.getOperator(); if (c_op == Token::And || c_op == Token::Or) // special case: short-circuiting @@ -312,22 +313,22 @@ bool ExpressionCompiler::visit(BinaryOperation const& _binaryOperation) // for commutative operators, push the literal as late as possible to allow improved optimization auto isLiteral = [](Expression const& _e) { - return dynamic_cast<Literal const*>(&_e) || _e.type()->category() == Type::Category::IntegerConstant; + return dynamic_cast<Literal const*>(&_e) || _e.annotation().type->category() == Type::Category::IntegerConstant; }; bool swap = m_optimize && Token::isCommutativeOp(c_op) && isLiteral(rightExpression) && !isLiteral(leftExpression); if (swap) { leftExpression.accept(*this); - utils().convertType(*leftExpression.type(), commonType, cleanupNeeded); + utils().convertType(*leftExpression.annotation().type, commonType, cleanupNeeded); rightExpression.accept(*this); - utils().convertType(*rightExpression.type(), commonType, cleanupNeeded); + utils().convertType(*rightExpression.annotation().type, commonType, cleanupNeeded); } else { rightExpression.accept(*this); - utils().convertType(*rightExpression.type(), commonType, cleanupNeeded); + utils().convertType(*rightExpression.annotation().type, commonType, cleanupNeeded); leftExpression.accept(*this); - utils().convertType(*leftExpression.type(), commonType, cleanupNeeded); + utils().convertType(*leftExpression.annotation().type, commonType, cleanupNeeded); } if (Token::isCompareOp(c_op)) appendCompareOperatorCode(c_op, commonType); @@ -343,25 +344,25 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) { CompilerContext::LocationSetter locationSetter(m_context, _functionCall); using Location = FunctionType::Location; - if (_functionCall.isTypeConversion()) + if (_functionCall.annotation().isTypeConversion) { solAssert(_functionCall.arguments().size() == 1, ""); solAssert(_functionCall.names().empty(), ""); Expression const& firstArgument = *_functionCall.arguments().front(); firstArgument.accept(*this); - utils().convertType(*firstArgument.type(), *_functionCall.type()); + utils().convertType(*firstArgument.annotation().type, *_functionCall.annotation().type); return false; } FunctionTypePointer functionType; - if (_functionCall.isStructConstructorCall()) + if (_functionCall.annotation().isStructConstructorCall) { - auto const& type = dynamic_cast<TypeType const&>(*_functionCall.expression().type()); + auto const& type = dynamic_cast<TypeType const&>(*_functionCall.expression().annotation().type); auto const& structType = dynamic_cast<StructType const&>(*type.actualType()); functionType = structType.constructorType(); } else - functionType = dynamic_pointer_cast<FunctionType const>(_functionCall.expression().type()); + functionType = dynamic_pointer_cast<FunctionType const>(_functionCall.expression().annotation().type); TypePointers const& parameterTypes = functionType->parameterTypes(); vector<ASTPointer<Expression const>> const& callArguments = _functionCall.arguments(); @@ -385,9 +386,9 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) solAssert(found, ""); } - if (_functionCall.isStructConstructorCall()) + if (_functionCall.annotation().isStructConstructorCall) { - TypeType const& type = dynamic_cast<TypeType const&>(*_functionCall.expression().type()); + TypeType const& type = dynamic_cast<TypeType const&>(*_functionCall.expression().annotation().type); auto const& structType = dynamic_cast<StructType const&>(*type.actualType()); m_context << max(u256(32u), structType.memorySize()); @@ -397,7 +398,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) for (unsigned i = 0; i < arguments.size(); ++i) { arguments[i]->accept(*this); - utils().convertType(*arguments[i]->type(), *functionType->parameterTypes()[i]); + utils().convertType(*arguments[i]->annotation().type, *functionType->parameterTypes()[i]); utils().storeInMemoryDynamic(*functionType->parameterTypes()[i]); } m_context << eth::Instruction::POP; @@ -416,7 +417,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) for (unsigned i = 0; i < arguments.size(); ++i) { arguments[i]->accept(*this); - utils().convertType(*arguments[i]->type(), *function.parameterTypes()[i]); + utils().convertType(*arguments[i]->annotation().type, *function.parameterTypes()[i]); } _functionCall.expression().accept(*this); @@ -449,7 +450,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) for (auto const& arg: arguments) { arg->accept(*this); - argumentTypes.push_back(arg->type()); + argumentTypes.push_back(arg->annotation().type); } ContractDefinition const& contract = dynamic_cast<ContractType const&>(*function.returnParameterTypes().front()).contractDefinition(); @@ -481,7 +482,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) _functionCall.expression().accept(*this); arguments.front()->accept(*this); - utils().convertType(*arguments.front()->type(), IntegerType(256), true); + utils().convertType(*arguments.front()->annotation().type, IntegerType(256), true); // Note that function is not the original function, but the ".gas" function. // Its values of gasSet and valueSet is equal to the original function's though. unsigned stackDepth = (function.gasSet() ? 1 : 0) + (function.valueSet() ? 1 : 0); @@ -505,7 +506,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) m_context << u256(0); // do not send gas (there still is the stipend) arguments.front()->accept(*this); utils().convertType( - *arguments.front()->type(), + *arguments.front()->annotation().type, *function.parameterTypes().front(), true ); appendExternalFunctionCall( @@ -525,7 +526,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) break; case Location::Suicide: arguments.front()->accept(*this); - utils().convertType(*arguments.front()->type(), *function.parameterTypes().front(), true); + utils().convertType(*arguments.front()->annotation().type, *function.parameterTypes().front(), true); m_context << eth::Instruction::SUICIDE; break; case Location::SHA3: @@ -534,7 +535,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) for (auto const& arg: arguments) { arg->accept(*this); - argumentTypes.push_back(arg->type()); + argumentTypes.push_back(arg->annotation().type); } utils().fetchFreeMemoryPointer(); utils().encodeToMemory(argumentTypes, TypePointers(), function.padArguments(), true); @@ -552,12 +553,12 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) for (unsigned arg = logNumber; arg > 0; --arg) { arguments[arg]->accept(*this); - utils().convertType(*arguments[arg]->type(), *function.parameterTypes()[arg], true); + utils().convertType(*arguments[arg]->annotation().type, *function.parameterTypes()[arg], true); } arguments.front()->accept(*this); utils().fetchFreeMemoryPointer(); utils().encodeToMemory( - {arguments.front()->type()}, + {arguments.front()->annotation().type}, {function.parameterTypes().front()}, false, true); @@ -577,7 +578,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) ++numIndexed; arguments[arg - 1]->accept(*this); utils().convertType( - *arguments[arg - 1]->type(), + *arguments[arg - 1]->annotation().type, *function.parameterTypes()[arg - 1], true ); @@ -596,7 +597,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) if (!event.parameters()[arg]->isIndexed()) { arguments[arg]->accept(*this); - nonIndexedArgTypes.push_back(arguments[arg]->type()); + nonIndexedArgTypes.push_back(arguments[arg]->annotation().type); nonIndexedParamTypes.push_back(function.parameterTypes()[arg]); } utils().fetchFreeMemoryPointer(); @@ -609,7 +610,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) case Location::BlockHash: { arguments[0]->accept(*this); - utils().convertType(*arguments[0]->type(), *function.parameterTypes()[0], true); + utils().convertType(*arguments[0]->annotation().type, *function.parameterTypes()[0], true); m_context << eth::Instruction::BLOCKHASH; break; } @@ -644,24 +645,24 @@ void ExpressionCompiler::endVisit(MemberAccess const& _memberAccess) { CompilerContext::LocationSetter locationSetter(m_context, _memberAccess); ASTString const& member = _memberAccess.memberName(); - switch (_memberAccess.expression().type()->category()) + switch (_memberAccess.expression().annotation().type->category()) { case Type::Category::Contract: { bool alsoSearchInteger = false; - ContractType const& type = dynamic_cast<ContractType const&>(*_memberAccess.expression().type()); + ContractType const& type = dynamic_cast<ContractType const&>(*_memberAccess.expression().annotation().type); if (type.isSuper()) { - solAssert(!!_memberAccess.referencedDeclaration(), "Referenced declaration not resolved."); + solAssert(!!_memberAccess.annotation().referencedDeclaration, "Referenced declaration not resolved."); m_context << m_context.superFunctionEntryLabel( - dynamic_cast<FunctionDefinition const&>(*_memberAccess.referencedDeclaration()), + dynamic_cast<FunctionDefinition const&>(*_memberAccess.annotation().referencedDeclaration), type.contractDefinition() ).pushTag(); } else { // ordinary contract type - if (Declaration const* declaration = _memberAccess.referencedDeclaration()) + if (Declaration const* declaration = _memberAccess.annotation().referencedDeclaration) { u256 identifier; if (auto const* variable = dynamic_cast<VariableDeclaration const*>(declaration)) @@ -684,7 +685,7 @@ void ExpressionCompiler::endVisit(MemberAccess const& _memberAccess) if (member == "balance") { utils().convertType( - *_memberAccess.expression().type(), + *_memberAccess.expression().annotation().type, IntegerType(0, IntegerType::Modifier::Address), true ); @@ -692,7 +693,7 @@ void ExpressionCompiler::endVisit(MemberAccess const& _memberAccess) } else if ((set<string>{"send", "call", "callcode"}).count(member)) utils().convertType( - *_memberAccess.expression().type(), + *_memberAccess.expression().annotation().type, IntegerType(0, IntegerType::Modifier::Address), true ); @@ -700,7 +701,7 @@ void ExpressionCompiler::endVisit(MemberAccess const& _memberAccess) BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Invalid member access to integer.")); break; case Type::Category::Function: - solAssert(!!_memberAccess.expression().type()->memberType(member), + solAssert(!!_memberAccess.expression().annotation().type->memberType(member), "Invalid member access to function."); break; case Type::Category::Magic: @@ -735,7 +736,7 @@ void ExpressionCompiler::endVisit(MemberAccess const& _memberAccess) break; case Type::Category::Struct: { - StructType const& type = dynamic_cast<StructType const&>(*_memberAccess.expression().type()); + StructType const& type = dynamic_cast<StructType const&>(*_memberAccess.expression().annotation().type); switch (type.location()) { case DataLocation::Storage: @@ -748,7 +749,7 @@ void ExpressionCompiler::endVisit(MemberAccess const& _memberAccess) case DataLocation::Memory: { m_context << type.memoryOffsetOfMember(member) << eth::Instruction::ADD; - setLValue<MemoryItem>(_memberAccess, *_memberAccess.type()); + setLValue<MemoryItem>(_memberAccess, *_memberAccess.annotation().type); break; } default: @@ -758,13 +759,13 @@ void ExpressionCompiler::endVisit(MemberAccess const& _memberAccess) } case Type::Category::Enum: { - EnumType const& type = dynamic_cast<EnumType const&>(*_memberAccess.expression().type()); + EnumType const& type = dynamic_cast<EnumType const&>(*_memberAccess.expression().annotation().type); m_context << type.memberValue(_memberAccess.memberName()); break; } case Type::Category::TypeType: { - TypeType const& type = dynamic_cast<TypeType const&>(*_memberAccess.expression().type()); + TypeType const& type = dynamic_cast<TypeType const&>(*_memberAccess.expression().annotation().type); solAssert( !type.members().membersByName(_memberAccess.memberName()).empty(), "Invalid member access to " + type.toString(false) @@ -772,12 +773,12 @@ void ExpressionCompiler::endVisit(MemberAccess const& _memberAccess) if (dynamic_cast<ContractType const*>(type.actualType().get())) { - auto const& funType = dynamic_cast<FunctionType const&>(*_memberAccess.type()); + auto const& funType = dynamic_cast<FunctionType const&>(*_memberAccess.annotation().type); if (funType.location() != FunctionType::Location::Internal) m_context << funType.externalIdentifier(); else { - auto const* function = dynamic_cast<FunctionDefinition const*>(_memberAccess.referencedDeclaration()); + auto const* function = dynamic_cast<FunctionDefinition const*>(_memberAccess.annotation().referencedDeclaration); solAssert(!!function, "Function not found in member access"); m_context << m_context.functionEntryLabel(*function).pushTag(); } @@ -789,7 +790,7 @@ void ExpressionCompiler::endVisit(MemberAccess const& _memberAccess) case Type::Category::Array: { solAssert(member == "length", "Illegal array member."); - auto const& type = dynamic_cast<ArrayType const&>(*_memberAccess.expression().type()); + auto const& type = dynamic_cast<ArrayType const&>(*_memberAccess.expression().annotation().type); if (!type.isDynamicallySized()) { utils().popStackElement(type); @@ -820,7 +821,7 @@ bool ExpressionCompiler::visit(IndexAccess const& _indexAccess) CompilerContext::LocationSetter locationSetter(m_context, _indexAccess); _indexAccess.baseExpression().accept(*this); - Type const& baseType = *_indexAccess.baseExpression().type(); + Type const& baseType = *_indexAccess.baseExpression().annotation().type; if (baseType.category() == Type::Category::Mapping) { @@ -834,7 +835,7 @@ bool ExpressionCompiler::visit(IndexAccess const& _indexAccess) // stack: base index mem // note: the following operations must not allocate memory! utils().encodeToMemory( - TypePointers{_indexAccess.indexExpression()->type()}, + TypePointers{_indexAccess.indexExpression()->annotation().type}, TypePointers{keyType}, false, true @@ -876,7 +877,7 @@ bool ExpressionCompiler::visit(IndexAccess const& _indexAccess) setLValueToStorageItem(_indexAccess); break; case DataLocation::Memory: - setLValue<MemoryItem>(_indexAccess, *_indexAccess.type(), !arrayType.isByteArray()); + setLValue<MemoryItem>(_indexAccess, *_indexAccess.annotation().type, !arrayType.isByteArray()); break; case DataLocation::CallData: //@todo if we implement this, the value in calldata has to be added to the base offset @@ -900,14 +901,14 @@ bool ExpressionCompiler::visit(IndexAccess const& _indexAccess) void ExpressionCompiler::endVisit(Identifier const& _identifier) { CompilerContext::LocationSetter locationSetter(m_context, _identifier); - Declaration const* declaration = &_identifier.referencedDeclaration(); + Declaration const* declaration = _identifier.annotation().referencedDeclaration; if (MagicVariableDeclaration const* magicVar = dynamic_cast<MagicVariableDeclaration const*>(declaration)) { - switch (magicVar->type()->category()) + switch (magicVar->type(_identifier.annotation().contractScope)->category()) { case Type::Category::Contract: // "this" or "super" - if (!dynamic_cast<ContractType const&>(*magicVar->type()).isSuper()) + if (!dynamic_cast<ContractType const&>(*magicVar->type(_identifier.annotation().contractScope)).isSuper()) m_context << eth::Instruction::ADDRESS; break; case Type::Category::Integer: @@ -927,7 +928,7 @@ void ExpressionCompiler::endVisit(Identifier const& _identifier) else { variable->value()->accept(*this); - utils().convertType(*variable->value()->type(), *variable->type()); + utils().convertType(*variable->value()->annotation().type, *variable->annotation().type); } } else if (auto contract = dynamic_cast<ContractDefinition const*>(declaration)) @@ -953,7 +954,7 @@ void ExpressionCompiler::endVisit(Identifier const& _identifier) void ExpressionCompiler::endVisit(Literal const& _literal) { CompilerContext::LocationSetter locationSetter(m_context, _literal); - TypePointer type = _literal.type(); + TypePointer type = _literal.annotation().type; switch (type->category()) { case Type::Category::IntegerConstant: @@ -1141,7 +1142,7 @@ void ExpressionCompiler::appendExternalFunctionCall( bool manualFunctionId = (funKind == FunctionKind::Bare || funKind == FunctionKind::BareCallCode) && !_arguments.empty() && - _arguments.front()->type()->mobileType()->calldataEncodedSize(false) == + _arguments.front()->annotation().type->mobileType()->calldataEncodedSize(false) == CompilerUtils::dataStartOffset; if (manualFunctionId) { @@ -1149,7 +1150,7 @@ void ExpressionCompiler::appendExternalFunctionCall( // function identifier. _arguments.front()->accept(*this); utils().convertType( - *_arguments.front()->type(), + *_arguments.front()->annotation().type, IntegerType(8 * CompilerUtils::dataStartOffset), true ); @@ -1161,7 +1162,7 @@ void ExpressionCompiler::appendExternalFunctionCall( for (size_t i = manualFunctionId ? 1 : 0; i < _arguments.size(); ++i) { _arguments[i]->accept(*this); - argumentTypes.push_back(_arguments[i]->type()); + argumentTypes.push_back(_arguments[i]->annotation().type); } // Copy function identifier to memory. @@ -1266,16 +1267,16 @@ void ExpressionCompiler::appendExpressionCopyToMemory(Type const& _expectedType, { solAssert(_expectedType.isValueType(), "Not implemented for non-value types."); _expression.accept(*this); - utils().convertType(*_expression.type(), _expectedType, true); + utils().convertType(*_expression.annotation().type, _expectedType, true); utils().storeInMemoryDynamic(_expectedType); } void ExpressionCompiler::setLValueFromDeclaration(Declaration const& _declaration, Expression const& _expression) { if (m_context.isLocalVariable(&_declaration)) - setLValue<StackVariable>(_expression, _declaration); + setLValue<StackVariable>(_expression, dynamic_cast<VariableDeclaration const&>(_declaration)); else if (m_context.isStateVariable(&_declaration)) - setLValue<StorageItem>(_expression, _declaration); + setLValue<StorageItem>(_expression, dynamic_cast<VariableDeclaration const&>(_declaration)); else BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_sourceLocation(_expression.location()) @@ -1284,7 +1285,7 @@ void ExpressionCompiler::setLValueFromDeclaration(Declaration const& _declaratio void ExpressionCompiler::setLValueToStorageItem(Expression const& _expression) { - setLValue<StorageItem>(_expression, *_expression.type()); + setLValue<StorageItem>(_expression, *_expression.annotation().type); } CompilerUtils ExpressionCompiler::utils() diff --git a/libsolidity/ExpressionCompiler.h b/libsolidity/ExpressionCompiler.h index 02df4b87..d8fef5af 100644 --- a/libsolidity/ExpressionCompiler.h +++ b/libsolidity/ExpressionCompiler.h @@ -127,7 +127,7 @@ void ExpressionCompiler::setLValue(Expression const& _expression, _Arguments con { solAssert(!m_currentLValue, "Current LValue not reset before trying to set new one."); std::unique_ptr<_LValueType> lvalue(new _LValueType(m_context, _arguments...)); - if (_expression.lvalueRequested()) + if (_expression.annotation().lValueRequested) m_currentLValue = move(lvalue); else lvalue->retrieveValue(_expression.location(), true); diff --git a/libsolidity/InterfaceHandler.cpp b/libsolidity/InterfaceHandler.cpp index fc79b493..50006caf 100644 --- a/libsolidity/InterfaceHandler.cpp +++ b/libsolidity/InterfaceHandler.cpp @@ -96,7 +96,7 @@ string InterfaceHandler::abiInterface(ContractDefinition const& _contractDef) { Json::Value input; input["name"] = p->name(); - input["type"] = p->type()->toString(true); + input["type"] = p->annotation().type->toString(true); input["indexed"] = p->isIndexed(); params.append(input); } diff --git a/libsolidity/LValue.cpp b/libsolidity/LValue.cpp index eb7cc073..81aaeb4d 100644 --- a/libsolidity/LValue.cpp +++ b/libsolidity/LValue.cpp @@ -31,8 +31,8 @@ using namespace dev; using namespace solidity; -StackVariable::StackVariable(CompilerContext& _compilerContext, Declaration const& _declaration): - LValue(_compilerContext, *_declaration.type()), +StackVariable::StackVariable(CompilerContext& _compilerContext, VariableDeclaration const& _declaration): + LValue(_compilerContext, *_declaration.annotation().type), m_baseStackOffset(m_context.baseStackOffsetOfVariable(_declaration)), m_size(m_dataType.sizeOnStack()) { @@ -131,8 +131,8 @@ void MemoryItem::setToZero(SourceLocation const&, bool _removeReference) const m_context << eth::Instruction::POP; } -StorageItem::StorageItem(CompilerContext& _compilerContext, Declaration const& _declaration): - StorageItem(_compilerContext, *_declaration.type()) +StorageItem::StorageItem(CompilerContext& _compilerContext, VariableDeclaration const& _declaration): + StorageItem(_compilerContext, *_declaration.annotation().type) { auto const& location = m_context.storageLocationOfVariable(_declaration); m_context << location.first << u256(location.second); diff --git a/libsolidity/LValue.h b/libsolidity/LValue.h index f02d8ad1..cbbfb102 100644 --- a/libsolidity/LValue.h +++ b/libsolidity/LValue.h @@ -35,6 +35,7 @@ class Declaration; class Type; class ArrayType; class CompilerContext; +class VariableDeclaration; /** * Abstract class used to retrieve, delete and store data in lvalues/variables. @@ -76,7 +77,7 @@ protected: class StackVariable: public LValue { public: - StackVariable(CompilerContext& _compilerContext, Declaration const& _declaration); + StackVariable(CompilerContext& _compilerContext, VariableDeclaration const& _declaration); virtual unsigned sizeOnStack() const override { return 0; } virtual void retrieveValue(SourceLocation const& _location, bool _remove = false) const override; @@ -129,7 +130,7 @@ class StorageItem: public LValue { public: /// Constructs the LValue and pushes the location of @a _declaration onto the stack. - StorageItem(CompilerContext& _compilerContext, Declaration const& _declaration); + StorageItem(CompilerContext& _compilerContext, VariableDeclaration const& _declaration); /// Constructs the LValue and assumes that the storage reference is already on the stack. StorageItem(CompilerContext& _compilerContext, Type const& _type); virtual unsigned sizeOnStack() const override { return 2; } diff --git a/libsolidity/NameAndTypeResolver.cpp b/libsolidity/NameAndTypeResolver.cpp index c3b49abd..c3e31728 100644 --- a/libsolidity/NameAndTypeResolver.cpp +++ b/libsolidity/NameAndTypeResolver.cpp @@ -22,6 +22,7 @@ #include <libsolidity/NameAndTypeResolver.h> #include <libsolidity/AST.h> +#include <libsolidity/TypeChecker.h> #include <libsolidity/Exceptions.h> using namespace std; @@ -31,7 +32,9 @@ namespace dev namespace solidity { -NameAndTypeResolver::NameAndTypeResolver(vector<Declaration const*> const& _globals) +NameAndTypeResolver::NameAndTypeResolver( + vector<Declaration const*> const& _globals +) { for (Declaration const* declaration: _globals) m_scopes[nullptr].registerDeclaration(*declaration); @@ -54,8 +57,8 @@ void NameAndTypeResolver::resolveNamesAndTypes(ContractDefinition& _contract) linearizeBaseContracts(_contract); std::vector<ContractDefinition const*> properBases( - ++_contract.linearizedBaseContracts().begin(), - _contract.linearizedBaseContracts().end() + ++_contract.annotation().linearizedBaseContracts.begin(), + _contract.annotation().linearizedBaseContracts.end() ); for (ContractDefinition const* base: properBases) @@ -108,13 +111,6 @@ void NameAndTypeResolver::resolveNamesAndTypes(ContractDefinition& _contract) } } -void NameAndTypeResolver::checkTypeRequirements(ContractDefinition& _contract) -{ - for (ASTPointer<StructDefinition> const& structDef: _contract.definedStructs()) - structDef->checkValidityOfMembers(); - _contract.checkTypeRequirements(); -} - void NameAndTypeResolver::updateDeclaration(Declaration const& _declaration) { m_scopes[nullptr].registerDeclaration(_declaration, false, true); @@ -187,23 +183,23 @@ void NameAndTypeResolver::linearizeBaseContracts(ContractDefinition& _contract) list<list<ContractDefinition const*>> input(1, {}); for (ASTPointer<InheritanceSpecifier> const& baseSpecifier: _contract.baseContracts()) { - ASTPointer<Identifier> baseName = baseSpecifier->name(); - auto base = dynamic_cast<ContractDefinition const*>(&baseName->referencedDeclaration()); + Identifier const& baseName = baseSpecifier->name(); + auto base = dynamic_cast<ContractDefinition const*>(baseName.annotation().referencedDeclaration); if (!base) - BOOST_THROW_EXCEPTION(baseName->createTypeError("Contract expected.")); + BOOST_THROW_EXCEPTION(baseName.createTypeError("Contract expected.")); // "push_front" has the effect that bases mentioned later can overwrite members of bases // mentioned earlier input.back().push_front(base); - vector<ContractDefinition const*> const& basesBases = base->linearizedBaseContracts(); + vector<ContractDefinition const*> const& basesBases = base->annotation().linearizedBaseContracts; if (basesBases.empty()) - BOOST_THROW_EXCEPTION(baseName->createTypeError("Definition of base has to precede definition of derived contract")); + BOOST_THROW_EXCEPTION(baseName.createTypeError("Definition of base has to precede definition of derived contract")); input.push_front(list<ContractDefinition const*>(basesBases.begin(), basesBases.end())); } input.back().push_front(&_contract); vector<ContractDefinition const*> result = cThreeMerge(input); if (result.empty()) BOOST_THROW_EXCEPTION(_contract.createTypeError("Linearization of inheritance graph impossible")); - _contract.setLinearizedBaseContracts(result); + _contract.annotation().linearizedBaseContracts = result; } template <class _T> @@ -404,139 +400,5 @@ void DeclarationRegistrationHelper::registerDeclaration(Declaration& _declaratio enterNewSubScope(_declaration); } -ReferencesResolver::ReferencesResolver( - ASTNode& _root, - NameAndTypeResolver& _resolver, - ContractDefinition const* _currentContract, - ParameterList const* _returnParameters, - bool _resolveInsideCode, - bool _allowLazyTypes -): - m_resolver(_resolver), - m_currentContract(_currentContract), - m_returnParameters(_returnParameters), - m_resolveInsideCode(_resolveInsideCode), - m_allowLazyTypes(_allowLazyTypes) -{ - _root.accept(*this); -} - -void ReferencesResolver::endVisit(VariableDeclaration& _variable) -{ - // endVisit because the internal type needs resolving if it is a user defined type - // or mapping - if (_variable.typeName()) - { - TypePointer type = _variable.typeName()->toType(); - using Location = VariableDeclaration::Location; - Location loc = _variable.referenceLocation(); - // References are forced to calldata for external function parameters (not return) - // and memory for parameters (also return) of publicly visible functions. - // They default to memory for function parameters and storage for local variables. - if (auto ref = dynamic_cast<ReferenceType const*>(type.get())) - { - if (_variable.isExternalCallableParameter()) - { - // force location of external function parameters (not return) to calldata - if (loc != Location::Default) - BOOST_THROW_EXCEPTION(_variable.createTypeError( - "Location has to be calldata for external functions " - "(remove the \"memory\" or \"storage\" keyword)." - )); - type = ref->copyForLocation(DataLocation::CallData, true); - } - else if (_variable.isCallableParameter() && _variable.scope()->isPublic()) - { - // force locations of public or external function (return) parameters to memory - if (loc == VariableDeclaration::Location::Storage) - BOOST_THROW_EXCEPTION(_variable.createTypeError( - "Location has to be memory for publicly visible functions " - "(remove the \"storage\" keyword)." - )); - type = ref->copyForLocation(DataLocation::Memory, true); - } - else - { - if (_variable.isConstant()) - { - if (loc != Location::Default && loc != Location::Memory) - BOOST_THROW_EXCEPTION(_variable.createTypeError( - "Storage location has to be \"memory\" (or unspecified) for constants." - )); - loc = Location::Memory; - } - if (loc == Location::Default) - loc = _variable.isCallableParameter() ? Location::Memory : Location::Storage; - bool isPointer = !_variable.isStateVariable(); - type = ref->copyForLocation( - loc == Location::Memory ? - DataLocation::Memory : - DataLocation::Storage, - isPointer - ); - } - } - else if (loc != Location::Default && !ref) - BOOST_THROW_EXCEPTION(_variable.createTypeError( - "Storage location can only be given for array or struct types." - )); - - _variable.setType(type); - - if (!_variable.type()) - BOOST_THROW_EXCEPTION(_variable.typeName()->createTypeError("Invalid type name")); - } - else if (!m_allowLazyTypes) - BOOST_THROW_EXCEPTION(_variable.createTypeError("Explicit type needed.")); - // otherwise we have a "var"-declaration whose type is resolved by the first assignment -} - -bool ReferencesResolver::visit(Return& _return) -{ - _return.setFunctionReturnParameters(m_returnParameters); - return true; -} - -bool ReferencesResolver::visit(Mapping&) -{ - return true; -} - -bool ReferencesResolver::visit(UserDefinedTypeName& _typeName) -{ - auto declarations = m_resolver.nameFromCurrentScope(_typeName.name()); - if (declarations.empty()) - BOOST_THROW_EXCEPTION( - DeclarationError() << - errinfo_sourceLocation(_typeName.location()) << - errinfo_comment("Undeclared identifier.") - ); - else if (declarations.size() > 1) - BOOST_THROW_EXCEPTION( - DeclarationError() << - errinfo_sourceLocation(_typeName.location()) << - errinfo_comment("Duplicate identifier.") - ); - else - _typeName.setReferencedDeclaration(**declarations.begin()); - return false; -} - -bool ReferencesResolver::visit(Identifier& _identifier) -{ - auto declarations = m_resolver.nameFromCurrentScope(_identifier.name()); - if (declarations.empty()) - BOOST_THROW_EXCEPTION( - DeclarationError() << - errinfo_sourceLocation(_identifier.location()) << - errinfo_comment("Undeclared identifier.") - ); - else if (declarations.size() == 1) - _identifier.setReferencedDeclaration(*declarations.front(), m_currentContract); - else - _identifier.setOverloadedDeclarations(m_resolver.cleanedDeclarations(_identifier, declarations)); - return false; -} - } } diff --git a/libsolidity/NameAndTypeResolver.h b/libsolidity/NameAndTypeResolver.h index 101787ea..d28671ed 100644 --- a/libsolidity/NameAndTypeResolver.h +++ b/libsolidity/NameAndTypeResolver.h @@ -25,9 +25,10 @@ #include <map> #include <list> #include <boost/noncopyable.hpp> - #include <libsolidity/DeclarationContainer.h> +#include <libsolidity/ReferencesResolver.h> #include <libsolidity/ASTVisitor.h> +#include <libsolidity/ASTAnnotations.h> namespace dev { @@ -42,13 +43,11 @@ namespace solidity class NameAndTypeResolver: private boost::noncopyable { public: - explicit NameAndTypeResolver(std::vector<Declaration const*> const& _globals); + NameAndTypeResolver(std::vector<Declaration const*> const& _globals); /// Registers all declarations found in the source unit. void registerDeclarations(SourceUnit& _sourceUnit); /// Resolves all names and types referenced from the given contract. void resolveNamesAndTypes(ContractDefinition& _contract); - /// Check all type requirements in the given contract. - void checkTypeRequirements(ContractDefinition& _contract); /// Updates the given global declaration (used for "this"). Not to be used with declarations /// that create their own scope. void updateDeclaration(Declaration const& _declaration); @@ -125,36 +124,5 @@ private: VariableScope* m_currentFunction; }; -/** - * Resolves references to declarations (of variables and types) and also establishes the link - * between a return statement and the return parameter list. - */ -class ReferencesResolver: private ASTVisitor -{ -public: - ReferencesResolver( - ASTNode& _root, - NameAndTypeResolver& _resolver, - ContractDefinition const* _currentContract, - ParameterList const* _returnParameters, - bool _resolveInsideCode = false, - bool _allowLazyTypes = true - ); - -private: - virtual void endVisit(VariableDeclaration& _variable) override; - virtual bool visit(Block&) override { return m_resolveInsideCode; } - virtual bool visit(Identifier& _identifier) override; - virtual bool visit(UserDefinedTypeName& _typeName) override; - virtual bool visit(Mapping&) override; - virtual bool visit(Return& _return) override; - - NameAndTypeResolver& m_resolver; - ContractDefinition const* m_currentContract; - ParameterList const* m_returnParameters; - bool m_resolveInsideCode; - bool m_allowLazyTypes; -}; - } } diff --git a/libsolidity/ReferencesResolver.cpp b/libsolidity/ReferencesResolver.cpp new file mode 100644 index 00000000..623ac8f7 --- /dev/null +++ b/libsolidity/ReferencesResolver.cpp @@ -0,0 +1,225 @@ +/* + This file is part of cpp-ethereum. + + cpp-ethereum is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + cpp-ethereum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>. +*/ +/** + * @author Christian <c@ethdev.com> + * @date 2015 + * Component that resolves type names to types and annotates the AST accordingly. + */ + +#include <libsolidity/ReferencesResolver.h> +#include <libsolidity/AST.h> +#include <libsolidity/NameAndTypeResolver.h> +#include <libsolidity/Exceptions.h> +#include <libsolidity/ConstantEvaluator.h> + +using namespace std; +using namespace dev; +using namespace dev::solidity; + + +ReferencesResolver::ReferencesResolver( + ASTNode& _root, + NameAndTypeResolver& _resolver, + ContractDefinition const* _currentContract, + ParameterList const* _returnParameters, + bool _resolveInsideCode +): + m_resolver(_resolver), + m_currentContract(_currentContract), + m_returnParameters(_returnParameters), + m_resolveInsideCode(_resolveInsideCode) +{ + _root.accept(*this); +} + +bool ReferencesResolver::visit(Return const& _return) +{ + _return.annotation().functionReturnParameters = m_returnParameters; + return true; +} + +bool ReferencesResolver::visit(UserDefinedTypeName const& _typeName) +{ + auto declarations = m_resolver.nameFromCurrentScope(_typeName.name()); + if (declarations.empty()) + BOOST_THROW_EXCEPTION( + DeclarationError() << + errinfo_sourceLocation(_typeName.location()) << + errinfo_comment("Undeclared identifier.") + ); + else if (declarations.size() > 1) + BOOST_THROW_EXCEPTION( + DeclarationError() << + errinfo_sourceLocation(_typeName.location()) << + errinfo_comment("Duplicate identifier.") + ); + Declaration const* declaration = *declarations.begin(); + _typeName.annotation().referencedDeclaration = declaration; + return true; +} + +bool ReferencesResolver::visit(Identifier const& _identifier) +{ + auto declarations = m_resolver.nameFromCurrentScope(_identifier.name()); + if (declarations.empty()) + BOOST_THROW_EXCEPTION( + DeclarationError() << + errinfo_sourceLocation(_identifier.location()) << + errinfo_comment("Undeclared identifier.") + ); + else if (declarations.size() == 1) + { + _identifier.annotation().referencedDeclaration = declarations.front(); + _identifier.annotation().contractScope = m_currentContract; + } + else + _identifier.annotation().overloadedDeclarations = + m_resolver.cleanedDeclarations(_identifier, declarations); + return false; +} + +void ReferencesResolver::endVisit(VariableDeclaration const& _variable) +{ + if (_variable.annotation().type) + return; + + TypePointer type; + if (_variable.typeName()) + { + type = typeFor(*_variable.typeName()); + using Location = VariableDeclaration::Location; + Location loc = _variable.referenceLocation(); + // References are forced to calldata for external function parameters (not return) + // and memory for parameters (also return) of publicly visible functions. + // They default to memory for function parameters and storage for local variables. + if (auto ref = dynamic_cast<ReferenceType const*>(type.get())) + { + if (_variable.isExternalCallableParameter()) + { + // force location of external function parameters (not return) to calldata + if (loc != Location::Default) + BOOST_THROW_EXCEPTION(_variable.createTypeError( + "Location has to be calldata for external functions " + "(remove the \"memory\" or \"storage\" keyword)." + )); + type = ref->copyForLocation(DataLocation::CallData, true); + } + else if (_variable.isCallableParameter() && _variable.scope()->isPublic()) + { + // force locations of public or external function (return) parameters to memory + if (loc == VariableDeclaration::Location::Storage) + BOOST_THROW_EXCEPTION(_variable.createTypeError( + "Location has to be memory for publicly visible functions " + "(remove the \"storage\" keyword)." + )); + type = ref->copyForLocation(DataLocation::Memory, true); + } + else + { + if (_variable.isConstant()) + { + if (loc != Location::Default && loc != Location::Memory) + BOOST_THROW_EXCEPTION(_variable.createTypeError( + "Storage location has to be \"memory\" (or unspecified) for constants." + )); + loc = Location::Memory; + } + if (loc == Location::Default) + loc = _variable.isCallableParameter() ? Location::Memory : Location::Storage; + bool isPointer = !_variable.isStateVariable(); + type = ref->copyForLocation( + loc == Location::Memory ? + DataLocation::Memory : + DataLocation::Storage, + isPointer + ); + } + } + else if (loc != Location::Default && !ref) + BOOST_THROW_EXCEPTION(_variable.createTypeError( + "Storage location can only be given for array or struct types." + )); + + if (!type) + BOOST_THROW_EXCEPTION(_variable.typeName()->createTypeError("Invalid type name.")); + + } + else if (!_variable.canHaveAutoType()) + BOOST_THROW_EXCEPTION(_variable.createTypeError("Explicit type needed.")); + // otherwise we have a "var"-declaration whose type is resolved by the first assignment + + _variable.annotation().type = type; +} + +TypePointer ReferencesResolver::typeFor(TypeName const& _typeName) +{ + if (_typeName.annotation().type) + return _typeName.annotation().type; + + TypePointer type; + if (auto elemTypeName = dynamic_cast<ElementaryTypeName const*>(&_typeName)) + type = Type::fromElementaryTypeName(elemTypeName->typeName()); + else if (auto typeName = dynamic_cast<UserDefinedTypeName const*>(&_typeName)) + { + Declaration const* declaration = typeName->annotation().referencedDeclaration; + solAssert(!!declaration, ""); + + if (StructDefinition const* structDef = dynamic_cast<StructDefinition const*>(declaration)) + type = make_shared<StructType>(*structDef); + else if (EnumDefinition const* enumDef = dynamic_cast<EnumDefinition const*>(declaration)) + type = make_shared<EnumType>(*enumDef); + else if (ContractDefinition const* contract = dynamic_cast<ContractDefinition const*>(declaration)) + type = make_shared<ContractType>(*contract); + else + BOOST_THROW_EXCEPTION(typeName->createTypeError( + "Name has to refer to a struct, enum or contract." + )); + } + else if (auto mapping = dynamic_cast<Mapping const*>(&_typeName)) + { + TypePointer keyType = typeFor(mapping->keyType()); + TypePointer valueType = typeFor(mapping->valueType()); + // Convert key type to memory. + keyType = ReferenceType::copyForLocationIfReference(DataLocation::Memory, keyType); + // Convert value type to storage reference. + valueType = ReferenceType::copyForLocationIfReference(DataLocation::Storage, valueType); + type = make_shared<MappingType>(keyType, valueType); + } + else if (auto arrayType = dynamic_cast<ArrayTypeName const*>(&_typeName)) + { + TypePointer baseType = typeFor(arrayType->baseType()); + if (baseType->storageBytes() == 0) + BOOST_THROW_EXCEPTION(arrayType->baseType().createTypeError( + "Illegal base type of storage size zero for array." + )); + if (Expression const* length = arrayType->length()) + { + if (!length->annotation().type) + ConstantEvaluator e(*length); + + auto const* lengthType = dynamic_cast<IntegerConstantType const*>(length->annotation().type.get()); + if (!lengthType) + BOOST_THROW_EXCEPTION(length->createTypeError("Invalid array length.")); + type = make_shared<ArrayType>(DataLocation::Storage, baseType, lengthType->literalValue(nullptr)); + } + else + type = make_shared<ArrayType>(DataLocation::Storage, baseType); + } + + return _typeName.annotation().type = move(type); +} + diff --git a/libsolidity/ReferencesResolver.h b/libsolidity/ReferencesResolver.h new file mode 100644 index 00000000..b8a55dc2 --- /dev/null +++ b/libsolidity/ReferencesResolver.h @@ -0,0 +1,69 @@ +/* + This file is part of cpp-ethereum. + + cpp-ethereum is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + cpp-ethereum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>. +*/ +/** + * @author Christian <c@ethdev.com> + * @date 2014 + * Component that resolves type names to types and annotates the AST accordingly. + */ + +#pragma once + +#include <map> +#include <list> +#include <boost/noncopyable.hpp> +#include <libsolidity/ASTVisitor.h> +#include <libsolidity/ASTAnnotations.h> + +namespace dev +{ +namespace solidity +{ + +class NameAndTypeResolver; + +/** + * Resolves references to declarations (of variables and types) and also establishes the link + * between a return statement and the return parameter list. + */ +class ReferencesResolver: private ASTConstVisitor +{ +public: + ReferencesResolver( + ASTNode& _root, + NameAndTypeResolver& _resolver, + ContractDefinition const* _currentContract, + ParameterList const* _returnParameters, + bool _resolveInsideCode = false + ); + +private: + virtual bool visit(Block const&) override { return m_resolveInsideCode; } + virtual bool visit(Identifier const& _identifier) override; + virtual bool visit(UserDefinedTypeName const& _typeName) override; + virtual bool visit(Return const& _return) override; + virtual void endVisit(VariableDeclaration const& _variable) override; + + TypePointer typeFor(TypeName const& _typeName); + + NameAndTypeResolver& m_resolver; + ContractDefinition const* m_currentContract; + ParameterList const* m_returnParameters; + bool m_resolveInsideCode; +}; + +} +} diff --git a/libsolidity/TypeChecker.cpp b/libsolidity/TypeChecker.cpp new file mode 100644 index 00000000..f453e2fa --- /dev/null +++ b/libsolidity/TypeChecker.cpp @@ -0,0 +1,1143 @@ +/* + This file is part of cpp-ethereum. + + cpp-ethereum is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + cpp-ethereum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>. +*/ +/** + * @author Christian <c@ethdev.com> + * @date 2015 + * Type analyzer and checker. + */ + +#include <libsolidity/TypeChecker.h> +#include <memory> +#include <boost/range/adaptor/reversed.hpp> +#include <libsolidity/AST.h> + +using namespace std; +using namespace dev; +using namespace dev::solidity; + + +bool TypeChecker::checkTypeRequirements(const ContractDefinition& _contract) +{ + try + { + visit(_contract); + } + catch (FatalError const&) + { + // We got a fatal error which required to stop further type checking, but we can + // continue normally from here. + if (m_errors.empty()) + throw; // Something is weird here, rather throw again. + } + + return m_errors.empty(); +} + +TypePointer const& TypeChecker::type(Expression const& _expression) const +{ + solAssert(!!_expression.annotation().type, "Type requested but not present."); + return _expression.annotation().type; +} + +TypePointer const& TypeChecker::type(VariableDeclaration const& _variable) const +{ + solAssert(!!_variable.annotation().type, "Type requested but not present."); + return _variable.annotation().type; +} + +bool TypeChecker::visit(ContractDefinition const& _contract) +{ + // We force our own visiting order here. + ASTNode::listAccept(_contract.definedStructs(), *this); + ASTNode::listAccept(_contract.baseContracts(), *this); + + checkContractDuplicateFunctions(_contract); + checkContractIllegalOverrides(_contract); + checkContractAbstractFunctions(_contract); + checkContractAbstractConstructors(_contract); + + FunctionDefinition const* function = _contract.constructor(); + if (function && !function->returnParameters().empty()) + typeError(*function->returnParameterList(), "Non-empty \"returns\" directive for constructor."); + + FunctionDefinition const* fallbackFunction = nullptr; + for (ASTPointer<FunctionDefinition> const& function: _contract.definedFunctions()) + { + if (function->name().empty()) + { + if (fallbackFunction) + { + auto err = make_shared<DeclarationError>(); + *err << errinfo_comment("Only one fallback function is allowed."); + m_errors.push_back(err); + } + else + { + fallbackFunction = function.get(); + if (!fallbackFunction->parameters().empty()) + typeError(fallbackFunction->parameterList(), "Fallback function cannot take parameters."); + } + } + if (!function->isImplemented()) + _contract.annotation().isFullyImplemented = false; + } + + ASTNode::listAccept(_contract.stateVariables(), *this); + ASTNode::listAccept(_contract.events(), *this); + ASTNode::listAccept(_contract.functionModifiers(), *this); + ASTNode::listAccept(_contract.definedFunctions(), *this); + + checkContractExternalTypeClashes(_contract); + // check for hash collisions in function signatures + set<FixedHash<4>> hashes; + for (auto const& it: _contract.interfaceFunctionList()) + { + FixedHash<4> const& hash = it.first; + if (hashes.count(hash)) + typeError( + _contract, + string("Function signature hash collision for ") + it.second->externalSignature() + ); + hashes.insert(hash); + } + + if (_contract.isLibrary()) + checkLibraryRequirements(_contract); + + return false; +} + +void TypeChecker::checkContractDuplicateFunctions(ContractDefinition const& _contract) +{ + /// Checks that two functions with the same name defined in this contract have different + /// argument types and that there is at most one constructor. + map<string, vector<FunctionDefinition const*>> functions; + for (ASTPointer<FunctionDefinition> const& function: _contract.definedFunctions()) + functions[function->name()].push_back(function.get()); + + // Constructor + if (functions[_contract.name()].size() > 1) + { + SecondarySourceLocation ssl; + auto it = ++functions[_contract.name()].begin(); + for (; it != functions[_contract.name()].end(); ++it) + ssl.append("Another declaration is here:", (*it)->location()); + + auto err = make_shared<DeclarationError>(); + *err << + errinfo_sourceLocation(functions[_contract.name()].front()->location()) << + errinfo_comment("More than one constructor defined.") << + errinfo_secondarySourceLocation(ssl); + m_errors.push_back(err); + } + for (auto const& it: functions) + { + vector<FunctionDefinition const*> const& overloads = it.second; + for (size_t i = 0; i < overloads.size(); ++i) + for (size_t j = i + 1; j < overloads.size(); ++j) + if (FunctionType(*overloads[i]).hasEqualArgumentTypes(FunctionType(*overloads[j]))) + { + auto err = make_shared<DeclarationError>(); + *err << + errinfo_sourceLocation(overloads[j]->location()) << + errinfo_comment("Function with same name and arguments defined twice.") << + errinfo_secondarySourceLocation(SecondarySourceLocation().append( + "Other declaration is here:", overloads[i]->location())); + m_errors.push_back(err); + } + } +} + +void TypeChecker::checkContractAbstractFunctions(ContractDefinition const& _contract) +{ + // Mapping from name to function definition (exactly one per argument type equality class) and + // flag to indicate whether it is fully implemented. + using FunTypeAndFlag = std::pair<FunctionTypePointer, bool>; + map<string, vector<FunTypeAndFlag>> functions; + + // Search from base to derived + for (ContractDefinition const* contract: boost::adaptors::reverse(_contract.annotation().linearizedBaseContracts)) + for (ASTPointer<FunctionDefinition> const& function: contract->definedFunctions()) + { + auto& overloads = functions[function->name()]; + FunctionTypePointer funType = make_shared<FunctionType>(*function); + auto it = find_if(overloads.begin(), overloads.end(), [&](FunTypeAndFlag const& _funAndFlag) + { + return funType->hasEqualArgumentTypes(*_funAndFlag.first); + }); + if (it == overloads.end()) + overloads.push_back(make_pair(funType, function->isImplemented())); + else if (it->second) + { + if (!function->isImplemented()) + typeError(*function, "Redeclaring an already implemented function as abstract"); + } + else if (function->isImplemented()) + it->second = true; + } + + // Set to not fully implemented if at least one flag is false. + for (auto const& it: functions) + for (auto const& funAndFlag: it.second) + if (!funAndFlag.second) + { + _contract.annotation().isFullyImplemented = false; + return; + } +} + +void TypeChecker::checkContractAbstractConstructors(ContractDefinition const& _contract) +{ + set<ContractDefinition const*> argumentsNeeded; + // check that we get arguments for all base constructors that need it. + // If not mark the contract as abstract (not fully implemented) + + vector<ContractDefinition const*> const& bases = _contract.annotation().linearizedBaseContracts; + for (ContractDefinition const* contract: bases) + if (FunctionDefinition const* constructor = contract->constructor()) + if (contract != &_contract && !constructor->parameters().empty()) + argumentsNeeded.insert(contract); + + for (ContractDefinition const* contract: bases) + { + if (FunctionDefinition const* constructor = contract->constructor()) + for (auto const& modifier: constructor->modifiers()) + { + auto baseContract = dynamic_cast<ContractDefinition const*>( + &dereference(*modifier->name()) + ); + if (baseContract) + argumentsNeeded.erase(baseContract); + } + + + for (ASTPointer<InheritanceSpecifier> const& base: contract->baseContracts()) + { + auto baseContract = dynamic_cast<ContractDefinition const*>(&dereference(base->name())); + solAssert(baseContract, ""); + if (!base->arguments().empty()) + argumentsNeeded.erase(baseContract); + } + } + if (!argumentsNeeded.empty()) + _contract.annotation().isFullyImplemented = false; +} + +void TypeChecker::checkContractIllegalOverrides(ContractDefinition const& _contract) +{ + // TODO unify this at a later point. for this we need to put the constness and the access specifier + // into the types + map<string, vector<FunctionDefinition const*>> functions; + map<string, ModifierDefinition const*> modifiers; + + // We search from derived to base, so the stored item causes the error. + for (ContractDefinition const* contract: _contract.annotation().linearizedBaseContracts) + { + for (ASTPointer<FunctionDefinition> const& function: contract->definedFunctions()) + { + if (function->isConstructor()) + continue; // constructors can neither be overridden nor override anything + string const& name = function->name(); + if (modifiers.count(name)) + typeError(*modifiers[name], "Override changes function to modifier."); + FunctionType functionType(*function); + // function should not change the return type + for (FunctionDefinition const* overriding: functions[name]) + { + FunctionType overridingType(*overriding); + if (!overridingType.hasEqualArgumentTypes(functionType)) + continue; + if ( + overriding->visibility() != function->visibility() || + overriding->isDeclaredConst() != function->isDeclaredConst() || + overridingType != functionType + ) + typeError(*overriding, "Override changes extended function signature."); + } + functions[name].push_back(function.get()); + } + for (ASTPointer<ModifierDefinition> const& modifier: contract->functionModifiers()) + { + string const& name = modifier->name(); + ModifierDefinition const*& override = modifiers[name]; + if (!override) + override = modifier.get(); + else if (ModifierType(*override) != ModifierType(*modifier)) + typeError(*override, "Override changes modifier signature."); + if (!functions[name].empty()) + typeError(*override, "Override changes modifier to function."); + } + } +} + +void TypeChecker::checkContractExternalTypeClashes(ContractDefinition const& _contract) +{ + map<string, vector<pair<Declaration const*, FunctionTypePointer>>> externalDeclarations; + for (ContractDefinition const* contract: _contract.annotation().linearizedBaseContracts) + { + for (ASTPointer<FunctionDefinition> const& f: contract->definedFunctions()) + if (f->isPartOfExternalInterface()) + { + auto functionType = make_shared<FunctionType>(*f); + externalDeclarations[functionType->externalSignature(f->name())].push_back( + make_pair(f.get(), functionType) + ); + } + for (ASTPointer<VariableDeclaration> const& v: contract->stateVariables()) + if (v->isPartOfExternalInterface()) + { + auto functionType = make_shared<FunctionType>(*v); + externalDeclarations[functionType->externalSignature(v->name())].push_back( + make_pair(v.get(), functionType) + ); + } + } + for (auto const& it: externalDeclarations) + for (size_t i = 0; i < it.second.size(); ++i) + for (size_t j = i + 1; j < it.second.size(); ++j) + if (!it.second[i].second->hasEqualArgumentTypes(*it.second[j].second)) + typeError( + *it.second[j].first, + "Function overload clash during conversion to external types for arguments." + ); +} + +void TypeChecker::checkLibraryRequirements(ContractDefinition const& _contract) +{ + solAssert(_contract.isLibrary(), ""); + if (!_contract.baseContracts().empty()) + typeError(_contract, "Library is not allowed to inherit."); + + for (auto const& var: _contract.stateVariables()) + if (!var->isConstant()) + typeError(*var, "Library cannot have non-constant state variables"); +} + +void TypeChecker::endVisit(InheritanceSpecifier const& _inheritance) +{ + auto base = dynamic_cast<ContractDefinition const*>(&dereference(_inheritance.name())); + solAssert(base, "Base contract not available."); + + if (base->isLibrary()) + typeError(_inheritance, "Libraries cannot be inherited from."); + + auto const& arguments = _inheritance.arguments(); + TypePointers parameterTypes = ContractType(*base).constructorType()->parameterTypes(); + if (!arguments.empty() && parameterTypes.size() != arguments.size()) + typeError( + _inheritance, + "Wrong argument count for constructor call: " + + toString(arguments.size()) + + " arguments given but expected " + + toString(parameterTypes.size()) + + "." + ); + + for (size_t i = 0; i < arguments.size(); ++i) + if (!type(*arguments[i])->isImplicitlyConvertibleTo(*parameterTypes[i])) + typeError( + *arguments[i], + "Invalid type for argument in constructor call. " + "Invalid implicit conversion from " + + type(*arguments[i])->toString() + + " to " + + parameterTypes[i]->toString() + + " requested." + ); +} + +bool TypeChecker::visit(StructDefinition const& _struct) +{ + for (ASTPointer<VariableDeclaration> const& member: _struct.members()) + if (!type(*member)->canBeStored()) + typeError(*member, "Type cannot be used in struct."); + + // Check recursion, fatal error if detected. + using StructPointer = StructDefinition const*; + using StructPointersSet = set<StructPointer>; + function<void(StructPointer,StructPointersSet const&)> check = [&](StructPointer _struct, StructPointersSet const& _parents) + { + if (_parents.count(_struct)) + fatalTypeError(*_struct, "Recursive struct definition."); + StructPointersSet parents = _parents; + parents.insert(_struct); + for (ASTPointer<VariableDeclaration> const& member: _struct->members()) + if (type(*member)->category() == Type::Category::Struct) + { + auto const& typeName = dynamic_cast<UserDefinedTypeName const&>(*member->typeName()); + check(&dynamic_cast<StructDefinition const&>(*typeName.annotation().referencedDeclaration), parents); + } + }; + check(&_struct, StructPointersSet{}); + + ASTNode::listAccept(_struct.members(), *this); + + return false; +} + +bool TypeChecker::visit(FunctionDefinition const& _function) +{ + for (ASTPointer<VariableDeclaration> const& var: _function.parameters() + _function.returnParameters()) + { + if (!type(*var)->canLiveOutsideStorage()) + typeError(*var, "Type is required to live outside storage."); + if (_function.visibility() >= FunctionDefinition::Visibility::Public && !(type(*var)->externalType())) + typeError(*var, "Internal type is not allowed for public and external functions."); + } + for (ASTPointer<ModifierInvocation> const& modifier: _function.modifiers()) + visitManually( + *modifier, + _function.isConstructor() ? + dynamic_cast<ContractDefinition const&>(*_function.scope()).annotation().linearizedBaseContracts : + vector<ContractDefinition const*>() + ); + if (_function.isImplemented()) + _function.body().accept(*this); + return false; +} + +bool TypeChecker::visit(VariableDeclaration const& _variable) +{ + // Variables can be declared without type (with "var"), in which case the first assignment + // sets the type. + // Note that assignments before the first declaration are legal because of the special scoping + // rules inherited from JavaScript. + + // This only infers the type from its type name. + // If an explicit type is required, it throws, otherwise it returns TypePointer(); + TypePointer varType = _variable.annotation().type; + if (_variable.isConstant()) + { + if (!dynamic_cast<ContractDefinition const*>(_variable.scope())) + typeError(_variable, "Illegal use of \"constant\" specifier."); + if (!_variable.value()) + typeError(_variable, "Uninitialized \"constant\" variable."); + if (varType && !varType->isValueType()) + { + bool constImplemented = false; + if (auto arrayType = dynamic_cast<ArrayType const*>(varType.get())) + constImplemented = arrayType->isByteArray(); + if (!constImplemented) + typeError( + _variable, + "Illegal use of \"constant\" specifier. \"constant\" " + "is not yet implemented for this type." + ); + } + } + if (varType) + { + if (_variable.value()) + expectType(*_variable.value(), *varType); + } + else + { + // Infer type from value. + if (!_variable.value()) + fatalTypeError(_variable, "Assignment necessary for type detection."); + _variable.value()->accept(*this); + + TypePointer const& valueType = type(*_variable.value()); + solAssert(!!valueType, ""); + if ( + valueType->category() == Type::Category::IntegerConstant && + !dynamic_pointer_cast<IntegerConstantType const>(valueType)->integerType() + ) + fatalTypeError(*_variable.value(), "Invalid integer constant " + valueType->toString() + "."); + else if (valueType->category() == Type::Category::Void) + fatalTypeError(_variable, "Variable cannot have void type."); + varType = valueType->mobileType(); + } + solAssert(!!varType, ""); + _variable.annotation().type = varType; + if (!_variable.isStateVariable()) + { + if (varType->dataStoredIn(DataLocation::Memory) || varType->dataStoredIn(DataLocation::CallData)) + if (!varType->canLiveOutsideStorage()) + typeError(_variable, "Type " + varType->toString() + " is only valid in storage."); + } + else if ( + _variable.visibility() >= VariableDeclaration::Visibility::Public && + !FunctionType(_variable).externalType() + ) + typeError(_variable, "Internal type is not allowed for public state variables."); + return false; +} + +void TypeChecker::visitManually( + ModifierInvocation const& _modifier, + vector<ContractDefinition const*> const& _bases +) +{ + std::vector<ASTPointer<Expression>> const& arguments = _modifier.arguments(); + for (ASTPointer<Expression> const& argument: arguments) + argument->accept(*this); + _modifier.name()->accept(*this); + + auto const* declaration = &dereference(*_modifier.name()); + vector<ASTPointer<VariableDeclaration>> emptyParameterList; + vector<ASTPointer<VariableDeclaration>> const* parameters = nullptr; + if (auto modifierDecl = dynamic_cast<ModifierDefinition const*>(declaration)) + parameters = &modifierDecl->parameters(); + else + // check parameters for Base constructors + for (ContractDefinition const* base: _bases) + if (declaration == base) + { + if (auto referencedConstructor = base->constructor()) + parameters = &referencedConstructor->parameters(); + else + parameters = &emptyParameterList; + break; + } + if (!parameters) + typeError(_modifier, "Referenced declaration is neither modifier nor base class."); + if (parameters->size() != arguments.size()) + typeError( + _modifier, + "Wrong argument count for modifier invocation: " + + toString(arguments.size()) + + " arguments given but expected " + + toString(parameters->size()) + + "." + ); + for (size_t i = 0; i < _modifier.arguments().size(); ++i) + if (!type(*arguments[i])->isImplicitlyConvertibleTo(*type(*(*parameters)[i]))) + typeError( + *arguments[i], + "Invalid type for argument in modifier invocation. " + "Invalid implicit conversion from " + + type(*arguments[i])->toString() + + " to " + + type(*(*parameters)[i])->toString() + + " requested." + ); +} + +bool TypeChecker::visit(EventDefinition const& _eventDef) +{ + unsigned numIndexed = 0; + for (ASTPointer<VariableDeclaration> const& var: _eventDef.parameters()) + { + if (var->isIndexed()) + numIndexed++; + if (numIndexed > 3) + typeError(_eventDef, "More than 3 indexed arguments for event."); + if (!type(*var)->canLiveOutsideStorage()) + typeError(*var, "Type is required to live outside storage."); + if (!type(*var)->externalType()) + typeError(*var, "Internal type is not allowed as event parameter type."); + } + return false; +} + + +bool TypeChecker::visit(IfStatement const& _ifStatement) +{ + expectType(_ifStatement.condition(), BoolType()); + _ifStatement.trueStatement().accept(*this); + if (_ifStatement.falseStatement()) + _ifStatement.falseStatement()->accept(*this); + return false; +} + +bool TypeChecker::visit(WhileStatement const& _whileStatement) +{ + expectType(_whileStatement.condition(), BoolType()); + _whileStatement.body().accept(*this); + return false; +} + +bool TypeChecker::visit(ForStatement const& _forStatement) +{ + if (_forStatement.initializationExpression()) + _forStatement.initializationExpression()->accept(*this); + if (_forStatement.condition()) + expectType(*_forStatement.condition(), BoolType()); + if (_forStatement.loopExpression()) + _forStatement.loopExpression()->accept(*this); + _forStatement.body().accept(*this); + return false; +} + +void TypeChecker::endVisit(Return const& _return) +{ + if (!_return.expression()) + return; + ParameterList const* params = _return.annotation().functionReturnParameters; + if (!params) + typeError(_return, "Return arguments not allowed."); + else if (params->parameters().size() != 1) + typeError(_return, "Different number of arguments in return statement than in returns declaration."); + else + { + // this could later be changed such that the paramaters type is an anonymous struct type, + // but for now, we only allow one return parameter + TypePointer const& expected = type(*params->parameters().front()); + if (!type(*_return.expression())->isImplicitlyConvertibleTo(*expected)) + typeError( + *_return.expression(), + "Return argument type " + + type(*_return.expression())->toString() + + " is not implicitly convertible to expected type (type of first return variable) " + + expected->toString() + + "." + ); + } +} + +void TypeChecker::endVisit(ExpressionStatement const& _statement) +{ + if (type(_statement.expression())->category() == Type::Category::IntegerConstant) + if (!dynamic_pointer_cast<IntegerConstantType const>(type(_statement.expression()))->integerType()) + typeError(_statement.expression(), "Invalid integer constant."); +} + +bool TypeChecker::visit(Assignment const& _assignment) +{ + requireLValue(_assignment.leftHandSide()); + TypePointer t = type(_assignment.leftHandSide()); + _assignment.annotation().type = t; + if (t->category() == Type::Category::Mapping) + { + typeError(_assignment, "Mappings cannot be assigned to."); + _assignment.rightHandSide().accept(*this); + } + else if (_assignment.assignmentOperator() == Token::Assign) + expectType(_assignment.rightHandSide(), *t); + else + { + // compound assignment + _assignment.rightHandSide().accept(*this); + TypePointer resultType = t->binaryOperatorResult( + Token::AssignmentToBinaryOp(_assignment.assignmentOperator()), + type(_assignment.rightHandSide()) + ); + if (!resultType || *resultType != *t) + typeError( + _assignment, + "Operator " + + string(Token::toString(_assignment.assignmentOperator())) + + " not compatible with types " + + t->toString() + + " and " + + type(_assignment.rightHandSide())->toString() + ); + } + return false; +} + +bool TypeChecker::visit(UnaryOperation const& _operation) +{ + // Inc, Dec, Add, Sub, Not, BitNot, Delete + Token::Value op = _operation.getOperator(); + if (op == Token::Value::Inc || op == Token::Value::Dec || op == Token::Value::Delete) + requireLValue(_operation.subExpression()); + else + _operation.subExpression().accept(*this); + TypePointer const& subExprType = type(_operation.subExpression()); + TypePointer t = type(_operation.subExpression())->unaryOperatorResult(op); + if (!t) + { + typeError( + _operation, + "Unary operator " + + string(Token::toString(op)) + + " cannot be applied to type " + + subExprType->toString() + ); + t = subExprType; + } + _operation.annotation().type = t; + return false; +} + +void TypeChecker::endVisit(BinaryOperation const& _operation) +{ + TypePointer const& leftType = type(_operation.leftExpression()); + TypePointer const& rightType = type(_operation.rightExpression()); + TypePointer commonType = leftType->binaryOperatorResult(_operation.getOperator(), rightType); + if (!commonType) + { + typeError( + _operation, + "Operator " + + string(Token::toString(_operation.getOperator())) + + " not compatible with types " + + leftType->toString() + + " and " + + rightType->toString() + ); + commonType = leftType; + } + _operation.annotation().commonType = commonType; + _operation.annotation().type = + Token::isCompareOp(_operation.getOperator()) ? + make_shared<BoolType>() : + commonType; +} + +bool TypeChecker::visit(FunctionCall const& _functionCall) +{ + bool isPositionalCall = _functionCall.names().empty(); + vector<ASTPointer<Expression const>> arguments = _functionCall.arguments(); + vector<ASTPointer<ASTString>> const& argumentNames = _functionCall.names(); + + // We need to check arguments' type first as they will be needed for overload resolution. + shared_ptr<TypePointers> argumentTypes; + if (isPositionalCall) + argumentTypes = make_shared<TypePointers>(); + for (ASTPointer<Expression const> const& argument: arguments) + { + argument->accept(*this); + // only store them for positional calls + if (isPositionalCall) + argumentTypes->push_back(type(*argument)); + } + if (isPositionalCall) + _functionCall.expression().annotation().argumentTypes = move(argumentTypes); + + _functionCall.expression().accept(*this); + TypePointer expressionType = type(_functionCall.expression()); + + if (auto const* typeType = dynamic_cast<TypeType const*>(expressionType.get())) + { + _functionCall.annotation().isStructConstructorCall = (typeType->actualType()->category() == Type::Category::Struct); + _functionCall.annotation().isTypeConversion = !_functionCall.annotation().isStructConstructorCall; + } + else + _functionCall.annotation().isStructConstructorCall = _functionCall.annotation().isTypeConversion = false; + + if (_functionCall.annotation().isTypeConversion) + { + TypeType const& t = dynamic_cast<TypeType const&>(*expressionType); + TypePointer resultType = t.actualType(); + if (arguments.size() != 1) + typeError(_functionCall, "Exactly one argument expected for explicit type conversion."); + else if (!isPositionalCall) + typeError(_functionCall, "Type conversion cannot allow named arguments."); + else + { + TypePointer const& argType = type(*arguments.front()); + if (auto argRefType = dynamic_cast<ReferenceType const*>(argType.get())) + // do not change the data location when converting + // (data location cannot yet be specified for type conversions) + resultType = ReferenceType::copyForLocationIfReference(argRefType->location(), resultType); + if (!argType->isExplicitlyConvertibleTo(*resultType)) + typeError(_functionCall, "Explicit type conversion not allowed."); + } + _functionCall.annotation().type = resultType; + + return false; + } + + // Actual function call or struct constructor call. + + FunctionTypePointer functionType; + + /// For error message: Struct members that were removed during conversion to memory. + set<string> membersRemovedForStructConstructor; + if (_functionCall.annotation().isStructConstructorCall) + { + TypeType const& t = dynamic_cast<TypeType const&>(*expressionType); + auto const& structType = dynamic_cast<StructType const&>(*t.actualType()); + functionType = structType.constructorType(); + membersRemovedForStructConstructor = structType.membersMissingInMemory(); + } + else + functionType = dynamic_pointer_cast<FunctionType const>(expressionType); + + if (!functionType) + { + typeError(_functionCall, "Type is not callable"); + _functionCall.annotation().type = make_shared<VoidType>(); + return false; + } + else + { + // @todo actually the return type should be an anonymous struct, + // but we change it to the type of the first return value until we have anonymous + // structs and tuples + if (functionType->returnParameterTypes().empty()) + _functionCall.annotation().type = make_shared<VoidType>(); + else + _functionCall.annotation().type = functionType->returnParameterTypes().front(); + } + + //@todo would be nice to create a struct type from the arguments + // and then ask if that is implicitly convertible to the struct represented by the + // function parameters + TypePointers const& parameterTypes = functionType->parameterTypes(); + if (!functionType->takesArbitraryParameters() && parameterTypes.size() != arguments.size()) + { + string msg = + "Wrong argument count for function call: " + + toString(arguments.size()) + + " arguments given but expected " + + toString(parameterTypes.size()) + + "."; + // Extend error message in case we try to construct a struct with mapping member. + if (_functionCall.annotation().isStructConstructorCall && !membersRemovedForStructConstructor.empty()) + { + msg += " Members that have to be skipped in memory:"; + for (auto const& member: membersRemovedForStructConstructor) + msg += " " + member; + } + typeError(_functionCall, msg); + } + else if (isPositionalCall) + { + // call by positional arguments + for (size_t i = 0; i < arguments.size(); ++i) + if ( + !functionType->takesArbitraryParameters() && + !type(*arguments[i])->isImplicitlyConvertibleTo(*parameterTypes[i]) + ) + typeError( + *arguments[i], + "Invalid type for argument in function call. " + "Invalid implicit conversion from " + + type(*arguments[i])->toString() + + " to " + + parameterTypes[i]->toString() + + " requested." + ); + } + else + { + // call by named arguments + auto const& parameterNames = functionType->parameterNames(); + if (functionType->takesArbitraryParameters()) + typeError( + _functionCall, + "Named arguments cannnot be used for functions that take arbitrary parameters." + ); + else if (parameterNames.size() > argumentNames.size()) + typeError(_functionCall, "Some argument names are missing."); + else if (parameterNames.size() < argumentNames.size()) + typeError(_functionCall, "Too many arguments."); + else + { + // check duplicate names + bool duplication = false; + for (size_t i = 0; i < argumentNames.size(); i++) + for (size_t j = i + 1; j < argumentNames.size(); j++) + if (*argumentNames[i] == *argumentNames[j]) + { + duplication = true; + typeError(*arguments[i], "Duplicate named argument."); + } + + // check actual types + if (!duplication) + for (size_t i = 0; i < argumentNames.size(); i++) + { + bool found = false; + for (size_t j = 0; j < parameterNames.size(); j++) + if (parameterNames[j] == *argumentNames[i]) + { + found = true; + // check type convertible + if (!type(*arguments[i])->isImplicitlyConvertibleTo(*parameterTypes[j])) + typeError( + *arguments[i], + "Invalid type for argument in function call. " + "Invalid implicit conversion from " + + type(*arguments[i])->toString() + + " to " + + parameterTypes[i]->toString() + + " requested." + ); + break; + } + + if (!found) + typeError( + _functionCall, + "Named argument does not match function declaration." + ); + } + } + } + + return false; +} + +void TypeChecker::endVisit(NewExpression const& _newExpression) +{ + auto contract = dynamic_cast<ContractDefinition const*>(&dereference(_newExpression.contractName())); + + if (!contract) + fatalTypeError(_newExpression, "Identifier is not a contract."); + if (!contract->annotation().isFullyImplemented) + typeError(_newExpression, "Trying to create an instance of an abstract contract."); + + auto scopeContract = _newExpression.contractName().annotation().contractScope; + auto const& bases = contract->annotation().linearizedBaseContracts; + solAssert(!bases.empty(), "Linearized base contracts not yet available."); + if (find(bases.begin(), bases.end(), scopeContract) != bases.end()) + typeError( + _newExpression, + "Circular reference for contract creation: cannot create instance of derived or same contract." + ); + + auto contractType = make_shared<ContractType>(*contract); + TypePointers const& parameterTypes = contractType->constructorType()->parameterTypes(); + _newExpression.annotation().type = make_shared<FunctionType>( + parameterTypes, + TypePointers{contractType}, + strings(), + strings(), + FunctionType::Location::Creation + ); +} + +bool TypeChecker::visit(MemberAccess const& _memberAccess) +{ + _memberAccess.expression().accept(*this); + TypePointer exprType = type(_memberAccess.expression()); + ASTString const& memberName = _memberAccess.memberName(); + + // Retrieve the types of the arguments if this is used to call a function. + auto const& argumentTypes = _memberAccess.annotation().argumentTypes; + MemberList::MemberMap possibleMembers = exprType->members().membersByName(memberName); + if (possibleMembers.size() > 1 && argumentTypes) + { + // do overload resolution + for (auto it = possibleMembers.begin(); it != possibleMembers.end();) + if ( + it->type->category() == Type::Category::Function && + !dynamic_cast<FunctionType const&>(*it->type).canTakeArguments(*argumentTypes) + ) + it = possibleMembers.erase(it); + else + ++it; + } + if (possibleMembers.size() == 0) + { + auto storageType = ReferenceType::copyForLocationIfReference( + DataLocation::Storage, + exprType + ); + if (!storageType->members().membersByName(memberName).empty()) + fatalTypeError( + _memberAccess, + "Member \"" + memberName + "\" is not available in " + + exprType->toString() + + " outside of storage." + ); + fatalTypeError( + _memberAccess, + "Member \"" + memberName + "\" not found or not visible " + "after argument-dependent lookup in " + exprType->toString() + ); + } + else if (possibleMembers.size() > 1) + fatalTypeError( + _memberAccess, + "Member \"" + memberName + "\" not unique " + "after argument-dependent lookup in " + exprType->toString() + ); + + auto& annotation = _memberAccess.annotation(); + annotation.referencedDeclaration = possibleMembers.front().declaration; + annotation.type = possibleMembers.front().type; + if (exprType->category() == Type::Category::Struct) + annotation.isLValue = true; + else if (exprType->category() == Type::Category::Array) + { + auto const& arrayType(dynamic_cast<ArrayType const&>(*exprType)); + annotation.isLValue = ( + memberName == "length" && + arrayType.location() == DataLocation::Storage && + arrayType.isDynamicallySized() + ); + } + + return false; +} + +bool TypeChecker::visit(IndexAccess const& _access) +{ + _access.baseExpression().accept(*this); + TypePointer baseType = type(_access.baseExpression()); + TypePointer resultType; + bool isLValue = false; + Expression const* index = _access.indexExpression(); + switch (baseType->category()) + { + case Type::Category::Array: + { + ArrayType const& actualType = dynamic_cast<ArrayType const&>(*baseType); + if (!index) + typeError(_access, "Index expression cannot be omitted."); + else if (actualType.isString()) + { + typeError(_access, "Index access for string is not possible."); + index->accept(*this); + } + else + { + expectType(*index, IntegerType(256)); + if (auto integerType = dynamic_cast<IntegerConstantType const*>(type(*index).get())) + if (!actualType.isDynamicallySized() && actualType.length() <= integerType->literalValue(nullptr)) + typeError(_access, "Out of bounds array access."); + } + resultType = actualType.baseType(); + isLValue = actualType.location() != DataLocation::CallData; + break; + } + case Type::Category::Mapping: + { + MappingType const& actualType = dynamic_cast<MappingType const&>(*baseType); + if (!index) + typeError(_access, "Index expression cannot be omitted."); + else + expectType(*index, *actualType.keyType()); + resultType = actualType.valueType(); + isLValue = true; + break; + } + case Type::Category::TypeType: + { + TypeType const& typeType = dynamic_cast<TypeType const&>(*baseType); + if (!index) + resultType = make_shared<TypeType>(make_shared<ArrayType>(DataLocation::Memory, typeType.actualType())); + else + { + index->accept(*this); + if (auto length = dynamic_cast<IntegerConstantType const*>(type(*index).get())) + resultType = make_shared<TypeType>(make_shared<ArrayType>( + DataLocation::Memory, + typeType.actualType(), + length->literalValue(nullptr) + )); + else + typeError(*index, "Integer constant expected."); + } + break; + } + default: + fatalTypeError( + _access.baseExpression(), + "Indexed expression has to be a type, mapping or array (is " + baseType->toString() + ")" + ); + } + _access.annotation().type = move(resultType); + _access.annotation().isLValue = isLValue; + + return false; +} + +bool TypeChecker::visit(Identifier const& _identifier) +{ + IdentifierAnnotation& annotation = _identifier.annotation(); + if (!annotation.referencedDeclaration) + { + if (!annotation.argumentTypes) + fatalTypeError(_identifier, "Unable to determine overloaded type."); + if (annotation.overloadedDeclarations.empty()) + fatalTypeError(_identifier, "No candidates for overload resolution found."); + else if (annotation.overloadedDeclarations.size() == 1) + annotation.referencedDeclaration = *annotation.overloadedDeclarations.begin(); + else + { + vector<Declaration const*> candidates; + + for (Declaration const* declaration: annotation.overloadedDeclarations) + { + TypePointer function = declaration->type(_identifier.annotation().contractScope); + solAssert(!!function, "Requested type not present."); + auto const* functionType = dynamic_cast<FunctionType const*>(function.get()); + if (functionType && functionType->canTakeArguments(*annotation.argumentTypes)) + candidates.push_back(declaration); + } + if (candidates.empty()) + fatalTypeError(_identifier, "No matching declaration found after argument-dependent lookup."); + else if (candidates.size() == 1) + annotation.referencedDeclaration = candidates.front(); + else + fatalTypeError(_identifier, "No unique declaration found after argument-dependent lookup."); + } + } + solAssert( + !!annotation.referencedDeclaration, + "Referenced declaration is null after overload resolution." + ); + annotation.isLValue = annotation.referencedDeclaration->isLValue(); + annotation.type = annotation.referencedDeclaration->type(_identifier.annotation().contractScope); + if (!annotation.type) + fatalTypeError(_identifier, "Declaration referenced before type could be determined."); + return false; +} + +void TypeChecker::endVisit(ElementaryTypeNameExpression const& _expr) +{ + _expr.annotation().type = make_shared<TypeType>(Type::fromElementaryTypeName(_expr.typeToken())); +} + +void TypeChecker::endVisit(Literal const& _literal) +{ + _literal.annotation().type = Type::forLiteral(_literal); + if (!_literal.annotation().type) + fatalTypeError(_literal, "Invalid literal value."); +} + +Declaration const& TypeChecker::dereference(Identifier const& _identifier) +{ + solAssert(!!_identifier.annotation().referencedDeclaration, "Declaration not stored."); + return *_identifier.annotation().referencedDeclaration; +} + +void TypeChecker::expectType(Expression const& _expression, Type const& _expectedType) +{ + _expression.accept(*this); + + if (!type(_expression)->isImplicitlyConvertibleTo(_expectedType)) + typeError( + _expression, + "Type " + + type(_expression)->toString() + + " is not implicitly convertible to expected type " + + _expectedType.toString() + + "." + ); +} + +void TypeChecker::requireLValue(Expression const& _expression) +{ + _expression.accept(*this); + if (!_expression.annotation().isLValue) + typeError(_expression, "Expression has to be an lvalue."); + _expression.annotation().lValueRequested = true; +} + +void TypeChecker::typeError(ASTNode const& _node, string const& _description) +{ + auto err = make_shared<TypeError>(); + *err << + errinfo_sourceLocation(_node.location()) << + errinfo_comment(_description); + + m_errors.push_back(err); +} + +void TypeChecker::fatalTypeError(ASTNode const& _node, string const& _description) +{ + typeError(_node, _description); + BOOST_THROW_EXCEPTION(FatalError()); +} diff --git a/libsolidity/TypeChecker.h b/libsolidity/TypeChecker.h new file mode 100644 index 00000000..cc539e22 --- /dev/null +++ b/libsolidity/TypeChecker.h @@ -0,0 +1,114 @@ +/* + This file is part of cpp-ethereum. + + cpp-ethereum is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + cpp-ethereum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>. +*/ +/** + * @author Christian <c@ethdev.com> + * @date 2015 + * Type analyzer and checker. + */ + +#pragma once + +#include <libsolidity/TypeChecker.h> +#include <libsolidity/Types.h> +#include <libsolidity/ASTAnnotations.h> +#include <libsolidity/ASTForward.h> +#include <libsolidity/ASTVisitor.h> + +namespace dev +{ +namespace solidity +{ + + +/** + * The module that performs type analysis on the AST, checks the applicability of operations on + * those types and stores errors for invalid operations. + * Provides a way to retrieve the type of an AST node. + */ +class TypeChecker: private ASTConstVisitor +{ +public: + /// Performs type checking on the given contract and all of its sub-nodes. + /// @returns true iff all checks passed. + bool checkTypeRequirements(ContractDefinition const& _contract); + + /// @returns the list of errors found during type checking. + std::vector<std::shared_ptr<Error const>> const& errors() const { return m_errors; } + + /// @returns the type of an expression and asserts that it is present. + TypePointer const& type(Expression const& _expression) const; + /// @returns the type of the given variable and throws if the type is not present + /// (this can happen for variables with non-explicit types before their types are resolved) + TypePointer const& type(VariableDeclaration const& _variable) const; + + /// Adds a new error to the list of errors. + void typeError(ASTNode const& _node, std::string const& _description); + /// Adds a new error to the list of errors and throws to abort type checking. + void fatalTypeError(ASTNode const& _node, std::string const& _description); + +private: + virtual bool visit(ContractDefinition const& _contract) override; + /// Checks that two functions defined in this contract with the same name have different + /// arguments and that there is at most one constructor. + void checkContractDuplicateFunctions(ContractDefinition const& _contract); + void checkContractIllegalOverrides(ContractDefinition const& _contract); + void checkContractAbstractFunctions(ContractDefinition const& _contract); + void checkContractAbstractConstructors(ContractDefinition const& _contract); + /// Checks that different functions with external visibility end up having different + /// external argument types (i.e. different signature). + void checkContractExternalTypeClashes(ContractDefinition const& _contract); + /// Checks that all requirements for a library are fulfilled if this is a library. + void checkLibraryRequirements(ContractDefinition const& _contract); + + virtual void endVisit(InheritanceSpecifier const& _inheritance) override; + virtual bool visit(StructDefinition const& _struct) override; + virtual bool visit(FunctionDefinition const& _function) override; + virtual bool visit(VariableDeclaration const& _variable) override; + /// We need to do this manually because we want to pass the bases of the current contract in + /// case this is a base constructor call. + void visitManually(ModifierInvocation const& _modifier, std::vector<ContractDefinition const*> const& _bases); + virtual bool visit(EventDefinition const& _eventDef) override; + virtual bool visit(IfStatement const& _ifStatement) override; + virtual bool visit(WhileStatement const& _whileStatement) override; + virtual bool visit(ForStatement const& _forStatement) override; + virtual void endVisit(Return const& _return) override; + virtual void endVisit(ExpressionStatement const& _statement) override; + virtual bool visit(Assignment const& _assignment) override; + virtual void endVisit(BinaryOperation const& _operation) override; + virtual bool visit(UnaryOperation const& _operation) override; + virtual bool visit(FunctionCall const& _functionCall) override; + virtual void endVisit(NewExpression const& _newExpression) override; + virtual bool visit(MemberAccess const& _memberAccess) override; + virtual bool visit(IndexAccess const& _indexAccess) override; + virtual bool visit(Identifier const& _identifier) override; + virtual void endVisit(ElementaryTypeNameExpression const& _expr) override; + virtual void endVisit(Literal const& _literal) override; + + /// @returns the referenced declaration and throws on error. + Declaration const& dereference(Identifier const& _identifier); + + /// Runs type checks on @a _expression to infer its type and then checks that it is implicitly + /// convertible to @a _expectedType. + void expectType(Expression const& _expression, Type const& _expectedType); + /// Runs type checks on @a _expression to infer its type and then checks that it is an LValue. + void requireLValue(Expression const& _expression); + + std::vector<std::shared_ptr<Error const>> m_errors; +}; + +} +} diff --git a/libsolidity/Types.cpp b/libsolidity/Types.cpp index beb5becd..68e8e91d 100644 --- a/libsolidity/Types.cpp +++ b/libsolidity/Types.cpp @@ -157,55 +157,6 @@ TypePointer Type::fromElementaryTypeName(string const& _name) return fromElementaryTypeName(Token::fromIdentifierOrKeyword(_name)); } -TypePointer Type::fromUserDefinedTypeName(UserDefinedTypeName const& _typeName) -{ - Declaration const* declaration = _typeName.referencedDeclaration(); - if (StructDefinition const* structDef = dynamic_cast<StructDefinition const*>(declaration)) - return make_shared<StructType>(*structDef); - else if (EnumDefinition const* enumDef = dynamic_cast<EnumDefinition const*>(declaration)) - return make_shared<EnumType>(*enumDef); - else if (FunctionDefinition const* function = dynamic_cast<FunctionDefinition const*>(declaration)) - return make_shared<FunctionType>(*function); - else if (ContractDefinition const* contract = dynamic_cast<ContractDefinition const*>(declaration)) - return make_shared<ContractType>(*contract); - return TypePointer(); -} - -TypePointer Type::fromMapping(ElementaryTypeName& _keyType, TypeName& _valueType) -{ - TypePointer keyType = _keyType.toType(); - if (!keyType) - BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Error resolving type name.")); - TypePointer valueType = _valueType.toType(); - if (!valueType) - BOOST_THROW_EXCEPTION(_valueType.createTypeError("Invalid type name.")); - // Convert value type to storage reference. - valueType = ReferenceType::copyForLocationIfReference(DataLocation::Storage, valueType); - // Convert key type to memory. - keyType = ReferenceType::copyForLocationIfReference(DataLocation::Memory, keyType); - return make_shared<MappingType>(keyType, valueType); -} - -TypePointer Type::fromArrayTypeName(TypeName& _baseTypeName, Expression* _length) -{ - TypePointer baseType = _baseTypeName.toType(); - if (!baseType) - BOOST_THROW_EXCEPTION(_baseTypeName.createTypeError("Invalid type name.")); - if (baseType->storageBytes() == 0) - BOOST_THROW_EXCEPTION(_baseTypeName.createTypeError("Illegal base type of storage size zero for array.")); - if (_length) - { - if (!_length->type()) - _length->checkTypeRequirements(nullptr); - auto const* length = dynamic_cast<IntegerConstantType const*>(_length->type().get()); - if (!length) - BOOST_THROW_EXCEPTION(_length->createTypeError("Invalid array length.")); - return make_shared<ArrayType>(DataLocation::Storage, baseType, length->literalValue(nullptr)); - } - else - return make_shared<ArrayType>(DataLocation::Storage, baseType); -} - TypePointer Type::forLiteral(Literal const& _literal) { switch (_literal.token()) @@ -686,7 +637,7 @@ bool ContractType::isImplicitlyConvertibleTo(Type const& _convertTo) const return dynamic_cast<IntegerType const&>(_convertTo).isAddress(); if (_convertTo.category() == Category::Contract) { - auto const& bases = contractDefinition().linearizedBaseContracts(); + auto const& bases = contractDefinition().annotation().linearizedBaseContracts; if (m_super && bases.size() <= 1) return false; return find(m_super ? ++bases.begin() : bases.begin(), bases.end(), @@ -944,7 +895,7 @@ MemberList const& ContractType::members() const if (m_super) { // add the most derived of all functions which are visible in derived contracts - for (ContractDefinition const* base: m_contract.linearizedBaseContracts()) + for (ContractDefinition const* base: m_contract.annotation().linearizedBaseContracts) for (ASTPointer<FunctionDefinition> const& function: base->definedFunctions()) { if (!function->isVisibleInDerivedContracts()) @@ -998,13 +949,13 @@ shared_ptr<FunctionType const> const& ContractType::constructorType() const vector<tuple<VariableDeclaration const*, u256, unsigned>> ContractType::stateVariables() const { vector<VariableDeclaration const*> variables; - for (ContractDefinition const* contract: boost::adaptors::reverse(m_contract.linearizedBaseContracts())) + for (ContractDefinition const* contract: boost::adaptors::reverse(m_contract.annotation().linearizedBaseContracts)) for (ASTPointer<VariableDeclaration> const& variable: contract->stateVariables()) if (!variable->isConstant()) variables.push_back(variable.get()); TypePointers types; for (auto variable: variables) - types.push_back(variable->type()); + types.push_back(variable->annotation().type); StorageOffsets offsets; offsets.computeOffsets(types); @@ -1082,7 +1033,7 @@ MemberList const& StructType::members() const MemberList::MemberMap members; for (ASTPointer<VariableDeclaration> const& variable: m_struct.members()) { - TypePointer type = variable->type(); + TypePointer type = variable->annotation().type; // Skip all mapping members if we are not in storage. if (location() != DataLocation::Storage && !type->canLiveOutsideStorage()) continue; @@ -1147,7 +1098,7 @@ set<string> StructType::membersMissingInMemory() const { set<string> missing; for (ASTPointer<VariableDeclaration> const& variable: m_struct.members()) - if (!variable->type()->canLiveOutsideStorage()) + if (!variable->annotation().type->canLiveOutsideStorage()) missing.insert(variable->name()); return missing; } @@ -1211,14 +1162,14 @@ FunctionType::FunctionType(FunctionDefinition const& _function, bool _isInternal for (ASTPointer<VariableDeclaration> const& var: _function.parameters()) { paramNames.push_back(var->name()); - params.push_back(var->type()); + params.push_back(var->annotation().type); } retParams.reserve(_function.returnParameters().size()); retParamNames.reserve(_function.returnParameters().size()); for (ASTPointer<VariableDeclaration> const& var: _function.returnParameters()) { retParamNames.push_back(var->name()); - retParams.push_back(var->type()); + retParams.push_back(var->annotation().type); } swap(params, m_parameterTypes); swap(paramNames, m_parameterNames); @@ -1231,7 +1182,7 @@ FunctionType::FunctionType(VariableDeclaration const& _varDecl): { TypePointers paramTypes; vector<string> paramNames; - auto returnType = _varDecl.type(); + auto returnType = _varDecl.annotation().type; while (true) { @@ -1293,7 +1244,7 @@ FunctionType::FunctionType(const EventDefinition& _event): for (ASTPointer<VariableDeclaration> const& var: _event.parameters()) { paramNames.push_back(var->name()); - params.push_back(var->type()); + params.push_back(var->annotation().type); } swap(params, m_parameterTypes); swap(paramNames, m_parameterNames); @@ -1662,7 +1613,7 @@ MemberList const& TypeType::members() const )); else if (m_currentContract != nullptr) { - vector<ContractDefinition const*> currentBases = m_currentContract->linearizedBaseContracts(); + auto const& currentBases = m_currentContract->annotation().linearizedBaseContracts; if (find(currentBases.begin(), currentBases.end(), &contract) != currentBases.end()) // We are accessing the type of a base contract, so add all public and protected // members. Note that this does not add inherited functions on purpose. @@ -1687,7 +1638,7 @@ ModifierType::ModifierType(const ModifierDefinition& _modifier) TypePointers params; params.reserve(_modifier.parameters().size()); for (ASTPointer<VariableDeclaration> const& var: _modifier.parameters()) - params.push_back(var->type()); + params.push_back(var->annotation().type); swap(params, m_parameterTypes); } diff --git a/solc/CommandLineInterface.cpp b/solc/CommandLineInterface.cpp index cfc8a5e0..587dcded 100644 --- a/solc/CommandLineInterface.cpp +++ b/solc/CommandLineInterface.cpp @@ -490,7 +490,12 @@ bool CommandLineInterface::processInput() // TODO: Perhaps we should not compile unless requested bool optimize = m_args.count("optimize") > 0; unsigned runs = m_args["optimize-runs"].as<unsigned>(); - m_compiler->compile(optimize, runs); + if (!m_compiler->compile(optimize, runs)) + { + for (auto const& error: m_compiler->errors()) + SourceReferenceFormatter::printExceptionInformation(cerr, *error, "Error", *m_compiler); + return false; + } m_compiler->link(m_libraries); } catch (ParserError const& _exception) diff --git a/solc/jsonCompiler.cpp b/solc/jsonCompiler.cpp index d265ed9c..ba3e6912 100644 --- a/solc/jsonCompiler.cpp +++ b/solc/jsonCompiler.cpp @@ -46,10 +46,7 @@ string formatError(Exception const& _exception, string const& _name, CompilerSta { ostringstream errorOutput; SourceReferenceFormatter::printExceptionInformation(errorOutput, _exception, _name, _compiler); - - Json::Value output(Json::objectValue); - output["error"] = errorOutput.str(); - return Json::FastWriter().write(output); + return errorOutput.str(); } Json::Value functionHashes(ContractDefinition const& _contract) @@ -123,43 +120,52 @@ string compile(string _input, bool _optimize) sources[""] = _input; Json::Value output(Json::objectValue); + Json::Value errors(Json::arrayValue); CompilerStack compiler; try { - compiler.compile(_input, _optimize); + if (!compiler.compile(_input, _optimize)) + { + for (auto const& error: compiler.errors()) + errors.append(formatError(*error, "Error", compiler)); + } } catch (ParserError const& exception) { - return formatError(exception, "Parser error", compiler); + errors.append(formatError(exception, "Parser error", compiler)); } catch (DeclarationError const& exception) { - return formatError(exception, "Declaration error", compiler); + errors.append(formatError(exception, "Declaration error", compiler)); } catch (TypeError const& exception) { - return formatError(exception, "Type error", compiler); + errors.append(formatError(exception, "Type error", compiler)); } catch (CompilerError const& exception) { - return formatError(exception, "Compiler error", compiler); + errors.append(formatError(exception, "Compiler error", compiler)); } catch (InternalCompilerError const& exception) { - return formatError(exception, "Internal compiler error", compiler); + errors.append(formatError(exception, "Internal compiler error", compiler)); } catch (DocstringParsingError const& exception) { - return formatError(exception, "Documentation parsing error", compiler); + errors.append(formatError(exception, "Documentation parsing error", compiler)); } catch (Exception const& exception) { - output["error"] = "Exception during compilation: " + boost::diagnostic_information(exception); - return Json::FastWriter().write(output); + errors.append("Exception during compilation: " + boost::diagnostic_information(exception)); } catch (...) { - output["error"] = "Unknown exception during compilation."; + errors.append("Unknown exception during compilation."); + } + + if (errors.size() > 0) + { + output["errors"] = errors; return Json::FastWriter().write(output); } diff --git a/test/libsolidity/Assembly.cpp b/test/libsolidity/Assembly.cpp index 5bd6fed3..b4678611 100644 --- a/test/libsolidity/Assembly.cpp +++ b/test/libsolidity/Assembly.cpp @@ -31,6 +31,7 @@ #include <libsolidity/NameAndTypeResolver.h> #include <libsolidity/Compiler.h> #include <libsolidity/AST.h> +#include <libsolidity/TypeChecker.h> using namespace std; using namespace dev::eth; @@ -60,7 +61,8 @@ eth::AssemblyItems compileContract(const string& _sourceCode) for (ASTPointer<ASTNode> const& node: sourceUnit->nodes()) if (ContractDefinition* contract = dynamic_cast<ContractDefinition*>(node.get())) { - BOOST_REQUIRE_NO_THROW(resolver.checkTypeRequirements(*contract)); + TypeChecker checker; + BOOST_REQUIRE_NO_THROW(checker.checkTypeRequirements(*contract)); } for (ASTPointer<ASTNode> const& node: sourceUnit->nodes()) if (ContractDefinition* contract = dynamic_cast<ContractDefinition*>(node.get())) diff --git a/test/libsolidity/SolidityEndToEndTest.cpp b/test/libsolidity/SolidityEndToEndTest.cpp index 98da1e27..8026f216 100644 --- a/test/libsolidity/SolidityEndToEndTest.cpp +++ b/test/libsolidity/SolidityEndToEndTest.cpp @@ -1999,7 +1999,7 @@ BOOST_AUTO_TEST_CASE(single_copy_with_multiple_inheritance) BOOST_CHECK(callContractFunction("getViaB()") == encodeArgs(23)); } -BOOST_AUTO_TEST_CASE(explicit_base_cass) +BOOST_AUTO_TEST_CASE(explicit_base_class) { char const* sourceCode = R"( contract BaseBase { function g() returns (uint r) { return 1; } } diff --git a/test/libsolidity/SolidityExpressionCompiler.cpp b/test/libsolidity/SolidityExpressionCompiler.cpp index 747668b0..545775ee 100644 --- a/test/libsolidity/SolidityExpressionCompiler.cpp +++ b/test/libsolidity/SolidityExpressionCompiler.cpp @@ -29,6 +29,7 @@ #include <libsolidity/CompilerContext.h> #include <libsolidity/ExpressionCompiler.h> #include <libsolidity/AST.h> +#include <libsolidity/TypeChecker.h> #include "../TestHelper.h" using namespace std; @@ -117,7 +118,8 @@ bytes compileFirstExpression(const string& _sourceCode, vector<vector<string>> _ for (ASTPointer<ASTNode> const& node: sourceUnit->nodes()) if (ContractDefinition* contract = dynamic_cast<ContractDefinition*>(node.get())) { - ETH_TEST_REQUIRE_NO_THROW(resolver.checkTypeRequirements(*contract), "Checking type Requirements failed"); + TypeChecker typeChecker; + BOOST_REQUIRE(typeChecker.checkTypeRequirements(*contract)); } for (ASTPointer<ASTNode> const& node: sourceUnit->nodes()) if (ContractDefinition* contract = dynamic_cast<ContractDefinition*>(node.get())) diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index 2a720494..8346b8ca 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -29,6 +29,7 @@ #include <libsolidity/NameAndTypeResolver.h> #include <libsolidity/Exceptions.h> #include <libsolidity/GlobalContext.h> +#include <libsolidity/TypeChecker.h> #include "../TestHelper.h" using namespace std; @@ -43,33 +44,69 @@ namespace test namespace { -ASTPointer<SourceUnit> parseTextAndResolveNames(std::string const& _source) +pair<ASTPointer<SourceUnit>, shared_ptr<Exception const>> +parseAnalyseAndReturnError(string const& _source) { Parser parser; - ASTPointer<SourceUnit> sourceUnit = parser.parse(std::make_shared<Scanner>(CharStream(_source))); - NameAndTypeResolver resolver({}); - resolver.registerDeclarations(*sourceUnit); - std::shared_ptr<GlobalContext> globalContext = make_shared<GlobalContext>(); - - for (ASTPointer<ASTNode> const& node: sourceUnit->nodes()) - if (ContractDefinition* contract = dynamic_cast<ContractDefinition*>(node.get())) - { - globalContext->setCurrentContract(*contract); - resolver.updateDeclaration(*globalContext->currentThis()); - resolver.updateDeclaration(*globalContext->currentSuper()); - resolver.resolveNamesAndTypes(*contract); - } - for (ASTPointer<ASTNode> const& node: sourceUnit->nodes()) - if (ContractDefinition* contract = dynamic_cast<ContractDefinition*>(node.get())) - { - globalContext->setCurrentContract(*contract); - resolver.updateDeclaration(*globalContext->currentThis()); - resolver.checkTypeRequirements(*contract); - } + ASTPointer<SourceUnit> sourceUnit; + shared_ptr<Exception const> err; + // catch exceptions for a transition period + try + { + sourceUnit = parser.parse(std::make_shared<Scanner>(CharStream(_source))); + NameAndTypeResolver resolver({}); + resolver.registerDeclarations(*sourceUnit); + std::shared_ptr<GlobalContext> globalContext = make_shared<GlobalContext>(); + + for (ASTPointer<ASTNode> const& node: sourceUnit->nodes()) + if (ContractDefinition* contract = dynamic_cast<ContractDefinition*>(node.get())) + { + globalContext->setCurrentContract(*contract); + resolver.updateDeclaration(*globalContext->currentThis()); + resolver.updateDeclaration(*globalContext->currentSuper()); + resolver.resolveNamesAndTypes(*contract); + } + for (ASTPointer<ASTNode> const& node: sourceUnit->nodes()) + if (ContractDefinition* contract = dynamic_cast<ContractDefinition*>(node.get())) + { + globalContext->setCurrentContract(*contract); + resolver.updateDeclaration(*globalContext->currentThis()); + TypeChecker typeChecker; + if (!typeChecker.checkTypeRequirements(*contract)) + { + err = typeChecker.errors().front(); + break; + } + } + } + catch (ParserError const& _exception) + { + return make_pair(sourceUnit, make_shared<ParserError>(_exception)); + } + catch (DeclarationError const& _exception) + { + return make_pair(sourceUnit, make_shared<DeclarationError>(_exception)); + } + catch (TypeError const& _exception) + { + return make_pair(sourceUnit, make_shared<TypeError>(_exception)); + } + return make_pair(sourceUnit, err); +} - return sourceUnit; +ASTPointer<SourceUnit> parseAndAnalyse(string const& _source) +{ + auto sourceAndError = parseAnalyseAndReturnError(_source); + BOOST_REQUIRE(!sourceAndError.second); + return sourceAndError.first; } +shared_ptr<Exception const> parseAndAnalyseReturnError(std::string const& _source) +{ + auto sourceAndError = parseAnalyseAndReturnError(_source); + BOOST_REQUIRE(!!sourceAndError.second); + return sourceAndError.second; +} static ContractDefinition const* retrieveContract(ASTPointer<SourceUnit> _source, unsigned index) { @@ -79,7 +116,7 @@ static ContractDefinition const* retrieveContract(ASTPointer<SourceUnit> _source if ((contract = dynamic_cast<ContractDefinition*>(node.get())) && counter == index) return contract; - return NULL; + return nullptr; } static FunctionTypePointer const& retrieveFunctionBySignature(ContractDefinition const* _contract, @@ -91,6 +128,10 @@ static FunctionTypePointer const& retrieveFunctionBySignature(ContractDefinition } +#define SOLIDITY_CHECK_ERROR_TYPE(_statement, _ErrorType) \ + BOOST_CHECK(!!dynamic_cast<_ErrorType const*>(_statement.get())) + + BOOST_AUTO_TEST_SUITE(SolidityNameAndTypeResolution) BOOST_AUTO_TEST_CASE(smoke_test) @@ -99,7 +140,7 @@ BOOST_AUTO_TEST_CASE(smoke_test) " uint256 stateVariable1;\n" " function fun(uint256 arg1) { uint256 y; }" "}\n"; - ETH_TEST_CHECK_NO_THROW(parseTextAndResolveNames(text), "Parsing and Name Resolving Failed"); + ETH_TEST_CHECK_NO_THROW(parseAndAnalyse(text), "Parsing and Name Resolving Failed"); } BOOST_AUTO_TEST_CASE(double_stateVariable_declaration) @@ -108,7 +149,7 @@ BOOST_AUTO_TEST_CASE(double_stateVariable_declaration) " uint256 variable;\n" " uint128 variable;\n" "}\n"; - BOOST_CHECK_THROW(parseTextAndResolveNames(text), DeclarationError); + SOLIDITY_CHECK_ERROR_TYPE(parseAndAnalyseReturnError(text), DeclarationError); } BOOST_AUTO_TEST_CASE(double_function_declaration) @@ -117,7 +158,7 @@ BOOST_AUTO_TEST_CASE(double_function_declaration) " function fun() { var x; }\n" " function fun() { var x; }\n" "}\n"; - BOOST_CHECK_THROW(parseTextAndResolveNames(text), DeclarationError); + SOLIDITY_CHECK_ERROR_TYPE(parseAndAnalyseReturnError(text), DeclarationError); } BOOST_AUTO_TEST_CASE(double_variable_declaration) @@ -125,7 +166,7 @@ BOOST_AUTO_TEST_CASE(double_variable_declaration) char const* text = "contract test {\n" " function f() { uint256 x; if (true) { uint256 x; } }\n" "}\n"; - BOOST_CHECK_THROW(parseTextAndResolveNames(text), DeclarationError); + SOLIDITY_CHECK_ERROR_TYPE(parseAndAnalyseReturnError(text), DeclarationError); } BOOST_AUTO_TEST_CASE(name_shadowing) @@ -134,7 +175,7 @@ BOOST_AUTO_TEST_CASE(name_shadowing) " uint256 variable;\n" " function f() { uint32 variable ; }" "}\n"; - ETH_TEST_CHECK_NO_THROW(parseTextAndResolveNames(text), "Parsing and Name Resolving Failed"); + ETH_TEST_CHECK_NO_THROW(parseAndAnalyse(text), "Parsing and Name Resolving Failed"); } BOOST_AUTO_TEST_CASE(name_references) @@ -143,7 +184,7 @@ BOOST_AUTO_TEST_CASE(name_references) " uint256 variable;\n" " function f(uint256 arg) returns (uint out) { f(variable); test; out; }" "}\n"; - ETH_TEST_CHECK_NO_THROW(parseTextAndResolveNames(text), "Parsing and Name Resolving Failed"); + ETH_TEST_CHECK_NO_THROW(parseAndAnalyse(text), "Parsing and Name Resolving Failed"); } BOOST_AUTO_TEST_CASE(undeclared_name) @@ -152,7 +193,7 @@ BOOST_AUTO_TEST_CASE(undeclared_name) " uint256 variable;\n" " function f(uint256 arg) { f(notfound); }" "}\n"; - BOOST_CHECK_THROW(parseTextAndResolveNames(text), DeclarationError); + SOLIDITY_CHECK_ERROR_TYPE(parseAndAnalyseReturnError(text), DeclarationError); } BOOST_AUTO_TEST_CASE(reference_to_later_declaration) @@ -161,7 +202,7 @@ BOOST_AUTO_TEST_CASE(reference_to_later_declaration) " function g() { f(); }" " function f() { }" "}\n"; - ETH_TEST_CHECK_NO_THROW(parseTextAndResolveNames(text), "Parsing and Name Resolving Failed"); + ETH_TEST_CHECK_NO_THROW(parseAndAnalyse(text), "Parsing and Name Resolving Failed"); } BOOST_AUTO_TEST_CASE(struct_definition_directly_recursive) @@ -172,7 +213,7 @@ BOOST_AUTO_TEST_CASE(struct_definition_directly_recursive) " MyStructName x;\n" " }\n" "}\n"; - BOOST_CHECK_THROW(parseTextAndResolveNames(text), ParserError); + SOLIDITY_CHECK_ERROR_TYPE(parseAndAnalyseReturnError(text), TypeError); } BOOST_AUTO_TEST_CASE(struct_definition_indirectly_recursive) @@ -187,7 +228,7 @@ BOOST_AUTO_TEST_CASE(struct_definition_indirectly_recursive) " MyStructName1 x;\n" " }\n" "}\n"; - BOOST_CHECK_THROW(parseTextAndResolveNames(text), ParserError); + SOLIDITY_CHECK_ERROR_TYPE(parseAndAnalyseReturnError(text), TypeError); } BOOST_AUTO_TEST_CASE(struct_definition_not_really_recursive) @@ -198,7 +239,7 @@ BOOST_AUTO_TEST_CASE(struct_definition_not_really_recursive) struct s2 { s1 x; s1 y; } } )"; - BOOST_CHECK_NO_THROW(parseTextAndResolveNames(text)); + BOOST_CHECK_NO_THROW(parseAndAnalyse(text)); } BOOST_AUTO_TEST_CASE(struct_definition_recursion_via_mapping) @@ -210,7 +251,7 @@ BOOST_AUTO_TEST_CASE(struct_definition_recursion_via_mapping) " mapping(uint => MyStructName1) x;\n" " }\n" "}\n"; - ETH_TEST_CHECK_NO_THROW(parseTextAndResolveNames(text), "Parsing and Name Resolving Failed"); + ETH_TEST_CHECK_NO_THROW(parseAndAnalyse(text), "Parsing and Name Resolving Failed"); } BOOST_AUTO_TEST_CASE(type_inference_smoke_test) @@ -218,7 +259,7 @@ BOOST_AUTO_TEST_CASE(type_inference_smoke_test) char const* text = "contract test {\n" " function f(uint256 arg1, uint32 arg2) returns (bool ret) { var x = arg1 + arg2 == 8; ret = x; }" "}\n"; - ETH_TEST_CHECK_NO_THROW(parseTextAndResolveNames(text), "Parsing and Name Resolving Failed"); + ETH_TEST_CHECK_NO_THROW(parseAndAnalyse(text), "Parsing and Name Resolving Failed"); } BOOST_AUTO_TEST_CASE(type_checking_return) @@ -226,7 +267,7 @@ BOOST_AUTO_TEST_CASE(type_checking_return) char const* text = "contract test {\n" " function f() returns (bool r) { return 1 >= 2; }" "}\n"; - ETH_TEST_CHECK_NO_THROW(parseTextAndResolveNames(text), "Parsing and Name Resolving Failed"); + ETH_TEST_CHECK_NO_THROW(parseAndAnalyse(text), "Parsing and Name Resolving Failed"); } BOOST_AUTO_TEST_CASE(type_checking_return_wrong_number) @@ -234,7 +275,7 @@ BOOST_AUTO_TEST_CASE(type_checking_return_wrong_number) char const* text = "contract test {\n" " function f() returns (bool r1, bool r2) { return 1 >= 2; }" "}\n"; - BOOST_CHECK_THROW(parseTextAndResolveNames(text), TypeError); + SOLIDITY_CHECK_ERROR_TYPE(parseAndAnalyseReturnError(text), TypeError); } BOOST_AUTO_TEST_CASE(type_checking_return_wrong_type) @@ -242,7 +283,7 @@ BOOST_AUTO_TEST_CASE(type_checking_return_wrong_type) char const* text = "contract test {\n" " function f() returns (uint256 r) { return 1 >= 2; }" "}\n"; - BOOST_CHECK_THROW(parseTextAndResolveNames(text), TypeError); + SOLIDITY_CHECK_ERROR_TYPE(parseAndAnalyseReturnError(text), TypeError); } BOOST_AUTO_TEST_CASE(type_checking_function_call) @@ -251,7 +292,7 @@ BOOST_AUTO_TEST_CASE(type_checking_function_call) " function f() returns (bool r) { return g(12, true) == 3; }\n" " function g(uint256 a, bool b) returns (uint256 r) { }\n" "}\n"; - ETH_TEST_CHECK_NO_THROW(parseTextAndResolveNames(text), "Parsing and Name Resolving Failed"); + ETH_TEST_CHECK_NO_THROW(parseAndAnalyse(text), "Parsing and Name Resolving Failed"); } BOOST_AUTO_TEST_CASE(type_conversion_for_comparison) @@ -259,7 +300,7 @@ BOOST_AUTO_TEST_CASE(type_conversion_for_comparison) char const* text = "contract test {\n" " function f() { uint32(2) == int64(2); }" "}\n"; - ETH_TEST_CHECK_NO_THROW(parseTextAndResolveNames(text), "Parsing and Name Resolving Failed"); + ETH_TEST_CHECK_NO_THROW(parseAndAnalyse(text), "Parsing and Name Resolving Failed"); } BOOST_AUTO_TEST_CASE(type_conversion_for_comparison_invalid) @@ -267,7 +308,7 @@ BOOST_AUTO_TEST_CASE(type_conversion_for_comparison_invalid) char const* text = "contract test {\n" " function f() { int32(2) == uint64(2); }" "}\n"; - BOOST_CHECK_THROW(parseTextAndResolveNames(text), TypeError); + SOLIDITY_CHECK_ERROR_TYPE(parseAndAnalyseReturnError(text), TypeError); } BOOST_AUTO_TEST_CASE(type_inference_explicit_conversion) @@ -275,7 +316,7 @@ BOOST_AUTO_TEST_CASE(type_inference_explicit_conversion) char const* text = "contract test {\n" " function f() returns (int256 r) { var x = int256(uint32(2)); return x; }" "}\n"; - ETH_TEST_CHECK_NO_THROW(parseTextAndResolveNames(text), "Parsing and Name Resolving Failed"); + ETH_TEST_CHECK_NO_THROW(parseAndAnalyse(text), "Parsing and Name Resolving Failed"); } BOOST_AUTO_TEST_CASE(large_string_literal) @@ -283,7 +324,7 @@ BOOST_AUTO_TEST_CASE(large_string_literal) char const* text = "contract test {\n" " function f() { var x = \"123456789012345678901234567890123\"; }" "}\n"; - BOOST_CHECK_NO_THROW(parseTextAndResolveNames(text)); + BOOST_CHECK_NO_THROW(parseAndAnalyse(text)); } BOOST_AUTO_TEST_CASE(balance) @@ -293,7 +334,7 @@ BOOST_AUTO_TEST_CASE(balance) " uint256 x = address(0).balance;\n" " }\n" "}\n"; - ETH_TEST_CHECK_NO_THROW(parseTextAndResolveNames(text), "Parsing and Name Resolving Failed"); + ETH_TEST_CHECK_NO_THROW(parseAndAnalyse(text), "Parsing and Name Resolving Failed"); } BOOST_AUTO_TEST_CASE(balance_invalid) @@ -303,7 +344,7 @@ BOOST_AUTO_TEST_CASE(balance_invalid) " address(0).balance = 7;\n" " }\n" "}\n"; - BOOST_CHECK_THROW(parseTextAndResolveNames(text), TypeError); + SOLIDITY_CHECK_ERROR_TYPE(parseAndAnalyseReturnError(text), TypeError); } BOOST_AUTO_TEST_CASE(assignment_to_mapping) @@ -318,7 +359,7 @@ BOOST_AUTO_TEST_CASE(assignment_to_mapping) " data.map = a;\n" " }\n" "}\n"; - BOOST_CHECK_THROW(parseTextAndResolveNames(text), TypeError); + SOLIDITY_CHECK_ERROR_TYPE(parseAndAnalyseReturnError(text), TypeError); } BOOST_AUTO_TEST_CASE(assignment_to_struct) @@ -333,7 +374,7 @@ BOOST_AUTO_TEST_CASE(assignment_to_struct) " data = a;\n" " }\n" "}\n"; - ETH_TEST_CHECK_NO_THROW(parseTextAndResolveNames(text), "Parsing and Name Resolving Failed"); + ETH_TEST_CHECK_NO_THROW(parseAndAnalyse(text), "Parsing and Name Resolving Failed"); } BOOST_AUTO_TEST_CASE(returns_in_constructor) @@ -342,7 +383,7 @@ BOOST_AUTO_TEST_CASE(returns_in_constructor) " function test() returns (uint a) {\n" " }\n" "}\n"; - BOOST_CHECK_THROW(parseTextAndResolveNames(text), TypeError); + SOLIDITY_CHECK_ERROR_TYPE(parseAndAnalyseReturnError(text), TypeError); } BOOST_AUTO_TEST_CASE(forward_function_reference) @@ -357,7 +398,7 @@ BOOST_AUTO_TEST_CASE(forward_function_reference) " if (First(2).fun() == true) return 1;\n" " }\n" "}\n"; - ETH_TEST_CHECK_NO_THROW(parseTextAndResolveNames(text), "Parsing and Name Resolving Failed"); + ETH_TEST_CHECK_NO_THROW(parseAndAnalyse(text), "Parsing and Name Resolving Failed"); } BOOST_AUTO_TEST_CASE(comparison_bitop_precedence) @@ -367,7 +408,7 @@ BOOST_AUTO_TEST_CASE(comparison_bitop_precedence) " return 1 & 2 == 8 & 9 && 1 ^ 2 < 4 | 6;\n" " }\n" "}\n"; - ETH_TEST_CHECK_NO_THROW(parseTextAndResolveNames(text), "Parsing and Name Resolving Failed"); + ETH_TEST_CHECK_NO_THROW(parseAndAnalyse(text), "Parsing and Name Resolving Failed"); } BOOST_AUTO_TEST_CASE(function_no_implementation) @@ -376,12 +417,12 @@ BOOST_AUTO_TEST_CASE(function_no_implementation) char const* text = "contract test {\n" " function functionName(bytes32 input) returns (bytes32 out);\n" "}\n"; - ETH_TEST_REQUIRE_NO_THROW(sourceUnit = parseTextAndResolveNames(text), "Parsing and name Resolving failed"); + ETH_TEST_REQUIRE_NO_THROW(sourceUnit = parseAndAnalyse(text), "Parsing and name Resolving failed"); std::vector<ASTPointer<ASTNode>> nodes = sourceUnit->nodes(); ContractDefinition* contract = dynamic_cast<ContractDefinition*>(nodes[0].get()); BOOST_CHECK(contract); - BOOST_CHECK(!contract->isFullyImplemented()); - BOOST_CHECK(!contract->definedFunctions()[0]->isFullyImplemented()); + BOOST_CHECK(!contract->annotation().isFullyImplemented); + BOOST_CHECK(!contract->definedFunctions()[0]->isImplemented()); } BOOST_AUTO_TEST_CASE(abstract_contract) @@ -391,16 +432,16 @@ BOOST_AUTO_TEST_CASE(abstract_contract) contract base { function foo(); } contract derived is base { function foo() {} } )"; - ETH_TEST_REQUIRE_NO_THROW(sourceUnit = parseTextAndResolveNames(text), "Parsing and name Resolving failed"); + ETH_TEST_REQUIRE_NO_THROW(sourceUnit = parseAndAnalyse(text), "Parsing and name Resolving failed"); std::vector<ASTPointer<ASTNode>> nodes = sourceUnit->nodes(); ContractDefinition* base = dynamic_cast<ContractDefinition*>(nodes[0].get()); ContractDefinition* derived = dynamic_cast<ContractDefinition*>(nodes[1].get()); BOOST_CHECK(base); - BOOST_CHECK(!base->isFullyImplemented()); - BOOST_CHECK(!base->definedFunctions()[0]->isFullyImplemented()); + BOOST_CHECK(!base->annotation().isFullyImplemented); + BOOST_CHECK(!base->definedFunctions()[0]->isImplemented()); BOOST_CHECK(derived); - BOOST_CHECK(derived->isFullyImplemented()); - BOOST_CHECK(derived->definedFunctions()[0]->isFullyImplemented()); + BOOST_CHECK(derived->annotation().isFullyImplemented); + BOOST_CHECK(derived->definedFunctions()[0]->isImplemented()); } BOOST_AUTO_TEST_CASE(abstract_contract_with_overload) @@ -410,14 +451,14 @@ BOOST_AUTO_TEST_CASE(abstract_contract_with_overload) contract base { function foo(bool); } contract derived is base { function foo(uint) {} } )"; - ETH_TEST_REQUIRE_NO_THROW(sourceUnit = parseTextAndResolveNames(text), "Parsing and name Resolving failed"); + ETH_TEST_REQUIRE_NO_THROW(sourceUnit = parseAndAnalyse(text), "Parsing and name Resolving failed"); std::vector<ASTPointer<ASTNode>> nodes = sourceUnit->nodes(); ContractDefinition* base = dynamic_cast<ContractDefinition*>(nodes[0].get()); ContractDefinition* derived = dynamic_cast<ContractDefinition*>(nodes[1].get()); BOOST_REQUIRE(base); - BOOST_CHECK(!base->isFullyImplemented()); + BOOST_CHECK(!base->annotation().isFullyImplemented); BOOST_REQUIRE(derived); - BOOST_CHECK(!derived->isFullyImplemented()); + BOOST_CHECK(!derived->annotation().isFullyImplemented); } BOOST_AUTO_TEST_CASE(create_abstract_contract) @@ -430,7 +471,7 @@ BOOST_AUTO_TEST_CASE(create_abstract_contract) function foo() { b = new base();} } )"; - BOOST_CHECK_THROW(parseTextAndResolveNames(text), TypeError); + SOLIDITY_CHECK_ERROR_TYPE(parseAndAnalyseReturnError(text), TypeError); } BOOST_AUTO_TEST_CASE(abstract_contract_constructor_args_optional) @@ -444,7 +485,7 @@ BOOST_AUTO_TEST_CASE(abstract_contract_constructor_args_optional) function foo() {} } )"; - ETH_TEST_REQUIRE_NO_THROW(parseTextAndResolveNames(text), "Parsing and name resolving failed"); + ETH_TEST_REQUIRE_NO_THROW(parseAndAnalyse(text), "Parsing and name resolving failed"); } BOOST_AUTO_TEST_CASE(abstract_contract_constructor_args_not_provided) @@ -458,12 +499,12 @@ BOOST_AUTO_TEST_CASE(abstract_contract_constructor_args_not_provided) function foo() {} } )"; - ETH_TEST_REQUIRE_NO_THROW(sourceUnit = parseTextAndResolveNames(text), "Parsing and name resolving failed"); + ETH_TEST_REQUIRE_NO_THROW(sourceUnit = parseAndAnalyse(text), "Parsing and name resolving failed"); std::vector<ASTPointer<ASTNode>> nodes = sourceUnit->nodes(); BOOST_CHECK_EQUAL(nodes.size(), 3); ContractDefinition* derived = dynamic_cast<ContractDefinition*>(nodes[2].get()); BOOST_CHECK(derived); - BOOST_CHECK(!derived->isFullyImplemented()); + BOOST_CHECK(!derived->annotation().isFullyImplemented); } BOOST_AUTO_TEST_CASE(redeclare_implemented_abstract_function_as_abstract) @@ -474,7 +515,7 @@ BOOST_AUTO_TEST_CASE(redeclare_implemented_abstract_function_as_abstract) contract derived is base { function foo() {} } contract wrong is derived { function foo(); } )"; - BOOST_CHECK_THROW(parseTextAndResolveNames(text), TypeError); + SOLIDITY_CHECK_ERROR_TYPE(parseAndAnalyseReturnError(text), TypeError); } BOOST_AUTO_TEST_CASE(function_canonical_signature) @@ -485,7 +526,7 @@ BOOST_AUTO_TEST_CASE(function_canonical_signature) " ret = arg1 + arg2;\n" " }\n" "}\n"; - ETH_TEST_REQUIRE_NO_THROW(sourceUnit = parseTextAndResolveNames(text), "Parsing and name Resolving failed"); + ETH_TEST_REQUIRE_NO_THROW(sourceUnit = parseAndAnalyse(text), "Parsing and name Resolving failed"); for (ASTPointer<ASTNode> const& node: sourceUnit->nodes()) if (ContractDefinition* contract = dynamic_cast<ContractDefinition*>(node.get())) { @@ -502,7 +543,7 @@ BOOST_AUTO_TEST_CASE(function_canonical_signature_type_aliases) " ret = 5;\n" " }\n" "}\n"; - ETH_TEST_REQUIRE_NO_THROW(sourceUnit = parseTextAndResolveNames(text), "Parsing and name Resolving failed"); + ETH_TEST_REQUIRE_NO_THROW(sourceUnit = parseAndAnalyse(text), "Parsing and name Resolving failed"); for (ASTPointer<ASTNode> const& node: sourceUnit->nodes()) if (ContractDefinition* contract = dynamic_cast<ContractDefinition*>(node.get())) { @@ -525,7 +566,7 @@ BOOST_AUTO_TEST_CASE(function_external_types) ret = 5; } })"; - ETH_TEST_REQUIRE_NO_THROW(sourceUnit = parseTextAndResolveNames(text), "Parsing and name Resolving failed"); + ETH_TEST_REQUIRE_NO_THROW(sourceUnit = parseAndAnalyse(text), "Parsing and name Resolving failed"); for (ASTPointer<ASTNode> const& node: sourceUnit->nodes()) if (ContractDefinition* contract = dynamic_cast<ContractDefinition*>(node.get())) { @@ -547,7 +588,7 @@ BOOST_AUTO_TEST_CASE(enum_external_type) ret = 5; } })"; - ETH_TEST_REQUIRE_NO_THROW(sourceUnit = parseTextAndResolveNames(text), "Parsing and name Resolving failed"); + ETH_TEST_REQUIRE_NO_THROW(sourceUnit = parseAndAnalyse(text), "Parsing and name Resolving failed"); for (ASTPointer<ASTNode> const& node: sourceUnit->nodes()) if (ContractDefinition* contract = dynamic_cast<ContractDefinition*>(node.get())) { @@ -569,7 +610,7 @@ BOOST_AUTO_TEST_CASE(function_external_call_allowed_conversion) } function g (C c) external {} })"; - BOOST_CHECK_NO_THROW(parseTextAndResolveNames(text)); + BOOST_CHECK_NO_THROW(parseAndAnalyse(text)); } BOOST_AUTO_TEST_CASE(function_external_call_not_allowed_conversion) @@ -583,7 +624,7 @@ BOOST_AUTO_TEST_CASE(function_external_call_not_allowed_conversion) } function g (C c) external {} })"; - BOOST_CHECK_THROW(parseTextAndResolveNames(text), TypeError); + SOLIDITY_CHECK_ERROR_TYPE(parseAndAnalyseReturnError(text), TypeError); } BOOST_AUTO_TEST_CASE(function_internal_allowed_conversion) @@ -599,7 +640,7 @@ BOOST_AUTO_TEST_CASE(function_internal_allowed_conversion) g(a); } })"; - BOOST_CHECK_NO_THROW(parseTextAndResolveNames(text)); + BOOST_CHECK_NO_THROW(parseAndAnalyse(text)); } BOOST_AUTO_TEST_CASE(function_internal_not_allowed_conversion) @@ -615,7 +656,7 @@ BOOST_AUTO_TEST_CASE(function_internal_not_allowed_conversion) g(a); } })"; - BOOST_CHECK_THROW(parseTextAndResolveNames(text), TypeError); + SOLIDITY_CHECK_ERROR_TYPE(parseAndAnalyseReturnError(text), TypeError); } BOOST_AUTO_TEST_CASE(hash_collision_in_interface) @@ -626,7 +667,7 @@ BOOST_AUTO_TEST_CASE(hash_collision_in_interface) " function tgeo() {\n" " }\n" "}\n"; - BOOST_CHECK_THROW(parseTextAndResolveNames(text), TypeError); + SOLIDITY_CHECK_ERROR_TYPE(parseAndAnalyseReturnError(text), TypeError); } BOOST_AUTO_TEST_CASE(inheritance_basic) @@ -638,7 +679,7 @@ BOOST_AUTO_TEST_CASE(inheritance_basic) function f() { baseMember = 7; } } )"; - ETH_TEST_CHECK_NO_THROW(parseTextAndResolveNames(text), "Parsing and Name Resolving Failed"); + ETH_TEST_CHECK_NO_THROW(parseAndAnalyse(text), "Parsing and Name Resolving Failed"); } BOOST_AUTO_TEST_CASE(inheritance_diamond_basic) @@ -651,7 +692,7 @@ BOOST_AUTO_TEST_CASE(inheritance_diamond_basic) function g() { f(); rootFunction(); } } )"; - ETH_TEST_CHECK_NO_THROW(parseTextAndResolveNames(text), "Parsing and Name Resolving Failed"); + ETH_TEST_CHECK_NO_THROW(parseAndAnalyse(text), "Parsing and Name Resolving Failed"); } BOOST_AUTO_TEST_CASE(cyclic_inheritance) @@ -660,7 +701,7 @@ BOOST_AUTO_TEST_CASE(cyclic_inheritance) contract A is B { } contract B is A { } )"; - BOOST_CHECK_THROW(parseTextAndResolveNames(text), TypeError); + SOLIDITY_CHECK_ERROR_TYPE(parseAndAnalyseReturnError(text), TypeError); } BOOST_AUTO_TEST_CASE(legal_override_direct) @@ -669,7 +710,7 @@ BOOST_AUTO_TEST_CASE(legal_override_direct) contract B { function f() {} } contract C is B { function f(uint i) {} } )"; - BOOST_CHECK_NO_THROW(parseTextAndResolveNames(text)); + BOOST_CHECK_NO_THROW(parseAndAnalyse(text)); } BOOST_AUTO_TEST_CASE(legal_override_indirect) @@ -679,7 +720,7 @@ BOOST_AUTO_TEST_CASE(legal_override_indirect) contract B { function f() {} } contract C is A, B { } )"; - BOOST_CHECK_NO_THROW(parseTextAndResolveNames(text)); + BOOST_CHECK_NO_THROW(parseAndAnalyse(text)); } BOOST_AUTO_TEST_CASE(illegal_override_visibility) @@ -688,7 +729,7 @@ BOOST_AUTO_TEST_CASE(illegal_override_visibility) contract B { function f() internal {} } contract C is B { function f() public {} } )"; - BOOST_CHECK_THROW(parseTextAndResolveNames(text), TypeError); + SOLIDITY_CHECK_ERROR_TYPE(parseAndAnalyseReturnError(text), TypeError); } BOOST_AUTO_TEST_CASE(illegal_override_constness) @@ -697,7 +738,7 @@ BOOST_AUTO_TEST_CASE(illegal_override_constness) contract B { function f() constant {} } contract C is B { function f() {} } )"; - BOOST_CHECK_THROW(parseTextAndResolveNames(text), TypeError); + SOLIDITY_CHECK_ERROR_TYPE(parseAndAnalyseReturnError(text), TypeError); } BOOST_AUTO_TEST_CASE(complex_inheritance) @@ -707,7 +748,7 @@ BOOST_AUTO_TEST_CASE(complex_inheritance) contract B { function f() {} function g() returns (uint8 r) {} } contract C is A, B { } )"; - ETH_TEST_CHECK_NO_THROW(parseTextAndResolveNames(text), "Parsing and Name Resolving Failed"); + ETH_TEST_CHECK_NO_THROW(parseAndAnalyse(text), "Parsing and Name Resolving Failed"); } BOOST_AUTO_TEST_CASE(constructor_visibility) @@ -717,7 +758,7 @@ BOOST_AUTO_TEST_CASE(constructor_visibility) contract A { function A() { } } contract B is A { function f() { A x = A(0); } } )"; - ETH_TEST_CHECK_NO_THROW(parseTextAndResolveNames(text), "Parsing and Name Resolving Failed"); + ETH_TEST_CHECK_NO_THROW(parseAndAnalyse(text), "Parsing and Name Resolving Failed"); } BOOST_AUTO_TEST_CASE(overriding_constructor) @@ -727,7 +768,7 @@ BOOST_AUTO_TEST_CASE(overriding_constructor) contract A { function A() { } } contract B is A { function A() returns (uint8 r) {} } )"; - ETH_TEST_CHECK_NO_THROW(parseTextAndResolveNames(text), "Parsing and Name Resolving Failed"); + ETH_TEST_CHECK_NO_THROW(parseAndAnalyse(text), "Parsing and Name Resolving Failed"); } BOOST_AUTO_TEST_CASE(missing_base_constructor_arguments) @@ -736,7 +777,7 @@ BOOST_AUTO_TEST_CASE(missing_base_constructor_arguments) contract A { function A(uint a) { } } contract B is A { } )"; - ETH_TEST_CHECK_NO_THROW(parseTextAndResolveNames(text), "Parsing and Name Resolving Failed"); + ETH_TEST_CHECK_NO_THROW(parseAndAnalyse(text), "Parsing and Name Resolving Failed"); } BOOST_AUTO_TEST_CASE(base_constructor_arguments_override) @@ -745,7 +786,7 @@ BOOST_AUTO_TEST_CASE(base_constructor_arguments_override) contract A { function A(uint a) { } } contract B is A { } )"; - ETH_TEST_CHECK_NO_THROW(parseTextAndResolveNames(text), "Parsing and Name Resolving Failed"); + ETH_TEST_CHECK_NO_THROW(parseAndAnalyse(text), "Parsing and Name Resolving Failed"); } BOOST_AUTO_TEST_CASE(implicit_derived_to_base_conversion) @@ -756,7 +797,7 @@ BOOST_AUTO_TEST_CASE(implicit_derived_to_base_conversion) function f() { A a = B(1); } } )"; - ETH_TEST_CHECK_NO_THROW(parseTextAndResolveNames(text), "Parsing and Name Resolving Failed"); + ETH_TEST_CHECK_NO_THROW(parseAndAnalyse(text), "Parsing and Name Resolving Failed"); } BOOST_AUTO_TEST_CASE(implicit_base_to_derived_conversion) @@ -767,7 +808,7 @@ BOOST_AUTO_TEST_CASE(implicit_base_to_derived_conversion) function f() { B b = A(1); } } )"; - BOOST_CHECK_THROW(parseTextAndResolveNames(text), TypeError); + SOLIDITY_CHECK_ERROR_TYPE(parseAndAnalyseReturnError(text), TypeError); } BOOST_AUTO_TEST_CASE(function_modifier_invocation) @@ -779,7 +820,7 @@ BOOST_AUTO_TEST_CASE(function_modifier_invocation) modifier mod2(bytes7 a) { while (a == "1234567") _ } } )"; - ETH_TEST_CHECK_NO_THROW(parseTextAndResolveNames(text), "Parsing and Name Resolving Failed"); + ETH_TEST_CHECK_NO_THROW(parseAndAnalyse(text), "Parsing and Name Resolving Failed"); } BOOST_AUTO_TEST_CASE(invalid_function_modifier_type) @@ -790,7 +831,7 @@ BOOST_AUTO_TEST_CASE(invalid_function_modifier_type) modifier mod1(uint a) { if (a > 0) _ } } )"; - BOOST_CHECK_THROW(parseTextAndResolveNames(text), TypeError); + SOLIDITY_CHECK_ERROR_TYPE(parseAndAnalyseReturnError(text), TypeError); } BOOST_AUTO_TEST_CASE(function_modifier_invocation_parameters) @@ -802,7 +843,7 @@ BOOST_AUTO_TEST_CASE(function_modifier_invocation_parameters) modifier mod2(bytes7 a) { while (a == "1234567") _ } } )"; - ETH_TEST_CHECK_NO_THROW(parseTextAndResolveNames(text), "Parsing and Name Resolving Failed"); + ETH_TEST_CHECK_NO_THROW(parseAndAnalyse(text), "Parsing and Name Resolving Failed"); } BOOST_AUTO_TEST_CASE(function_modifier_invocation_local_variables) @@ -813,7 +854,7 @@ BOOST_AUTO_TEST_CASE(function_modifier_invocation_local_variables) modifier mod(uint a) { if (a > 0) _ } } )"; - ETH_TEST_CHECK_NO_THROW(parseTextAndResolveNames(text), "Parsing and Name Resolving Failed"); + ETH_TEST_CHECK_NO_THROW(parseAndAnalyse(text), "Parsing and Name Resolving Failed"); } BOOST_AUTO_TEST_CASE(legal_modifier_override) @@ -822,7 +863,7 @@ BOOST_AUTO_TEST_CASE(legal_modifier_override) contract A { modifier mod(uint a) {} } contract B is A { modifier mod(uint a) {} } )"; - ETH_TEST_CHECK_NO_THROW(parseTextAndResolveNames(text), "Parsing and Name Resolving Failed"); + ETH_TEST_CHECK_NO_THROW(parseAndAnalyse(text), "Parsing and Name Resolving Failed"); } BOOST_AUTO_TEST_CASE(illegal_modifier_override) @@ -831,7 +872,7 @@ BOOST_AUTO_TEST_CASE(illegal_modifier_override) contract A { modifier mod(uint a) {} } contract B is A { modifier mod(uint8 a) {} } )"; - BOOST_CHECK_THROW(parseTextAndResolveNames(text), TypeError); + SOLIDITY_CHECK_ERROR_TYPE(parseAndAnalyseReturnError(text), TypeError); } BOOST_AUTO_TEST_CASE(modifier_overrides_function) @@ -840,7 +881,7 @@ BOOST_AUTO_TEST_CASE(modifier_overrides_function) contract A { modifier mod(uint a) {} } contract B is A { function mod(uint a) {} } )"; - BOOST_CHECK_THROW(parseTextAndResolveNames(text), TypeError); + SOLIDITY_CHECK_ERROR_TYPE(parseAndAnalyseReturnError(text), TypeError); } BOOST_AUTO_TEST_CASE(function_overrides_modifier) @@ -849,7 +890,7 @@ BOOST_AUTO_TEST_CASE(function_overrides_modifier) contract A { function mod(uint a) {} } contract B is A { modifier mod(uint a) {} } )"; - BOOST_CHECK_THROW(parseTextAndResolveNames(text), TypeError); + SOLIDITY_CHECK_ERROR_TYPE(parseAndAnalyseReturnError(text), TypeError); } BOOST_AUTO_TEST_CASE(modifier_returns_value) @@ -860,7 +901,7 @@ BOOST_AUTO_TEST_CASE(modifier_returns_value) modifier mod(uint a) { return 7; } } )"; - BOOST_CHECK_THROW(parseTextAndResolveNames(text), TypeError); + SOLIDITY_CHECK_ERROR_TYPE(parseAndAnalyseReturnError(text), TypeError); } BOOST_AUTO_TEST_CASE(state_variable_accessors) @@ -876,7 +917,7 @@ BOOST_AUTO_TEST_CASE(state_variable_accessors) ASTPointer<SourceUnit> source; ContractDefinition const* contract; - ETH_TEST_CHECK_NO_THROW(source = parseTextAndResolveNames(text), "Parsing and Resolving names failed"); + ETH_TEST_CHECK_NO_THROW(source = parseAndAnalyse(text), "Parsing and Resolving names failed"); BOOST_REQUIRE((contract = retrieveContract(source, 0)) != nullptr); FunctionTypePointer function = retrieveFunctionBySignature(contract, "foo()"); BOOST_REQUIRE(function && function->hasDeclaration()); @@ -911,7 +952,7 @@ BOOST_AUTO_TEST_CASE(function_clash_with_state_variable_accessor) "uint256 foo;\n" " function foo() {}\n" "}\n"; - BOOST_CHECK_THROW(parseTextAndResolveNames(text), DeclarationError); + SOLIDITY_CHECK_ERROR_TYPE(parseAndAnalyseReturnError(text), DeclarationError); } BOOST_AUTO_TEST_CASE(private_state_variable) @@ -926,7 +967,7 @@ BOOST_AUTO_TEST_CASE(private_state_variable) ASTPointer<SourceUnit> source; ContractDefinition const* contract; - ETH_TEST_CHECK_NO_THROW(source = parseTextAndResolveNames(text), "Parsing and Resolving names failed"); + ETH_TEST_CHECK_NO_THROW(source = parseAndAnalyse(text), "Parsing and Resolving names failed"); BOOST_CHECK((contract = retrieveContract(source, 0)) != nullptr); FunctionTypePointer function; function = retrieveFunctionBySignature(contract, "foo()"); @@ -944,7 +985,7 @@ BOOST_AUTO_TEST_CASE(base_class_state_variable_accessor) "contract Child is Parent{\n" " function foo() returns (uint256) { return Parent.m_aMember; }\n" "}\n"; - ETH_TEST_CHECK_NO_THROW(parseTextAndResolveNames(text), "Parsing and Name Resolving Failed"); + ETH_TEST_CHECK_NO_THROW(parseAndAnalyse(text), "Parsing and Name Resolving Failed"); } BOOST_AUTO_TEST_CASE(base_class_state_variable_internal_member) @@ -955,7 +996,7 @@ BOOST_AUTO_TEST_CASE(base_class_state_variable_internal_member) "contract Child is Parent{\n" " function foo() returns (uint256) { return Parent.m_aMember; }\n" "}\n"; - ETH_TEST_CHECK_NO_THROW(parseTextAndResolveNames(text), "Parsing and Name Resolving Failed"); + ETH_TEST_CHECK_NO_THROW(parseAndAnalyse(text), "Parsing and Name Resolving Failed"); } BOOST_AUTO_TEST_CASE(state_variable_member_of_wrong_class1) @@ -969,7 +1010,7 @@ BOOST_AUTO_TEST_CASE(state_variable_member_of_wrong_class1) "contract Child is Parent2{\n" " function foo() returns (uint256) { return Parent2.m_aMember1; }\n" "}\n"; - BOOST_CHECK_THROW(parseTextAndResolveNames(text), TypeError); + SOLIDITY_CHECK_ERROR_TYPE(parseAndAnalyseReturnError(text), TypeError); } BOOST_AUTO_TEST_CASE(state_variable_member_of_wrong_class2) @@ -984,7 +1025,7 @@ BOOST_AUTO_TEST_CASE(state_variable_member_of_wrong_class2) " function foo() returns (uint256) { return Child.m_aMember2; }\n" " uint256 public m_aMember3;\n" "}\n"; - BOOST_CHECK_THROW(parseTextAndResolveNames(text), TypeError); + SOLIDITY_CHECK_ERROR_TYPE(parseAndAnalyseReturnError(text), TypeError); } BOOST_AUTO_TEST_CASE(fallback_function) @@ -995,7 +1036,7 @@ BOOST_AUTO_TEST_CASE(fallback_function) function() { x = 2; } } )"; - ETH_TEST_CHECK_NO_THROW(parseTextAndResolveNames(text), "Parsing and Name Resolving Failed"); + ETH_TEST_CHECK_NO_THROW(parseAndAnalyse(text), "Parsing and Name Resolving Failed"); } BOOST_AUTO_TEST_CASE(fallback_function_with_arguments) @@ -1006,7 +1047,7 @@ BOOST_AUTO_TEST_CASE(fallback_function_with_arguments) function(uint a) { x = 2; } } )"; - BOOST_CHECK_THROW(parseTextAndResolveNames(text), TypeError); + SOLIDITY_CHECK_ERROR_TYPE(parseAndAnalyseReturnError(text), TypeError); } BOOST_AUTO_TEST_CASE(fallback_function_twice) @@ -1018,7 +1059,7 @@ BOOST_AUTO_TEST_CASE(fallback_function_twice) function() { x = 3; } } )"; - BOOST_CHECK_THROW(parseTextAndResolveNames(text), DeclarationError); + SOLIDITY_CHECK_ERROR_TYPE(parseAndAnalyseReturnError(text), DeclarationError); } BOOST_AUTO_TEST_CASE(fallback_function_inheritance) @@ -1032,7 +1073,7 @@ BOOST_AUTO_TEST_CASE(fallback_function_inheritance) function() { x = 2; } } )"; - ETH_TEST_CHECK_NO_THROW(parseTextAndResolveNames(text), "Parsing and Name Resolving Failed"); + ETH_TEST_CHECK_NO_THROW(parseAndAnalyse(text), "Parsing and Name Resolving Failed"); } BOOST_AUTO_TEST_CASE(event) @@ -1042,7 +1083,7 @@ BOOST_AUTO_TEST_CASE(event) event e(uint indexed a, bytes3 indexed s, bool indexed b); function f() { e(2, "abc", true); } })"; - ETH_TEST_CHECK_NO_THROW(parseTextAndResolveNames(text), "Parsing and Name Resolving Failed"); + ETH_TEST_CHECK_NO_THROW(parseAndAnalyse(text), "Parsing and Name Resolving Failed"); } BOOST_AUTO_TEST_CASE(event_too_many_indexed) @@ -1051,7 +1092,7 @@ BOOST_AUTO_TEST_CASE(event_too_many_indexed) contract c { event e(uint indexed a, bytes3 indexed b, bool indexed c, uint indexed d); })"; - BOOST_CHECK_THROW(parseTextAndResolveNames(text), TypeError); + SOLIDITY_CHECK_ERROR_TYPE(parseAndAnalyseReturnError(text), TypeError); } BOOST_AUTO_TEST_CASE(event_call) @@ -1061,7 +1102,7 @@ BOOST_AUTO_TEST_CASE(event_call) event e(uint a, bytes3 indexed s, bool indexed b); function f() { e(2, "abc", true); } })"; - ETH_TEST_CHECK_NO_THROW(parseTextAndResolveNames(text), "Parsing and Name Resolving Failed"); + ETH_TEST_CHECK_NO_THROW(parseAndAnalyse(text), "Parsing and Name Resolving Failed"); } BOOST_AUTO_TEST_CASE(event_inheritance) @@ -1073,7 +1114,7 @@ BOOST_AUTO_TEST_CASE(event_inheritance) contract c is base { function f() { e(2, "abc", true); } })"; - ETH_TEST_CHECK_NO_THROW(parseTextAndResolveNames(text), "Parsing and Name Resolving Failed"); + ETH_TEST_CHECK_NO_THROW(parseAndAnalyse(text), "Parsing and Name Resolving Failed"); } BOOST_AUTO_TEST_CASE(multiple_events_argument_clash) @@ -1083,7 +1124,7 @@ BOOST_AUTO_TEST_CASE(multiple_events_argument_clash) event e1(uint a, uint e1, uint e2); event e2(uint a, uint e1, uint e2); })"; - ETH_TEST_CHECK_NO_THROW(parseTextAndResolveNames(text), "Parsing and Name Resolving Failed"); + ETH_TEST_CHECK_NO_THROW(parseAndAnalyse(text), "Parsing and Name Resolving Failed"); } BOOST_AUTO_TEST_CASE(access_to_default_function_visibility) @@ -1095,7 +1136,7 @@ BOOST_AUTO_TEST_CASE(access_to_default_function_visibility) contract d { function g() { c(0).f(); } })"; - ETH_TEST_CHECK_NO_THROW(parseTextAndResolveNames(text), "Parsing and Name Resolving Failed"); + ETH_TEST_CHECK_NO_THROW(parseAndAnalyse(text), "Parsing and Name Resolving Failed"); } BOOST_AUTO_TEST_CASE(access_to_internal_function) @@ -1107,7 +1148,7 @@ BOOST_AUTO_TEST_CASE(access_to_internal_function) contract d { function g() { c(0).f(); } })"; - BOOST_CHECK_THROW(parseTextAndResolveNames(text), TypeError); + SOLIDITY_CHECK_ERROR_TYPE(parseAndAnalyseReturnError(text), TypeError); } BOOST_AUTO_TEST_CASE(access_to_default_state_variable_visibility) @@ -1119,7 +1160,7 @@ BOOST_AUTO_TEST_CASE(access_to_default_state_variable_visibility) contract d { function g() { c(0).a(); } })"; - BOOST_CHECK_THROW(parseTextAndResolveNames(text), TypeError); + SOLIDITY_CHECK_ERROR_TYPE(parseAndAnalyseReturnError(text), TypeError); } BOOST_AUTO_TEST_CASE(access_to_internal_state_variable) @@ -1131,7 +1172,7 @@ BOOST_AUTO_TEST_CASE(access_to_internal_state_variable) contract d { function g() { c(0).a(); } })"; - ETH_TEST_CHECK_NO_THROW(parseTextAndResolveNames(text), "Parsing and Name Resolving Failed"); + ETH_TEST_CHECK_NO_THROW(parseAndAnalyse(text), "Parsing and Name Resolving Failed"); } BOOST_AUTO_TEST_CASE(error_count_in_named_args) @@ -1140,7 +1181,7 @@ BOOST_AUTO_TEST_CASE(error_count_in_named_args) " function a(uint a, uint b) returns (uint r) { r = a + b; }\n" " function b() returns (uint r) { r = a({a: 1}); }\n" "}\n"; - BOOST_CHECK_THROW(parseTextAndResolveNames(sourceCode), TypeError); + SOLIDITY_CHECK_ERROR_TYPE(parseAndAnalyseReturnError(sourceCode), TypeError); } BOOST_AUTO_TEST_CASE(empty_in_named_args) @@ -1149,7 +1190,7 @@ BOOST_AUTO_TEST_CASE(empty_in_named_args) " function a(uint a, uint b) returns (uint r) { r = a + b; }\n" " function b() returns (uint r) { r = a({}); }\n" "}\n"; - BOOST_CHECK_THROW(parseTextAndResolveNames(sourceCode), TypeError); + SOLIDITY_CHECK_ERROR_TYPE(parseAndAnalyseReturnError(sourceCode), TypeError); } BOOST_AUTO_TEST_CASE(duplicate_parameter_names_in_named_args) @@ -1158,7 +1199,7 @@ BOOST_AUTO_TEST_CASE(duplicate_parameter_names_in_named_args) " function a(uint a, uint b) returns (uint r) { r = a + b; }\n" " function b() returns (uint r) { r = a({a: 1, a: 2}); }\n" "}\n"; - BOOST_CHECK_THROW(parseTextAndResolveNames(sourceCode), TypeError); + SOLIDITY_CHECK_ERROR_TYPE(parseAndAnalyseReturnError(sourceCode), TypeError); } BOOST_AUTO_TEST_CASE(invalid_parameter_names_in_named_args) @@ -1167,7 +1208,7 @@ BOOST_AUTO_TEST_CASE(invalid_parameter_names_in_named_args) " function a(uint a, uint b) returns (uint r) { r = a + b; }\n" " function b() returns (uint r) { r = a({a: 1, c: 2}); }\n" "}\n"; - BOOST_CHECK_THROW(parseTextAndResolveNames(sourceCode), TypeError); + SOLIDITY_CHECK_ERROR_TYPE(parseAndAnalyseReturnError(sourceCode), TypeError); } BOOST_AUTO_TEST_CASE(empty_name_input_parameter) @@ -1177,7 +1218,7 @@ BOOST_AUTO_TEST_CASE(empty_name_input_parameter) function f(uint){ } })"; - ETH_TEST_CHECK_NO_THROW(parseTextAndResolveNames(text), "Parsing and Name Resolving Failed"); + ETH_TEST_CHECK_NO_THROW(parseAndAnalyse(text), "Parsing and Name Resolving Failed"); } BOOST_AUTO_TEST_CASE(empty_name_return_parameter) @@ -1187,7 +1228,7 @@ BOOST_AUTO_TEST_CASE(empty_name_return_parameter) function f() returns(bool){ } })"; - ETH_TEST_CHECK_NO_THROW(parseTextAndResolveNames(text), "Parsing and Name Resolving Failed"); + ETH_TEST_CHECK_NO_THROW(parseAndAnalyse(text), "Parsing and Name Resolving Failed"); } BOOST_AUTO_TEST_CASE(empty_name_input_parameter_with_named_one) @@ -1198,7 +1239,7 @@ BOOST_AUTO_TEST_CASE(empty_name_input_parameter_with_named_one) return k; } })"; - ETH_TEST_CHECK_NO_THROW(parseTextAndResolveNames(text), "Parsing and Name Resolving Failed"); + ETH_TEST_CHECK_NO_THROW(parseAndAnalyse(text), "Parsing and Name Resolving Failed"); } BOOST_AUTO_TEST_CASE(empty_name_return_parameter_with_named_one) @@ -1209,13 +1250,13 @@ BOOST_AUTO_TEST_CASE(empty_name_return_parameter_with_named_one) return 5; } })"; - BOOST_CHECK_THROW(parseTextAndResolveNames(text), TypeError); + SOLIDITY_CHECK_ERROR_TYPE(parseAndAnalyseReturnError(text), TypeError); } BOOST_AUTO_TEST_CASE(disallow_declaration_of_void_type) { char const* sourceCode = "contract c { function f() { var x = f(); } }"; - BOOST_CHECK_THROW(parseTextAndResolveNames(sourceCode), TypeError); + SOLIDITY_CHECK_ERROR_TYPE(parseAndAnalyseReturnError(sourceCode), TypeError); } BOOST_AUTO_TEST_CASE(overflow_caused_by_ether_units) @@ -1228,7 +1269,7 @@ BOOST_AUTO_TEST_CASE(overflow_caused_by_ether_units) } uint256 a; })"; - ETH_TEST_CHECK_NO_THROW(parseTextAndResolveNames(sourceCodeFine), + ETH_TEST_CHECK_NO_THROW(parseAndAnalyse(sourceCodeFine), "Parsing and Resolving names failed"); char const* sourceCode = R"( contract c { @@ -1238,7 +1279,7 @@ BOOST_AUTO_TEST_CASE(overflow_caused_by_ether_units) } uint256 a; })"; - BOOST_CHECK_THROW(parseTextAndResolveNames(sourceCode), TypeError); + SOLIDITY_CHECK_ERROR_TYPE(parseAndAnalyseReturnError(sourceCode), TypeError); } BOOST_AUTO_TEST_CASE(exp_operator_negative_exponent) @@ -1247,7 +1288,7 @@ BOOST_AUTO_TEST_CASE(exp_operator_negative_exponent) contract test { function f() returns(uint d) { return 2 ** -3; } })"; - BOOST_CHECK_THROW(parseTextAndResolveNames(sourceCode), TypeError); + SOLIDITY_CHECK_ERROR_TYPE(parseAndAnalyseReturnError(sourceCode), TypeError); } BOOST_AUTO_TEST_CASE(exp_operator_exponent_too_big) @@ -1256,7 +1297,7 @@ BOOST_AUTO_TEST_CASE(exp_operator_exponent_too_big) contract test { function f() returns(uint d) { return 2 ** 10000000000; } })"; - BOOST_CHECK_THROW(parseTextAndResolveNames(sourceCode), TypeError); + SOLIDITY_CHECK_ERROR_TYPE(parseAndAnalyseReturnError(sourceCode), TypeError); } BOOST_AUTO_TEST_CASE(enum_member_access) @@ -1271,7 +1312,7 @@ BOOST_AUTO_TEST_CASE(enum_member_access) ActionChoices choices; } )"; - ETH_TEST_CHECK_NO_THROW(parseTextAndResolveNames(text), "Parsing and Name Resolving Failed"); + ETH_TEST_CHECK_NO_THROW(parseAndAnalyse(text), "Parsing and Name Resolving Failed"); } BOOST_AUTO_TEST_CASE(enum_invalid_member_access) @@ -1286,7 +1327,7 @@ BOOST_AUTO_TEST_CASE(enum_invalid_member_access) ActionChoices choices; } )"; - BOOST_CHECK_THROW(parseTextAndResolveNames(text), TypeError); + SOLIDITY_CHECK_ERROR_TYPE(parseAndAnalyseReturnError(text), TypeError); } BOOST_AUTO_TEST_CASE(enum_explicit_conversion_is_okay) @@ -1303,7 +1344,7 @@ BOOST_AUTO_TEST_CASE(enum_explicit_conversion_is_okay) uint64 b; } )"; - ETH_TEST_CHECK_NO_THROW(parseTextAndResolveNames(text), "Parsing and Name Resolving Failed"); + ETH_TEST_CHECK_NO_THROW(parseAndAnalyse(text), "Parsing and Name Resolving Failed"); } BOOST_AUTO_TEST_CASE(int_to_enum_explicit_conversion_is_okay) @@ -1320,7 +1361,7 @@ BOOST_AUTO_TEST_CASE(int_to_enum_explicit_conversion_is_okay) ActionChoices b; } )"; - ETH_TEST_CHECK_NO_THROW(parseTextAndResolveNames(text), "Parsing and Name Resolving Failed"); + ETH_TEST_CHECK_NO_THROW(parseAndAnalyse(text), "Parsing and Name Resolving Failed"); } BOOST_AUTO_TEST_CASE(enum_implicit_conversion_is_not_okay) @@ -1337,7 +1378,7 @@ BOOST_AUTO_TEST_CASE(enum_implicit_conversion_is_not_okay) uint64 b; } )"; - BOOST_CHECK_THROW(parseTextAndResolveNames(text), TypeError); + SOLIDITY_CHECK_ERROR_TYPE(parseAndAnalyseReturnError(text), TypeError); } BOOST_AUTO_TEST_CASE(enum_duplicate_values) @@ -1347,7 +1388,7 @@ BOOST_AUTO_TEST_CASE(enum_duplicate_values) enum ActionChoices { GoLeft, GoRight, GoLeft, Sit } } )"; - BOOST_CHECK_THROW(parseTextAndResolveNames(text), DeclarationError); + SOLIDITY_CHECK_ERROR_TYPE(parseAndAnalyseReturnError(text), DeclarationError); } BOOST_AUTO_TEST_CASE(private_visibility) @@ -1360,7 +1401,7 @@ BOOST_AUTO_TEST_CASE(private_visibility) function g() { f(); } } )"; - BOOST_CHECK_THROW(parseTextAndResolveNames(sourceCode), DeclarationError); + SOLIDITY_CHECK_ERROR_TYPE(parseAndAnalyseReturnError(sourceCode), DeclarationError); } BOOST_AUTO_TEST_CASE(private_visibility_via_explicit_base_access) @@ -1373,7 +1414,7 @@ BOOST_AUTO_TEST_CASE(private_visibility_via_explicit_base_access) function g() { base.f(); } } )"; - BOOST_CHECK_THROW(parseTextAndResolveNames(sourceCode), TypeError); + SOLIDITY_CHECK_ERROR_TYPE(parseAndAnalyseReturnError(sourceCode), TypeError); } BOOST_AUTO_TEST_CASE(external_visibility) @@ -1384,7 +1425,7 @@ BOOST_AUTO_TEST_CASE(external_visibility) function g() { f(); } } )"; - BOOST_CHECK_THROW(parseTextAndResolveNames(sourceCode), DeclarationError); + SOLIDITY_CHECK_ERROR_TYPE(parseAndAnalyseReturnError(sourceCode), DeclarationError); } BOOST_AUTO_TEST_CASE(external_base_visibility) @@ -1397,7 +1438,7 @@ BOOST_AUTO_TEST_CASE(external_base_visibility) function g() { base.f(); } } )"; - BOOST_CHECK_THROW(parseTextAndResolveNames(sourceCode), TypeError); + SOLIDITY_CHECK_ERROR_TYPE(parseAndAnalyseReturnError(sourceCode), TypeError); } BOOST_AUTO_TEST_CASE(external_argument_assign) @@ -1407,7 +1448,7 @@ BOOST_AUTO_TEST_CASE(external_argument_assign) function f(uint a) external { a = 1; } } )"; - BOOST_CHECK_THROW(parseTextAndResolveNames(sourceCode), TypeError); + SOLIDITY_CHECK_ERROR_TYPE(parseAndAnalyseReturnError(sourceCode), TypeError); } BOOST_AUTO_TEST_CASE(external_argument_increment) @@ -1417,7 +1458,7 @@ BOOST_AUTO_TEST_CASE(external_argument_increment) function f(uint a) external { a++; } } )"; - BOOST_CHECK_THROW(parseTextAndResolveNames(sourceCode), TypeError); + SOLIDITY_CHECK_ERROR_TYPE(parseAndAnalyseReturnError(sourceCode), TypeError); } BOOST_AUTO_TEST_CASE(external_argument_delete) @@ -1427,7 +1468,7 @@ BOOST_AUTO_TEST_CASE(external_argument_delete) function f(uint a) external { delete a; } } )"; - BOOST_CHECK_THROW(parseTextAndResolveNames(sourceCode), TypeError); + SOLIDITY_CHECK_ERROR_TYPE(parseAndAnalyseReturnError(sourceCode), TypeError); } BOOST_AUTO_TEST_CASE(test_for_bug_override_function_with_bytearray_type) @@ -1440,7 +1481,7 @@ BOOST_AUTO_TEST_CASE(test_for_bug_override_function_with_bytearray_type) function f(bytes _a) external returns (uint256 r) {r = 42;} } )"; - ETH_TEST_CHECK_NO_THROW(parseTextAndResolveNames(sourceCode), "Parsing and Name Resolving failed"); + ETH_TEST_CHECK_NO_THROW(parseAndAnalyse(sourceCode), "Parsing and Name Resolving failed"); } BOOST_AUTO_TEST_CASE(array_with_nonconstant_length) @@ -1449,7 +1490,7 @@ BOOST_AUTO_TEST_CASE(array_with_nonconstant_length) contract c { function f(uint a) { uint8[a] x; } })"; - BOOST_CHECK_THROW(parseTextAndResolveNames(text), TypeError); + SOLIDITY_CHECK_ERROR_TYPE(parseAndAnalyseReturnError(text), TypeError); } BOOST_AUTO_TEST_CASE(array_copy_with_different_types1) @@ -1460,7 +1501,7 @@ BOOST_AUTO_TEST_CASE(array_copy_with_different_types1) uint[] b; function f() { b = a; } })"; - BOOST_CHECK_THROW(parseTextAndResolveNames(text), TypeError); + SOLIDITY_CHECK_ERROR_TYPE(parseAndAnalyseReturnError(text), TypeError); } BOOST_AUTO_TEST_CASE(array_copy_with_different_types2) @@ -1471,7 +1512,7 @@ BOOST_AUTO_TEST_CASE(array_copy_with_different_types2) uint8[] b; function f() { b = a; } })"; - BOOST_CHECK_THROW(parseTextAndResolveNames(text), TypeError); + SOLIDITY_CHECK_ERROR_TYPE(parseAndAnalyseReturnError(text), TypeError); } BOOST_AUTO_TEST_CASE(array_copy_with_different_types_conversion_possible) @@ -1482,7 +1523,7 @@ BOOST_AUTO_TEST_CASE(array_copy_with_different_types_conversion_possible) uint8[] b; function f() { a = b; } })"; - ETH_TEST_CHECK_NO_THROW(parseTextAndResolveNames(text), "Parsing and Name Resolving Failed"); + ETH_TEST_CHECK_NO_THROW(parseAndAnalyse(text), "Parsing and Name Resolving Failed"); } BOOST_AUTO_TEST_CASE(array_copy_with_different_types_static_dynamic) @@ -1493,7 +1534,7 @@ BOOST_AUTO_TEST_CASE(array_copy_with_different_types_static_dynamic) uint8[80] b; function f() { a = b; } })"; - ETH_TEST_CHECK_NO_THROW(parseTextAndResolveNames(text), "Parsing and Name Resolving Failed"); + ETH_TEST_CHECK_NO_THROW(parseAndAnalyse(text), "Parsing and Name Resolving Failed"); } BOOST_AUTO_TEST_CASE(array_copy_with_different_types_dynamic_static) @@ -1504,7 +1545,7 @@ BOOST_AUTO_TEST_CASE(array_copy_with_different_types_dynamic_static) uint[80] b; function f() { b = a; } })"; - BOOST_CHECK_THROW(parseTextAndResolveNames(text), TypeError); + SOLIDITY_CHECK_ERROR_TYPE(parseAndAnalyseReturnError(text), TypeError); } BOOST_AUTO_TEST_CASE(storage_variable_initialization_with_incorrect_type_int) @@ -1513,7 +1554,7 @@ BOOST_AUTO_TEST_CASE(storage_variable_initialization_with_incorrect_type_int) contract c { uint8 a = 1000; })"; - BOOST_CHECK_THROW(parseTextAndResolveNames(text), TypeError); + SOLIDITY_CHECK_ERROR_TYPE(parseAndAnalyseReturnError(text), TypeError); } BOOST_AUTO_TEST_CASE(storage_variable_initialization_with_incorrect_type_string) @@ -1522,7 +1563,7 @@ BOOST_AUTO_TEST_CASE(storage_variable_initialization_with_incorrect_type_string) contract c { uint a = "abc"; })"; - BOOST_CHECK_THROW(parseTextAndResolveNames(text), TypeError); + SOLIDITY_CHECK_ERROR_TYPE(parseAndAnalyseReturnError(text), TypeError); } BOOST_AUTO_TEST_CASE(test_fromElementaryTypeName) @@ -1637,7 +1678,7 @@ BOOST_AUTO_TEST_CASE(test_byte_is_alias_of_byte1) bytes arr; function f() { byte a = arr[0];} })"; - ETH_TEST_REQUIRE_NO_THROW(parseTextAndResolveNames(text), "Type resolving failed"); + ETH_TEST_REQUIRE_NO_THROW(parseAndAnalyse(text), "Type resolving failed"); } BOOST_AUTO_TEST_CASE(assigning_value_to_const_variable) @@ -1647,7 +1688,7 @@ BOOST_AUTO_TEST_CASE(assigning_value_to_const_variable) function changeIt() { x = 9; } uint constant x = 56; })"; - BOOST_CHECK_THROW(parseTextAndResolveNames(text), TypeError); + SOLIDITY_CHECK_ERROR_TYPE(parseAndAnalyseReturnError(text), TypeError); } BOOST_AUTO_TEST_CASE(complex_const_variable) @@ -1657,7 +1698,7 @@ BOOST_AUTO_TEST_CASE(complex_const_variable) contract Foo { mapping(uint => bool) constant mapVar; })"; - BOOST_CHECK_THROW(parseTextAndResolveNames(text), TypeError); + SOLIDITY_CHECK_ERROR_TYPE(parseAndAnalyseReturnError(text), TypeError); } BOOST_AUTO_TEST_CASE(uninitialized_const_variable) @@ -1666,7 +1707,7 @@ BOOST_AUTO_TEST_CASE(uninitialized_const_variable) contract Foo { uint constant y; })"; - BOOST_CHECK_THROW(parseTextAndResolveNames(text), TypeError); + SOLIDITY_CHECK_ERROR_TYPE(parseAndAnalyseReturnError(text), TypeError); } BOOST_AUTO_TEST_CASE(local_const_variable) @@ -1679,7 +1720,7 @@ BOOST_AUTO_TEST_CASE(local_const_variable) return local; } })"; - BOOST_CHECK_THROW(parseTextAndResolveNames(text), ParserError); + SOLIDITY_CHECK_ERROR_TYPE(parseAndAnalyseReturnError(text), ParserError); } BOOST_AUTO_TEST_CASE(overloaded_function_cannot_resolve) @@ -1691,7 +1732,7 @@ BOOST_AUTO_TEST_CASE(overloaded_function_cannot_resolve) function g() returns(uint) { return f(3, 5); } } )"; - BOOST_CHECK_THROW(parseTextAndResolveNames(sourceCode), TypeError); + SOLIDITY_CHECK_ERROR_TYPE(parseAndAnalyseReturnError(sourceCode), TypeError); } BOOST_AUTO_TEST_CASE(ambiguous_overloaded_function) @@ -1704,7 +1745,7 @@ BOOST_AUTO_TEST_CASE(ambiguous_overloaded_function) function g() returns(uint) { return f(1); } } )"; - BOOST_CHECK_THROW(parseTextAndResolveNames(sourceCode), TypeError); + SOLIDITY_CHECK_ERROR_TYPE(parseAndAnalyseReturnError(sourceCode), TypeError); } BOOST_AUTO_TEST_CASE(assignment_of_nonoverloaded_function) @@ -1715,7 +1756,7 @@ BOOST_AUTO_TEST_CASE(assignment_of_nonoverloaded_function) function g() returns(uint) { var x = f; return x(7); } } )"; - ETH_TEST_REQUIRE_NO_THROW(parseTextAndResolveNames(sourceCode), "Type resolving failed"); + ETH_TEST_REQUIRE_NO_THROW(parseAndAnalyse(sourceCode), "Type resolving failed"); } BOOST_AUTO_TEST_CASE(assignment_of_overloaded_function) @@ -1727,7 +1768,7 @@ BOOST_AUTO_TEST_CASE(assignment_of_overloaded_function) function g() returns(uint) { var x = f; return x(7); } } )"; - BOOST_CHECK_THROW(parseTextAndResolveNames(sourceCode), TypeError); + SOLIDITY_CHECK_ERROR_TYPE(parseAndAnalyseReturnError(sourceCode), TypeError); } BOOST_AUTO_TEST_CASE(external_types_clash) @@ -1741,7 +1782,7 @@ BOOST_AUTO_TEST_CASE(external_types_clash) function f(uint8 a) { } } )"; - BOOST_CHECK_THROW(parseTextAndResolveNames(sourceCode), TypeError); + SOLIDITY_CHECK_ERROR_TYPE(parseAndAnalyseReturnError(sourceCode), TypeError); } BOOST_AUTO_TEST_CASE(override_changes_return_types) @@ -1754,7 +1795,7 @@ BOOST_AUTO_TEST_CASE(override_changes_return_types) function f(uint a) returns (uint8) { } } )"; - BOOST_CHECK_THROW(parseTextAndResolveNames(sourceCode), TypeError); + SOLIDITY_CHECK_ERROR_TYPE(parseAndAnalyseReturnError(sourceCode), TypeError); } BOOST_AUTO_TEST_CASE(multiple_constructors) @@ -1765,7 +1806,7 @@ BOOST_AUTO_TEST_CASE(multiple_constructors) function test() {} } )"; - BOOST_CHECK_THROW(parseTextAndResolveNames(sourceCode), DeclarationError); + SOLIDITY_CHECK_ERROR_TYPE(parseAndAnalyseReturnError(sourceCode), DeclarationError); } BOOST_AUTO_TEST_CASE(equal_overload) @@ -1776,7 +1817,7 @@ BOOST_AUTO_TEST_CASE(equal_overload) function test(uint a) external {} } )"; - BOOST_CHECK_THROW(parseTextAndResolveNames(sourceCode), DeclarationError); + SOLIDITY_CHECK_ERROR_TYPE(parseAndAnalyseReturnError(sourceCode), DeclarationError); } BOOST_AUTO_TEST_CASE(uninitialized_var) @@ -1786,7 +1827,7 @@ BOOST_AUTO_TEST_CASE(uninitialized_var) function f() returns (uint) { var x; return 2; } } )"; - BOOST_CHECK_THROW(parseTextAndResolveNames(sourceCode), TypeError); + SOLIDITY_CHECK_ERROR_TYPE(parseAndAnalyseReturnError(sourceCode), TypeError); } BOOST_AUTO_TEST_CASE(string) @@ -1797,7 +1838,7 @@ BOOST_AUTO_TEST_CASE(string) function f(string x) external { s = x; } } )"; - BOOST_CHECK_NO_THROW(parseTextAndResolveNames(sourceCode)); + BOOST_CHECK_NO_THROW(parseAndAnalyse(sourceCode)); } BOOST_AUTO_TEST_CASE(string_index) @@ -1808,7 +1849,7 @@ BOOST_AUTO_TEST_CASE(string_index) function f() { var a = s[2]; } } )"; - BOOST_CHECK_THROW(parseTextAndResolveNames(sourceCode), TypeError); + SOLIDITY_CHECK_ERROR_TYPE(parseAndAnalyseReturnError(sourceCode), TypeError); } BOOST_AUTO_TEST_CASE(string_length) @@ -1819,7 +1860,7 @@ BOOST_AUTO_TEST_CASE(string_length) function f() { var a = s.length; } } )"; - BOOST_CHECK_THROW(parseTextAndResolveNames(sourceCode), TypeError); + SOLIDITY_CHECK_ERROR_TYPE(parseAndAnalyseReturnError(sourceCode), TypeError); } BOOST_AUTO_TEST_CASE(negative_integers_to_signed_out_of_bound) @@ -1829,7 +1870,7 @@ BOOST_AUTO_TEST_CASE(negative_integers_to_signed_out_of_bound) int8 public i = -129; } )"; - BOOST_CHECK_THROW(parseTextAndResolveNames(sourceCode), TypeError); + SOLIDITY_CHECK_ERROR_TYPE(parseAndAnalyseReturnError(sourceCode), TypeError); } BOOST_AUTO_TEST_CASE(negative_integers_to_signed_min) @@ -1839,7 +1880,7 @@ BOOST_AUTO_TEST_CASE(negative_integers_to_signed_min) int8 public i = -128; } )"; - BOOST_CHECK_NO_THROW(parseTextAndResolveNames(sourceCode)); + BOOST_CHECK_NO_THROW(parseAndAnalyse(sourceCode)); } BOOST_AUTO_TEST_CASE(positive_integers_to_signed_out_of_bound) @@ -1849,7 +1890,7 @@ BOOST_AUTO_TEST_CASE(positive_integers_to_signed_out_of_bound) int8 public j = 128; } )"; - BOOST_CHECK_THROW(parseTextAndResolveNames(sourceCode), TypeError); + SOLIDITY_CHECK_ERROR_TYPE(parseAndAnalyseReturnError(sourceCode), TypeError); } BOOST_AUTO_TEST_CASE(positive_integers_to_signed_out_of_bound_max) @@ -1859,7 +1900,7 @@ BOOST_AUTO_TEST_CASE(positive_integers_to_signed_out_of_bound_max) int8 public j = 127; } )"; - BOOST_CHECK_NO_THROW(parseTextAndResolveNames(sourceCode)); + BOOST_CHECK_NO_THROW(parseAndAnalyse(sourceCode)); } BOOST_AUTO_TEST_CASE(negative_integers_to_unsigned) @@ -1869,7 +1910,7 @@ BOOST_AUTO_TEST_CASE(negative_integers_to_unsigned) uint8 public x = -1; } )"; - BOOST_CHECK_THROW(parseTextAndResolveNames(sourceCode), TypeError); + SOLIDITY_CHECK_ERROR_TYPE(parseAndAnalyseReturnError(sourceCode), TypeError); } BOOST_AUTO_TEST_CASE(positive_integers_to_unsigned_out_of_bound) @@ -1879,7 +1920,7 @@ BOOST_AUTO_TEST_CASE(positive_integers_to_unsigned_out_of_bound) uint8 public x = 700; } )"; - BOOST_CHECK_THROW(parseTextAndResolveNames(sourceCode), TypeError); + SOLIDITY_CHECK_ERROR_TYPE(parseAndAnalyseReturnError(sourceCode), TypeError); } BOOST_AUTO_TEST_CASE(integer_boolean_operators) @@ -1887,15 +1928,15 @@ BOOST_AUTO_TEST_CASE(integer_boolean_operators) char const* sourceCode1 = R"( contract test { function() { uint x = 1; uint y = 2; x || y; } } )"; - BOOST_CHECK_THROW(parseTextAndResolveNames(sourceCode1), TypeError); + SOLIDITY_CHECK_ERROR_TYPE(parseAndAnalyseReturnError(sourceCode1), TypeError); char const* sourceCode2 = R"( contract test { function() { uint x = 1; uint y = 2; x && y; } } )"; - BOOST_CHECK_THROW(parseTextAndResolveNames(sourceCode2), TypeError); + SOLIDITY_CHECK_ERROR_TYPE(parseAndAnalyseReturnError(sourceCode2), TypeError); char const* sourceCode3 = R"( contract test { function() { uint x = 1; !x; } } )"; - BOOST_CHECK_THROW(parseTextAndResolveNames(sourceCode3), TypeError); + SOLIDITY_CHECK_ERROR_TYPE(parseAndAnalyseReturnError(sourceCode3), TypeError); } BOOST_AUTO_TEST_CASE(reference_compare_operators) @@ -1903,11 +1944,11 @@ BOOST_AUTO_TEST_CASE(reference_compare_operators) char const* sourceCode1 = R"( contract test { bytes a; bytes b; function() { a == b; } } )"; - BOOST_CHECK_THROW(parseTextAndResolveNames(sourceCode1), TypeError); + SOLIDITY_CHECK_ERROR_TYPE(parseAndAnalyseReturnError(sourceCode1), TypeError); char const* sourceCode2 = R"( contract test { struct s {uint a;} s x; s y; function() { x == y; } } )"; - BOOST_CHECK_THROW(parseTextAndResolveNames(sourceCode2), TypeError); + SOLIDITY_CHECK_ERROR_TYPE(parseAndAnalyseReturnError(sourceCode2), TypeError); } BOOST_AUTO_TEST_CASE(overwrite_memory_location_external) @@ -1917,7 +1958,7 @@ BOOST_AUTO_TEST_CASE(overwrite_memory_location_external) function f(uint[] memory a) external {} } )"; - BOOST_CHECK_THROW(parseTextAndResolveNames(sourceCode), TypeError); + SOLIDITY_CHECK_ERROR_TYPE(parseAndAnalyseReturnError(sourceCode), TypeError); } BOOST_AUTO_TEST_CASE(overwrite_storage_location_external) @@ -1927,7 +1968,7 @@ BOOST_AUTO_TEST_CASE(overwrite_storage_location_external) function f(uint[] storage a) external {} } )"; - BOOST_CHECK_THROW(parseTextAndResolveNames(sourceCode), TypeError); + SOLIDITY_CHECK_ERROR_TYPE(parseAndAnalyseReturnError(sourceCode), TypeError); } BOOST_AUTO_TEST_CASE(storage_location_local_variables) @@ -1941,7 +1982,7 @@ BOOST_AUTO_TEST_CASE(storage_location_local_variables) } } )"; - BOOST_CHECK_NO_THROW(parseTextAndResolveNames(sourceCode)); + BOOST_CHECK_NO_THROW(parseAndAnalyse(sourceCode)); } BOOST_AUTO_TEST_CASE(no_mappings_in_memory_array) @@ -1953,7 +1994,7 @@ BOOST_AUTO_TEST_CASE(no_mappings_in_memory_array) } } )"; - BOOST_CHECK_THROW(parseTextAndResolveNames(sourceCode), TypeError); + SOLIDITY_CHECK_ERROR_TYPE(parseAndAnalyseReturnError(sourceCode), TypeError); } BOOST_AUTO_TEST_CASE(assignment_mem_to_local_storage_variable) @@ -1967,7 +2008,7 @@ BOOST_AUTO_TEST_CASE(assignment_mem_to_local_storage_variable) } } )"; - BOOST_CHECK_THROW(parseTextAndResolveNames(sourceCode), TypeError); + SOLIDITY_CHECK_ERROR_TYPE(parseAndAnalyseReturnError(sourceCode), TypeError); } BOOST_AUTO_TEST_CASE(storage_assign_to_different_local_variable) @@ -1984,7 +2025,7 @@ BOOST_AUTO_TEST_CASE(storage_assign_to_different_local_variable) } } )"; - BOOST_CHECK_THROW(parseTextAndResolveNames(sourceCode), TypeError); + SOLIDITY_CHECK_ERROR_TYPE(parseAndAnalyseReturnError(sourceCode), TypeError); } BOOST_AUTO_TEST_CASE(no_delete_on_storage_pointers) @@ -1998,7 +2039,7 @@ BOOST_AUTO_TEST_CASE(no_delete_on_storage_pointers) } } )"; - BOOST_CHECK_THROW(parseTextAndResolveNames(sourceCode), TypeError); + SOLIDITY_CHECK_ERROR_TYPE(parseAndAnalyseReturnError(sourceCode), TypeError); } BOOST_AUTO_TEST_CASE(assignment_mem_storage_variable_directly) @@ -2011,7 +2052,7 @@ BOOST_AUTO_TEST_CASE(assignment_mem_storage_variable_directly) } } )"; - BOOST_CHECK_NO_THROW(parseTextAndResolveNames(sourceCode)); + BOOST_CHECK_NO_THROW(parseAndAnalyse(sourceCode)); } BOOST_AUTO_TEST_CASE(function_argument_mem_to_storage) @@ -2025,7 +2066,7 @@ BOOST_AUTO_TEST_CASE(function_argument_mem_to_storage) } } )"; - BOOST_CHECK_THROW(parseTextAndResolveNames(sourceCode), TypeError); + SOLIDITY_CHECK_ERROR_TYPE(parseAndAnalyseReturnError(sourceCode), TypeError); } BOOST_AUTO_TEST_CASE(function_argument_storage_to_mem) @@ -2039,7 +2080,7 @@ BOOST_AUTO_TEST_CASE(function_argument_storage_to_mem) } } )"; - BOOST_CHECK_NO_THROW(parseTextAndResolveNames(sourceCode)); + BOOST_CHECK_NO_THROW(parseAndAnalyse(sourceCode)); } BOOST_AUTO_TEST_CASE(mem_array_assignment_changes_base_type) @@ -2054,7 +2095,7 @@ BOOST_AUTO_TEST_CASE(mem_array_assignment_changes_base_type) } } )"; - BOOST_CHECK_THROW(parseTextAndResolveNames(sourceCode), TypeError); + SOLIDITY_CHECK_ERROR_TYPE(parseAndAnalyseReturnError(sourceCode), TypeError); } BOOST_AUTO_TEST_CASE(dynamic_return_types_not_possible) @@ -2067,7 +2108,7 @@ BOOST_AUTO_TEST_CASE(dynamic_return_types_not_possible) } } )"; - BOOST_CHECK_THROW(parseTextAndResolveNames(sourceCode), TypeError); + SOLIDITY_CHECK_ERROR_TYPE(parseAndAnalyseReturnError(sourceCode), TypeError); } BOOST_AUTO_TEST_CASE(memory_arrays_not_resizeable) @@ -2080,7 +2121,7 @@ BOOST_AUTO_TEST_CASE(memory_arrays_not_resizeable) } } )"; - BOOST_CHECK_THROW(parseTextAndResolveNames(sourceCode), TypeError); + SOLIDITY_CHECK_ERROR_TYPE(parseAndAnalyseReturnError(sourceCode), TypeError); } BOOST_AUTO_TEST_CASE(struct_constructor) @@ -2093,7 +2134,7 @@ BOOST_AUTO_TEST_CASE(struct_constructor) } } )"; - BOOST_CHECK_NO_THROW(parseTextAndResolveNames(sourceCode)); + BOOST_CHECK_NO_THROW(parseAndAnalyse(sourceCode)); } BOOST_AUTO_TEST_CASE(struct_constructor_nested) @@ -2108,7 +2149,7 @@ BOOST_AUTO_TEST_CASE(struct_constructor_nested) } } )"; - BOOST_CHECK_NO_THROW(parseTextAndResolveNames(sourceCode)); + BOOST_CHECK_NO_THROW(parseAndAnalyse(sourceCode)); } BOOST_AUTO_TEST_CASE(struct_named_constructor) @@ -2121,7 +2162,7 @@ BOOST_AUTO_TEST_CASE(struct_named_constructor) } } )"; - BOOST_CHECK_NO_THROW(parseTextAndResolveNames(sourceCode)); + BOOST_CHECK_NO_THROW(parseAndAnalyse(sourceCode)); } BOOST_AUTO_TEST_CASE(literal_strings) @@ -2134,7 +2175,7 @@ BOOST_AUTO_TEST_CASE(literal_strings) } } )"; - BOOST_CHECK_NO_THROW(parseTextAndResolveNames(text)); + BOOST_CHECK_NO_THROW(parseAndAnalyse(text)); } BOOST_AUTO_TEST_CASE(invalid_integer_literal_fraction) @@ -2146,7 +2187,7 @@ BOOST_AUTO_TEST_CASE(invalid_integer_literal_fraction) } } )"; - BOOST_CHECK_THROW(parseTextAndResolveNames(text), TypeError); + SOLIDITY_CHECK_ERROR_TYPE(parseAndAnalyseReturnError(text), TypeError); } BOOST_AUTO_TEST_CASE(invalid_integer_literal_exp) @@ -2158,7 +2199,7 @@ BOOST_AUTO_TEST_CASE(invalid_integer_literal_exp) } } )"; - BOOST_CHECK_THROW(parseTextAndResolveNames(text), TypeError); + SOLIDITY_CHECK_ERROR_TYPE(parseAndAnalyseReturnError(text), TypeError); } BOOST_AUTO_TEST_CASE(memory_structs_with_mappings) @@ -2173,7 +2214,7 @@ BOOST_AUTO_TEST_CASE(memory_structs_with_mappings) } } )"; - BOOST_CHECK_THROW(parseTextAndResolveNames(text), TypeError); + SOLIDITY_CHECK_ERROR_TYPE(parseAndAnalyseReturnError(text), TypeError); } BOOST_AUTO_TEST_CASE(string_bytes_conversion) @@ -2190,7 +2231,7 @@ BOOST_AUTO_TEST_CASE(string_bytes_conversion) function m() internal { string(b); } } )"; - BOOST_CHECK_NO_THROW(parseTextAndResolveNames(text)); + BOOST_CHECK_NO_THROW(parseAndAnalyse(text)); } BOOST_AUTO_TEST_CASE(inheriting_from_library) @@ -2199,7 +2240,7 @@ BOOST_AUTO_TEST_CASE(inheriting_from_library) library Lib {} contract Test is Lib {} )"; - BOOST_CHECK_THROW(parseTextAndResolveNames(text), TypeError); + SOLIDITY_CHECK_ERROR_TYPE(parseAndAnalyseReturnError(text), TypeError); } BOOST_AUTO_TEST_CASE(inheriting_library) @@ -2208,7 +2249,7 @@ BOOST_AUTO_TEST_CASE(inheriting_library) contract Test {} library Lib is Test {} )"; - BOOST_CHECK_THROW(parseTextAndResolveNames(text), TypeError); + SOLIDITY_CHECK_ERROR_TYPE(parseAndAnalyseReturnError(text), TypeError); } BOOST_AUTO_TEST_CASE(library_having_variables) @@ -2216,7 +2257,7 @@ BOOST_AUTO_TEST_CASE(library_having_variables) char const* text = R"( library Lib { uint x; } )"; - BOOST_CHECK_THROW(parseTextAndResolveNames(text), TypeError); + SOLIDITY_CHECK_ERROR_TYPE(parseAndAnalyseReturnError(text), TypeError); } BOOST_AUTO_TEST_CASE(valid_library) @@ -2224,7 +2265,7 @@ BOOST_AUTO_TEST_CASE(valid_library) char const* text = R"( library Lib { uint constant x = 9; } )"; - BOOST_CHECK_NO_THROW(parseTextAndResolveNames(text)); + BOOST_CHECK_NO_THROW(parseAndAnalyse(text)); } BOOST_AUTO_TEST_CASE(call_to_library_function) @@ -2240,7 +2281,7 @@ BOOST_AUTO_TEST_CASE(call_to_library_function) } } )"; - BOOST_CHECK_NO_THROW(parseTextAndResolveNames(text)); + BOOST_CHECK_NO_THROW(parseAndAnalyse(text)); } BOOST_AUTO_TEST_CASE(creating_contract_within_the_contract) @@ -2250,7 +2291,7 @@ BOOST_AUTO_TEST_CASE(creating_contract_within_the_contract) function f() { var x = new Test(); } } )"; - BOOST_CHECK_THROW(parseTextAndResolveNames(sourceCode), TypeError); + SOLIDITY_CHECK_ERROR_TYPE(parseAndAnalyseReturnError(sourceCode), TypeError); } BOOST_AUTO_TEST_CASE(array_out_of_bound_access) @@ -2264,7 +2305,7 @@ BOOST_AUTO_TEST_CASE(array_out_of_bound_access) } } )"; - BOOST_CHECK_THROW(parseTextAndResolveNames(text), TypeError); + SOLIDITY_CHECK_ERROR_TYPE(parseAndAnalyseReturnError(text), TypeError); } BOOST_AUTO_TEST_SUITE_END() |