/* Copyright 2012 Olivier de Gaalon This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License version 2 as published by the Free Software Foundation. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "templateresolver.h" #include "cpptypes.h" #include "templatedeclaration.h" #include "templateparameterdeclaration.h" using namespace Cpp; using namespace KDevelop; static bool isConstBased(const AbstractType::Ptr& type) { if (type->modifiers() & AbstractType::ConstModifier) return true; if (ArrayType::Ptr arrayType = type.cast()) return arrayType->elementType() ? isConstBased(arrayType->elementType()) : false; if (PointerType::Ptr ptrType = type.cast()) return ptrType->baseType() ? isConstBased(ptrType->baseType()) : false; return false; } static bool isConstBased(const IndexedTypeIdentifier& type) { return type.isConstant(); } static bool isVolatileBased(const AbstractType::Ptr& type) { if (type->modifiers() & AbstractType::VolatileModifier) return true; if (ArrayType::Ptr arrayType = type.cast()) return arrayType->elementType() ? isVolatileBased(arrayType->elementType()) : false; if (PointerType::Ptr ptrType = type.cast()) return ptrType->baseType() ? isVolatileBased(ptrType->baseType()) : false; return false; } static bool isVolatileBased(const IndexedTypeIdentifier& type) { return type.isVolatile(); } template static bool matchCV(const A& parameterType, const B& argumentType, C* res) { if (isConstBased(parameterType)) { if (!argumentType.template cast() && isConstBased(argumentType)) res->constMatch = true; else { res->valid = false; return false; //Invalid, param is const and arg is either non-const or has a different ptr-depth } } if (isVolatileBased(parameterType)) { if (!argumentType.template cast() && isVolatileBased(argumentType)) res->volatileMatch = true; else { res->valid = false; return false; //Invalid, param is volatile and arg is either non-volatile or has a different ptr-depth } } return true; } TemplateResolver::TemplateResolver(const TopDUContext* topContext) :m_topContext(topContext) { } uint TemplateResolver::matchTemplateParameterTypes( const AbstractType::Ptr& argumentType, const AbstractType::Ptr& parameterType, QMap< IndexedString, AbstractType::Ptr >& instantiatedTypes ) const { if ( !argumentType && !parameterType ) return 1; if ( !argumentType || !parameterType ) return 0; TemplateMatchType matchResult; matchTemplateParameterTypesInternal(argumentType, parameterType, instantiatedTypes, matchResult); return matchResult.toUint(); } bool TemplateResolver::templateHandleConstIntegralType(const AbstractType::Ptr& argumentType, const AbstractType::Ptr& parameterType, TemplateMatchType& res) const { ConstantIntegralType::Ptr argumentIntegral = argumentType.cast(); ConstantIntegralType::Ptr parameterIntegral = parameterType.cast(); if (argumentIntegral && parameterIntegral) { if (argumentIntegral->plainValue() != parameterIntegral->plainValue()) res.valid = false; return true; //Handled, valid if integral types match, invalid otherwise } else if (parameterIntegral) { //Nothing but an equal integral will match an integral parameter res.valid = false; return true; //Handled, invalid. } else if (argumentIntegral && !parameterType.cast()) { res.valid = false; //A const integral arg can only match an equal const integral or replace a delayed type return true; //Handled, invalid } return false; } bool TemplateResolver::templateHandleDelayedType ( const AbstractType::Ptr& argumentType, const AbstractType::Ptr& parameterType, QMap< IndexedString, AbstractType::Ptr >& instantiatedTypes, TemplateMatchType& res ) const { DelayedType::Ptr delayed = parameterType.cast(); if ( !delayed ) return false; //Not delayed type, not handled IndexedTypeIdentifier paramDelayedId = delayed->identifier(); //Delayed id should never have pointer depth, or it would be a pointerType and not a delayedType //If it's possible somehow for it to be both, it's not correctly handled here Q_ASSERT(!paramDelayedId.pointerDepth()); matchCV(paramDelayedId, argumentType, &res); ///TODO: the code only uses the last identifier and used to verify that /// only one Identifier is actually contained in the QualifiedIdentifier /// in the paramDelayedId /// This caused issues for __gnu_cxx::_S_mutex, _S_single, _S_atomic etc. /// it's not clear whether this is actually a bug or not - someone should /// investigate. But rather don't assert for now! IndexedString identifier = paramDelayedId.identifier().identifier().last().identifier(); if ( instantiatedTypes.contains( identifier ) ) instantiatedTypes[identifier] = argumentType; else res.valid = false; return true; //Parameter was delayed type, delayed type handled } bool TemplateResolver::templateHandleReferenceType(const AbstractType::Ptr& argumentType, const AbstractType::Ptr& parameterType, QMap< IndexedString, AbstractType::Ptr >& instantiatedTypes, TemplateMatchType& res) const { ReferenceType::Ptr argumentRef = argumentType.cast(); ReferenceType::Ptr parameterRef = parameterType.cast(); if ( argumentRef && parameterRef && argumentRef->isRValue() == parameterRef->isRValue() ) { ///In case of references on both sides, match the target-types res.referenceMatch = true; matchTemplateParameterTypesInternal( argumentRef->baseType(), parameterRef->baseType(), instantiatedTypes, res ); return true; //Handled by matching base types } else if (argumentRef) { //Argument is a reference of some sort, but will match non-reference CppTemplateParameterType if (parameterType.cast()) matchTemplateParameterTypesInternal( argumentRef->baseType(), parameterType, instantiatedTypes, res); else res.valid = false; return true; //Handled by matching argument base type against template param, //or invalidated because argument is ref and param isn't } else if ( parameterRef ) { res.valid = false; return true; //Handled, invalid as the parameter is a reference type but the argument is not } return false; //No references, not handled } bool TemplateResolver::templateHandlePointerType(const AbstractType::Ptr& argumentType, const AbstractType::Ptr& parameterType, QMap< IndexedString, AbstractType::Ptr >& instantiatedTypes, TemplateMatchType& res) const { ///In case of pointers on both sides, match the target-types PointerType::Ptr argumentPointer = argumentType.cast(); PointerType::Ptr parameterPointer = parameterType.cast(); if ( argumentPointer && parameterPointer && (( argumentPointer->modifiers() & AbstractType::ConstModifier ) == ( parameterPointer->modifiers() & AbstractType::ConstModifier ) ) ) { ++res.pointerMatchDepth; matchTemplateParameterTypesInternal( argumentPointer->baseType(), parameterPointer->baseType(), instantiatedTypes, res ); return true; //Handled by matching base types } else if (argumentPointer) { if (!parameterPointer && !isConstBased(parameterType) && parameterType.cast()) { matchTemplateParameterTypesInternal( argumentPointer->baseType(), parameterType, instantiatedTypes, res ); return true; //Handled by matching argument base type } //If argument is a pointer (const or otherwise), it will not match a non-pointer const parameter //Even if parameter isn't const, unless it's a CppTemplateParameterType it won't match res.valid = false; return true; //Handled, invalid } else if (parameterPointer) { res.valid = false; return true; //Handled, invalid as parameter had addition unmatched pointer depth } return false; //Not handled, neither argument nor parameter are pointers } bool TemplateResolver::templateHandleArrayType(const AbstractType::Ptr& argumentType, const AbstractType::Ptr& parameterType, QMap< IndexedString, AbstractType::Ptr >& instantiatedTypes, TemplateMatchType& res) const { ArrayType::Ptr argumentArray = argumentType.cast(); ArrayType::Ptr parameterArray = parameterType.cast(); if ( argumentArray && parameterArray && ( argumentArray->modifiers() & (AbstractType::VolatileModifier | AbstractType::ConstModifier ) ) == ( parameterArray->modifiers() & (AbstractType::VolatileModifier | AbstractType::ConstModifier ) ) ) { if ( argumentArray->modifiers() & AbstractType::ConstModifier ) res.constMatch = true; if ( argumentArray->modifiers() & AbstractType::VolatileModifier ) res.volatileMatch = true; res.arrayMatch = true; matchTemplateParameterTypesInternal( argumentArray->elementType(), parameterArray->elementType(), instantiatedTypes, res ); return true; } else if (argumentArray) { //Argument type is array, but will match non-array CppTemplateParameterType if (parameterType.cast()) matchTemplateParameterTypesInternal( argumentArray->elementType(), parameterType, instantiatedTypes, res ); else res.valid = false; return true; //Handled, either by matching argument elementType or by invalidation because parameter type cannot match } else if (parameterArray) { res.valid = false; //Parameter is array type, argument must be array type return true; } return false; } bool TemplateResolver::templateHandleIdentifiedType(const AbstractType::Ptr& argumentType, const AbstractType::Ptr& parameterType, QMap< IndexedString, AbstractType::Ptr >& instantiatedTypes, TemplateMatchType& res) const { ///Match assigned template-parameters, for example when matching QList to QList, assign int to T. const IdentifiedType* identifiedArgument = dynamic_cast( argumentType.unsafeData() ); const IdentifiedType* identifiedParameter = dynamic_cast( parameterType.unsafeData() ); if ( identifiedArgument && identifiedParameter ) { Declaration* argumentDeclaration = identifiedArgument->declaration( m_topContext ); Declaration* parameterDeclaration = identifiedParameter->declaration( m_topContext ); if (!argumentDeclaration || !parameterDeclaration) { //TODO: Very rare case which may be a bug elsewhere and could use a test. return false; //Unhandled, let it be accepted or rejected later } TemplateDeclaration* argumentTemplateDeclaration = dynamic_cast( argumentDeclaration ); TemplateDeclaration* parameterTemplateDeclaration = dynamic_cast( parameterDeclaration ); if ( !argumentTemplateDeclaration || !parameterTemplateDeclaration ) { //Is this as correct as using the indexedType of the non-template declarations? if (argumentDeclaration != parameterDeclaration) res.valid = false; //Two different non-template declarations == two different types return true; //Handled, with either invalid mismatched types or valid matched types } if ( argumentTemplateDeclaration->instantiatedFrom() == parameterTemplateDeclaration->instantiatedFrom() && argumentTemplateDeclaration->instantiatedFrom() ) { InstantiationInformation argumentInstantiatedWith = argumentTemplateDeclaration->instantiatedWith().information(); InstantiationInformation parameterInstantiatedWith = parameterTemplateDeclaration->instantiatedWith().information(); if ( argumentInstantiatedWith.templateParametersSize() != parameterInstantiatedWith.templateParametersSize() ) { res.valid = false; return true; //Handled, invalid } for ( uint a = 0; a < argumentInstantiatedWith.templateParametersSize(); ++a ) { if ( !matchTemplateParameterTypes( argumentInstantiatedWith.templateParameters()[a].abstractType(), parameterInstantiatedWith.templateParameters()[a].abstractType(), instantiatedTypes) ) { res.valid = false; return true; //Handled, invalid } } res.templateArgsMatch = true; return true; //Handled, valid } } else if (identifiedArgument || identifiedParameter) { //Is there any case wherein an identifiedArgument will go up against a CppTemplateParameterType? //If This is possible we need a test case Q_ASSERT(!parameterType.cast()); res.valid = false; return true; //Handled, invalid. If only one is identified, it's not a match } return false; } void TemplateResolver::matchTemplateParameterTypesInternal ( const AbstractType::Ptr& argumentType, const AbstractType::Ptr& parameterType, QMap< IndexedString, AbstractType::Ptr >& instantiatedTypes, TemplateMatchType& res ) const { if (!argumentType || !parameterType) { kWarning() << "Invalid Type Encountered"; res.valid = false; return; } if (templateHandleConstIntegralType(argumentType, parameterType, res)) return; if (templateHandleDelayedType(argumentType, parameterType, instantiatedTypes, res)) return; if (templateHandleReferenceType(argumentType, parameterType, instantiatedTypes, res)) return; if (templateHandlePointerType(argumentType, parameterType, instantiatedTypes, res)) return; if (templateHandleArrayType(argumentType, parameterType, instantiatedTypes, res)) return; if (!matchCV(parameterType, argumentType, &res)) { return; } if ( CppTemplateParameterType::Ptr templateParam = parameterType.cast() ) { Declaration* decl = templateParam->declaration( m_topContext ); if ( decl ) { //Should not be possible to have a CPPTemplateParameterType with template ids..? Q_ASSERT(decl->identifier().templateIdentifiersCount() == 0); IndexedString id = decl->identifier().identifier(); //FIXME: Sometimes when matching templates within templates, delayedType will set the identifier first //The other way around is also probably possible //This needs more work to make sure the right type is set here //Q_ASSERT(instantiatedTypes[id].isNull()); instantiatedTypes[id] = argumentType; return; } } if (templateHandleIdentifiedType(argumentType, parameterType, instantiatedTypes, res)) return; //This /should/ be correct for all unhandled cases if (parameterType->indexed() != argumentType->indexed()) res.valid = false; //Invalid, types don't match }