kde-extraapps/kdevelop/languages/cpp/cppduchain/templateresolver.cpp

352 lines
15 KiB
C++
Raw Normal View History

/*
Copyright 2012 Olivier de Gaalon <olviier.jg@gmail.com>
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<ArrayType>())
return arrayType->elementType() ? isConstBased(arrayType->elementType()) : false;
if (PointerType::Ptr ptrType = type.cast<PointerType>())
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<ArrayType>())
return arrayType->elementType() ? isVolatileBased(arrayType->elementType()) : false;
if (PointerType::Ptr ptrType = type.cast<PointerType>())
return ptrType->baseType() ? isVolatileBased(ptrType->baseType()) : false;
return false;
}
static bool isVolatileBased(const IndexedTypeIdentifier& type)
{
return type.isVolatile();
}
template<typename A, typename B, typename C>
static bool matchCV(const A& parameterType, const B& argumentType, C* res)
{
if (isConstBased(parameterType))
{
if (!argumentType.template cast<PointerType>() && 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<PointerType>() && 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>();
ConstantIntegralType::Ptr parameterIntegral = parameterType.cast<ConstantIntegralType>();
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<DelayedType>())
{
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<DelayedType>();
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>();
ReferenceType::Ptr parameterRef = parameterType.cast<ReferenceType>();
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<CppTemplateParameterType>())
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>();
PointerType::Ptr parameterPointer = parameterType.cast<PointerType>();
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<CppTemplateParameterType>())
{
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>();
ArrayType::Ptr parameterArray = parameterType.cast<ArrayType>();
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<CppTemplateParameterType>())
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<int> to QList<T>, assign int to T.
const IdentifiedType* identifiedArgument = dynamic_cast<const IdentifiedType*>( argumentType.unsafeData() );
const IdentifiedType* identifiedParameter = dynamic_cast<const IdentifiedType*>( 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<TemplateDeclaration*>( argumentDeclaration );
TemplateDeclaration* parameterTemplateDeclaration = dynamic_cast<TemplateDeclaration*>( 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<CppTemplateParameterType>());
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<CppTemplateParameterType>() )
{
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
}