diff options
author | chriseth <c@ethdev.com> | 2015-04-15 23:40:50 +0800 |
---|---|---|
committer | chriseth <c@ethdev.com> | 2015-04-15 23:40:50 +0800 |
commit | 0c69d5fdcd3286f47c81dbcbcfb8802861eab8b5 (patch) | |
tree | f78c665e6084300b939cc4cfcebb97d3bd44d9f5 /AST.cpp | |
parent | 158795e48f4285d713b11f78cdd04de8c6d1f667 (diff) | |
download | dexon-solidity-0c69d5fdcd3286f47c81dbcbcfb8802861eab8b5.tar.gz dexon-solidity-0c69d5fdcd3286f47c81dbcbcfb8802861eab8b5.tar.zst dexon-solidity-0c69d5fdcd3286f47c81dbcbcfb8802861eab8b5.zip |
Fixed function overloads.
Added tests, disallowed non-calling usage of non-unique function
references.
Diffstat (limited to 'AST.cpp')
-rw-r--r-- | AST.cpp | 330 |
1 files changed, 181 insertions, 149 deletions
@@ -52,6 +52,7 @@ void ContractDefinition::checkTypeRequirements() for (ASTPointer<InheritanceSpecifier> const& baseSpecifier: getBaseContracts()) baseSpecifier->checkTypeRequirements(); + checkDuplicateFunctions(); checkIllegalOverrides(); checkAbstractFunctions(); @@ -82,20 +83,11 @@ void ContractDefinition::checkTypeRequirements() for (ASTPointer<FunctionDefinition> const& function: getDefinedFunctions()) function->checkTypeRequirements(); - // check for duplicate declaration - set<string> functions; - for (ASTPointer<FunctionDefinition> const& function: getDefinedFunctions()) - { - string signature = function->getCanonicalSignature(); - if (functions.count(signature)) - BOOST_THROW_EXCEPTION(DeclarationError() << errinfo_sourceLocation(function->getLocation()) - << errinfo_comment("Duplicate functions are not allowed.")); - functions.insert(signature); - } for (ASTPointer<VariableDeclaration> const& variable: m_stateVariables) variable->checkTypeRequirements(); + checkExternalTypeClashes(); // check for hash collisions in function signatures set<FixedHash<4>> hashes; for (auto const& it: getInterfaceFunctionList()) @@ -140,6 +132,33 @@ FunctionDefinition const* ContractDefinition::getFallbackFunction() const 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: getDefinedFunctions()) + functions[function->getName()].push_back(function.get()); + if (functions[getName()].size() > 1) + BOOST_THROW_EXCEPTION( + DeclarationError() << + errinfo_sourceLocation(getLocation()) << + errinfo_comment("More than one constructor defined.") + ); + 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]->getLocation()) << + errinfo_comment("Function with same name and arguments already defined.") + ); + } +} + void ContractDefinition::checkAbstractFunctions() { map<string, bool> functions; @@ -166,8 +185,7 @@ 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, FunctionDefinition const*> functions; - set<string> functionNames; + map<string, vector<FunctionDefinition const*>> functions; map<string, ModifierDefinition const*> modifiers; // We search from derived to base, so the stored item causes the error. @@ -180,14 +198,21 @@ void ContractDefinition::checkIllegalOverrides() const string const& name = function->getName(); if (modifiers.count(name)) BOOST_THROW_EXCEPTION(modifiers[name]->createTypeError("Override changes function to modifier.")); - FunctionDefinition const*& override = functions[function->getCanonicalSignature()]; - functionNames.insert(name); - if (!override) - override = function.get(); - else if (override->getVisibility() != function->getVisibility() || - override->isDeclaredConst() != function->isDeclaredConst() || - FunctionType(*override) != FunctionType(*function)) - BOOST_THROW_EXCEPTION(override->createTypeError("Override changes extended function signature.")); + 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->getVisibility() != function->getVisibility() || + 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->getFunctionModifiers()) { @@ -197,12 +222,43 @@ void ContractDefinition::checkIllegalOverrides() const override = modifier.get(); else if (ModifierType(*override) != ModifierType(*modifier)) BOOST_THROW_EXCEPTION(override->createTypeError("Override changes modifier signature.")); - if (functionNames.count(name)) + 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: getLinearizedBaseContracts()) + { + for (ASTPointer<FunctionDefinition> const& f: contract->getDefinedFunctions()) + if (f->isPartOfExternalInterface()) + { + auto functionType = make_shared<FunctionType>(*f); + externalDeclarations[functionType->externalSignature(f->getName())].push_back( + make_pair(f.get(), functionType) + ); + } + for (ASTPointer<VariableDeclaration> const& v: contract->getStateVariables()) + if (v->isPartOfExternalInterface()) + { + auto functionType = make_shared<FunctionType>(*v); + externalDeclarations[functionType->externalSignature(v->getName())].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." + )); +} + std::vector<ASTPointer<EventDefinition>> const& ContractDefinition::getInterfaceEvents() const { if (!m_interfaceEvents) @@ -291,11 +347,11 @@ TypePointer EnumValue::getType(ContractDefinition const*) const void InheritanceSpecifier::checkTypeRequirements() { - m_baseName->checkTypeRequirements(); + m_baseName->checkTypeRequirements(nullptr); for (ASTPointer<Expression> const& argument: m_arguments) - argument->checkTypeRequirements(); + argument->checkTypeRequirements(nullptr); - ContractDefinition const* base = dynamic_cast<ContractDefinition const*>(m_baseName->getReferencedDeclaration()); + ContractDefinition const* base = dynamic_cast<ContractDefinition const*>(&m_baseName->getReferencedDeclaration()); solAssert(base, "Base contract not available."); TypePointers parameterTypes = ContractType(*base).getConstructorType()->getParameterTypes(); if (parameterTypes.size() != m_arguments.size()) @@ -409,11 +465,7 @@ void VariableDeclaration::checkTypeRequirements() else { // no type declared and no previous assignment, infer the type - Identifier* identifier = dynamic_cast<Identifier*>(m_value.get()); - if (identifier) - identifier->checkTypeRequirementsFromVariableDeclaration(); - else - m_value->checkTypeRequirements(); + m_value->checkTypeRequirements(nullptr); TypePointer type = m_value->getType(); if (type->getCategory() == Type::Category::IntegerConstant) @@ -452,11 +504,15 @@ void ModifierDefinition::checkTypeRequirements() void ModifierInvocation::checkTypeRequirements(vector<ASTPointer<InheritanceSpecifier>> const& _bases) { - m_modifierName->checkTypeRequirements(); + TypePointers argumentTypes; for (ASTPointer<Expression> const& argument: m_arguments) - argument->checkTypeRequirements(); + { + argument->checkTypeRequirements(nullptr); + argumentTypes.push_back(argument->getType()); + } + m_modifierName->checkTypeRequirements(&argumentTypes); - auto declaration = m_modifierName->getReferencedDeclaration(); + auto const* declaration = &m_modifierName->getReferencedDeclaration(); vector<ASTPointer<VariableDeclaration>> emptyParameterList; vector<ASTPointer<VariableDeclaration>> const* parameters = nullptr; if (auto modifier = dynamic_cast<ModifierDefinition const*>(declaration)) @@ -464,7 +520,7 @@ void ModifierInvocation::checkTypeRequirements(vector<ASTPointer<InheritanceSpec else // check parameters for Base constructors for (auto const& base: _bases) - if (declaration == base->getName()->getReferencedDeclaration()) + if (declaration == &base->getName()->getReferencedDeclaration()) { if (auto referencedConstructor = dynamic_cast<ContractDefinition const&>(*declaration).getConstructor()) parameters = &referencedConstructor->getParameters(); @@ -547,9 +603,9 @@ void VariableDeclarationStatement::checkTypeRequirements() m_variable->checkTypeRequirements(); } -void Assignment::checkTypeRequirements() +void Assignment::checkTypeRequirements(TypePointers const*) { - m_leftHandSide->checkTypeRequirements(); + m_leftHandSide->checkTypeRequirements(nullptr); m_leftHandSide->requireLValue(); if (m_leftHandSide->getType()->getCategory() == Type::Category::Mapping) BOOST_THROW_EXCEPTION(createTypeError("Mappings cannot be assigned to.")); @@ -559,7 +615,7 @@ void Assignment::checkTypeRequirements() else { // compound assignment - m_rightHandSide->checkTypeRequirements(); + m_rightHandSide->checkTypeRequirements(nullptr); TypePointer resultType = m_type->binaryOperatorResult(Token::AssignmentToBinaryOp(m_assigmentOperator), m_rightHandSide->getType()); if (!resultType || *resultType != *m_type) @@ -572,7 +628,7 @@ void Assignment::checkTypeRequirements() void ExpressionStatement::checkTypeRequirements() { - m_expression->checkTypeRequirements(); + m_expression->checkTypeRequirements(nullptr); if (m_expression->getType()->getCategory() == Type::Category::IntegerConstant) if (!dynamic_pointer_cast<IntegerConstantType const>(m_expression->getType())->getIntegerType()) BOOST_THROW_EXCEPTION(m_expression->createTypeError("Invalid integer constant.")); @@ -580,7 +636,7 @@ void ExpressionStatement::checkTypeRequirements() void Expression::expectType(Type const& _expectedType) { - checkTypeRequirements(); + checkTypeRequirements(nullptr); Type const& type = *getType(); if (!type.isImplicitlyConvertibleTo(_expectedType)) BOOST_THROW_EXCEPTION(createTypeError("Type " + type.toString() + @@ -595,10 +651,10 @@ void Expression::requireLValue() m_lvalueRequested = true; } -void UnaryOperation::checkTypeRequirements() +void UnaryOperation::checkTypeRequirements(TypePointers const*) { // Inc, Dec, Add, Sub, Not, BitNot, Delete - m_subExpression->checkTypeRequirements(); + 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->getType()->unaryOperatorResult(m_operator); @@ -606,10 +662,10 @@ void UnaryOperation::checkTypeRequirements() BOOST_THROW_EXCEPTION(createTypeError("Unary operator not compatible with type.")); } -void BinaryOperation::checkTypeRequirements() +void BinaryOperation::checkTypeRequirements(TypePointers const*) { - m_left->checkTypeRequirements(); - m_right->checkTypeRequirements(); + m_left->checkTypeRequirements(nullptr); + m_right->checkTypeRequirements(nullptr); m_commonType = m_left->getType()->binaryOperatorResult(m_operator, m_right->getType()); if (!m_commonType) BOOST_THROW_EXCEPTION(createTypeError("Operator " + string(Token::toString(m_operator)) + @@ -619,17 +675,22 @@ void BinaryOperation::checkTypeRequirements() m_type = Token::isCompareOp(m_operator) ? make_shared<BoolType>() : m_commonType; } -void FunctionCall::checkTypeRequirements() +void FunctionCall::checkTypeRequirements(TypePointers const*) { - // we need to check arguments' type first as their info will be used by m_express(Identifier). + 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(); + { + argument->checkTypeRequirements(nullptr); + // only store them for positional calls + if (isPositionalCall) + argumentTypes.push_back(argument->getType()); + } - auto identifier = dynamic_cast<Identifier*>(m_expression.get()); - if (identifier) - identifier->checkTypeRequirementsWithFunctionCall(*this); - else - m_expression->checkTypeRequirements(); + m_expression->checkTypeRequirements(isPositionalCall ? &argumentTypes : nullptr); Type const* expressionType = m_expression->getType().get(); if (isTypeConversion()) @@ -639,7 +700,7 @@ void FunctionCall::checkTypeRequirements() // number of non-mapping members if (m_arguments.size() != 1) BOOST_THROW_EXCEPTION(createTypeError("More than one argument for explicit type conversion.")); - if (!m_names.empty()) + if (!isPositionalCall) BOOST_THROW_EXCEPTION(createTypeError("Type conversion cannot allow named arguments.")); if (!m_arguments.front()->getType()->isExplicitlyConvertibleTo(*type.getActualType())) BOOST_THROW_EXCEPTION(createTypeError("Explicit type conversion not allowed.")); @@ -654,8 +715,9 @@ void FunctionCall::checkTypeRequirements() if (!functionType->takesArbitraryParameters() && parameterTypes.size() != m_arguments.size()) BOOST_THROW_EXCEPTION(createTypeError("Wrong argument count for function call.")); - if (m_names.empty()) + if (isPositionalCall) { + // call by positional arguments for (size_t i = 0; i < m_arguments.size(); ++i) if (!functionType->takesArbitraryParameters() && !m_arguments[i]->getType()->isImplicitlyConvertibleTo(*parameterTypes[i])) @@ -663,6 +725,7 @@ void FunctionCall::checkTypeRequirements() } else { + // call by named arguments if (functionType->takesArbitraryParameters()) BOOST_THROW_EXCEPTION(createTypeError("Named arguments cannnot be used for functions " "that take arbitrary parameters.")); @@ -700,27 +763,6 @@ void FunctionCall::checkTypeRequirements() else m_type = functionType->getReturnParameterTypes().front(); } - else if (OverloadedFunctionType const* overloadedTypes = dynamic_cast<OverloadedFunctionType const*>(expressionType)) - { - // this only applies to "x(3)" where x is assigned by "var x = f;" where f is an overloaded functions. - auto identifier = dynamic_cast<Identifier*>(m_expression.get()); - solAssert(identifier, "only applies to 'var x = f;'"); - - Declaration const* function = overloadedTypes->getIdentifier()->overloadResolution(*this); - if (!function) - BOOST_THROW_EXCEPTION(createTypeError("Can't resolve declarations")); - - identifier->setReferencedDeclaration(*function); - identifier->checkTypeRequirements(); - - TypePointer type = identifier->getType(); - FunctionType const* functionType = dynamic_cast<FunctionType const*>(type.get()); - - if (functionType->getReturnParameterTypes().empty()) - m_type = make_shared<VoidType>(); - else - m_type = functionType->getReturnParameterTypes().front(); - } else BOOST_THROW_EXCEPTION(createTypeError("Type is not callable.")); } @@ -730,10 +772,10 @@ bool FunctionCall::isTypeConversion() const return m_expression->getType()->getCategory() == Type::Category::TypeType; } -void NewExpression::checkTypeRequirements() +void NewExpression::checkTypeRequirements(TypePointers const*) { - m_contractName->checkTypeRequirements(); - m_contract = dynamic_cast<ContractDefinition const*>(m_contractName->getReferencedDeclaration()); + m_contractName->checkTypeRequirements(nullptr); + m_contract = dynamic_cast<ContractDefinition const*>(&m_contractName->getReferencedDeclaration()); if (!m_contract) BOOST_THROW_EXCEPTION(createTypeError("Identifier is not a contract.")); if (!m_contract->isFullyImplemented()) @@ -744,15 +786,37 @@ void NewExpression::checkTypeRequirements() FunctionType::Location::Creation); } -void MemberAccess::checkTypeRequirements() +void MemberAccess::checkTypeRequirements(TypePointers const* _argumentTypes) { - m_expression->checkTypeRequirements(); + m_expression->checkTypeRequirements(nullptr); Type const& type = *m_expression->getType(); - m_type = type.getMemberType(*m_memberName); - if (!m_type) - BOOST_THROW_EXCEPTION(createTypeError("Member \"" + *m_memberName + "\" not found or not " - "visible in " + type.toString())); - // This should probably move somewhere else. + + MemberList::MemberMap possibleMembers = type.getMembers().membersByName(*m_memberName); + if (possibleMembers.size() > 1 && _argumentTypes) + { + // do override resolution + for (auto it = possibleMembers.begin(); it != possibleMembers.end();) + if ( + it->type->getCategory() == Type::Category::Function && + !dynamic_cast<FunctionType const&>(*it->type).canTakeArguments(*_argumentTypes) + ) + it = possibleMembers.erase(it); + else + ++it; + } + if (possibleMembers.size() == 0) + 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.getCategory() == Type::Category::Struct) m_isLValue = true; else if (type.getCategory() == Type::Category::Array) @@ -765,9 +829,9 @@ void MemberAccess::checkTypeRequirements() m_isLValue = false; } -void IndexAccess::checkTypeRequirements() +void IndexAccess::checkTypeRequirements(TypePointers const*) { - m_base->checkTypeRequirements(); + m_base->checkTypeRequirements(nullptr); switch (m_base->getType()->getCategory()) { case Type::Category::Array: @@ -800,7 +864,7 @@ void IndexAccess::checkTypeRequirements() m_type = make_shared<TypeType>(make_shared<ArrayType>(ArrayType::Location::Memory, type.getActualType())); else { - m_index->checkTypeRequirements(); + m_index->checkTypeRequirements(nullptr); auto length = dynamic_cast<IntegerConstantType const*>(m_index->getType().get()); if (!length) BOOST_THROW_EXCEPTION(m_index->createTypeError("Integer constant expected.")); @@ -815,89 +879,57 @@ void IndexAccess::checkTypeRequirements() } } -void Identifier::checkTypeRequirementsWithFunctionCall(FunctionCall const& _functionCall) -{ - solAssert(m_referencedDeclaration || !m_overloadedDeclarations.empty(), "Identifier not resolved."); - - if (!m_referencedDeclaration) - setReferencedDeclaration(*overloadResolution(_functionCall)); - - checkTypeRequirements(); -} - -void Identifier::checkTypeRequirementsFromVariableDeclaration() +void Identifier::checkTypeRequirements(TypePointers const* _argumentTypes) { - solAssert(m_referencedDeclaration || !m_overloadedDeclarations.empty(), "Identifier not resolved."); - if (!m_referencedDeclaration) - m_type = make_shared<OverloadedFunctionType>(this); - else - checkTypeRequirements(); - - m_isLValue = true; -} - -void Identifier::checkTypeRequirements() -{ - solAssert(m_referencedDeclaration, "Identifier not resolved."); - + { + 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->getType(m_currentContract); if (!m_type) BOOST_THROW_EXCEPTION(createTypeError("Declaration referenced before type could be determined.")); } -Declaration const* Identifier::overloadResolution(FunctionCall const& _functionCall) +Declaration const& Identifier::getReferencedDeclaration() const +{ + solAssert(!!m_referencedDeclaration, "Identifier not resolved."); + return *m_referencedDeclaration; +} + +void Identifier::overloadResolution(TypePointers const& _argumentTypes) { - solAssert(m_overloadedDeclarations.size() > 1, "FunctionIdentifier not resolved."); solAssert(!m_referencedDeclaration, "Referenced declaration should be null before overload resolution."); + solAssert(!m_overloadedDeclarations.empty(), "No candidates for overload resolution found."); - std::vector<ASTPointer<Expression const>> arguments = _functionCall.getArguments(); - std::vector<ASTPointer<ASTString>> const& argumentNames = _functionCall.getNames(); + std::vector<Declaration const*> possibles; + if (m_overloadedDeclarations.size() == 1) + m_referencedDeclaration = *m_overloadedDeclarations.begin(); - if (argumentNames.empty()) + for (Declaration const* declaration: m_overloadedDeclarations) { - // positional arguments - std::vector<Declaration const*> possibles; - for (Declaration const* declaration: m_overloadedDeclarations) - { - TypePointer const& function = declaration->getType(); - auto const& functionType = dynamic_cast<FunctionType const&>(*function); - TypePointers const& parameterTypes = functionType.getParameterTypes(); - - if (functionType.takesArbitraryParameters() || - (arguments.size() == parameterTypes.size() && - std::equal(arguments.cbegin(), arguments.cend(), parameterTypes.cbegin(), - [](ASTPointer<Expression const> const& argument, TypePointer const& parameterType) - { - return argument->getType()->isImplicitlyConvertibleTo(*parameterType); - }))) - possibles.push_back(declaration); - } - if (possibles.empty()) - BOOST_THROW_EXCEPTION(createTypeError("Can't resolve identifier")); - else if (std::none_of(possibles.cbegin() + 1, possibles.cend(), - [&possibles](Declaration const* declaration) - { - return declaration->getScope() == possibles.front()->getScope(); - })) - return possibles.front(); - else - BOOST_THROW_EXCEPTION(createTypeError("Can't resolve identifier")); + TypePointer const& function = declaration->getType(); + 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 - // named arguments - // TODO: don't support right now - BOOST_THROW_EXCEPTION(createTypeError("Named arguments with overloaded functions are not supported yet.")); - return nullptr; + BOOST_THROW_EXCEPTION(createTypeError("No unique declaration found after argument-dependent lookup.")); } -void ElementaryTypeNameExpression::checkTypeRequirements() +void ElementaryTypeNameExpression::checkTypeRequirements(TypePointers const*) { m_type = make_shared<TypeType>(Type::fromElementaryTypeName(m_typeToken)); } -void Literal::checkTypeRequirements() +void Literal::checkTypeRequirements(TypePointers const*) { m_type = Type::forLiteral(*this); if (!m_type) |