/* This file is part of KDevelop Copyright 2007 David Nolden 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 "templatedeclaration.h" #include #include #include #include #include #include #include #include #include #include "templateparameterdeclaration.h" #include "qtfunctiondeclaration.h" #include "cppducontext.h" #include "expressionparser.h" #include "templateresolver.h" #include #include #include #include using namespace KDevelop; using namespace Cpp; #define REGISTER_TEMPLATE_DECLARATION(Declaration) typedef SpecialTemplateDeclaration TheTemplate ## Declaration; \ REGISTER_DUCHAIN_ITEM_WITH_DATA(TheTemplate ## Declaration, SpecialTemplateDeclarationData); REGISTER_TEMPLATE_DECLARATION(Declaration) REGISTER_TEMPLATE_DECLARATION(ClassDeclaration) REGISTER_TEMPLATE_DECLARATION(TemplateParameterDeclaration) REGISTER_TEMPLATE_DECLARATION(ClassFunctionDeclaration) REGISTER_TEMPLATE_DECLARATION(ClassMemberDeclaration) REGISTER_TEMPLATE_DECLARATION(FunctionDeclaration) REGISTER_TEMPLATE_DECLARATION(QtFunctionDeclaration) REGISTER_TEMPLATE_DECLARATION(FunctionDefinition) REGISTER_TEMPLATE_DECLARATION(AliasDeclaration) REGISTER_TEMPLATE_DECLARATION(ForwardDeclaration) QMutex TemplateDeclaration::instantiationsMutex(QMutex::Recursive); typedef CppDUContext StandardCppDUContext; namespace Cpp { DEFINE_LIST_MEMBER_HASH(SpecialTemplateDeclarationData, m_specializations, IndexedDeclaration) DEFINE_LIST_MEMBER_HASH(SpecialTemplateDeclarationData, m_specializedWith, IndexedType) } AbstractType::Ptr applyPointerReference( AbstractType::Ptr ptr, const KDevelop::IndexedTypeIdentifier& id ) { AbstractType::Ptr ret = ptr; if(ret && ((static_cast(ret->modifiers() & AbstractType::ConstModifier) != id.isConstant()) || (static_cast(ret->modifiers() & AbstractType::VolatileModifier) != id.isVolatile()))) { ret->setModifiers((id.isConstant() ? AbstractType::ConstModifier : AbstractType::NoModifiers) | (id.isVolatile() ? AbstractType::VolatileModifier : AbstractType::NoModifiers)); } for( int a = 0; a < id.pointerDepth(); ++a ) { uint modifiers = AbstractType::NoModifiers; if( id.isConstPointer( a ) ) modifiers = AbstractType::ConstModifier; PointerType::Ptr newRet( new PointerType() ); newRet->setModifiers(modifiers); newRet->setBaseType( ret ); ret = newRet.cast(); } if(id.isReference() ) { uint modifiers = AbstractType::NoModifiers; if( id.isConstant() ) modifiers |= AbstractType::ConstModifier; if( id.isVolatile() ) modifiers |= AbstractType::VolatileModifier; ReferenceType::Ptr newRet( new ReferenceType() ); newRet->setModifiers(modifiers); newRet->setBaseType( ret ); newRet->setIsRValue(id.isRValue()); ret = newRet.cast(); } return ret; } ///Finds out whether any DelayedType's are involved in a given type(It searches return-values, argument-types, base-classes, etc.) struct DelayedTypeSearcher : public KDevelop::SimpleTypeVisitor { bool found; DelayedTypeSearcher() : found(false) { } // We want to use SimpleTypeVisitor implementations using SimpleTypeVisitor::visit; virtual bool visit ( const AbstractType* type ) override { if( found ) return false; if( dynamic_cast(type) ) found = true; return !found; } virtual bool visit (const StructureType *) override { ///We do not want to visit member-types, so return false here return false; } }; namespace Cpp { TypePtr containsDelayedType(AbstractType::Ptr type) { PointerType::Ptr pType = type.cast(); ReferenceType::Ptr rType = type.cast(); DelayedType::Ptr delayedType = type.cast(); TypeAliasType::Ptr aType = type.cast(); if( pType ) return containsDelayedType(pType->baseType()); if( rType ) return containsDelayedType(rType->baseType()); if( aType ) return containsDelayedType(aType->type()); return delayedType; } } /** * Thread-local data to ensure recursion limits are not exceeded. * Also holds the implementation data to support defaulte template parameters. */ struct ThreadLocalData { ThreadLocalData() : delayedDepth(0) , aliasDepth(0) {} // used to apply default template parameters QMultiHash typeOverloads; // recursion counter for delayed type resolution uint delayedDepth; // recursion counter for alias type resolution uint aliasDepth; }; #if (QT_VERSION >= 0x040801) QThreadStorage threadData; inline ThreadLocalData& threadDataLocal() { return threadData.localData(); } #else QThreadStorage threadData; inline ThreadLocalData& threadDataLocal() { if(!threadData.localData()) threadData.setLocalData(new ThreadLocalData()); return *threadData.localData(); } #endif /** * RAII class to push/pop a type overload for a given identifier. */ struct PushTypeOverload { PushTypeOverload(const IndexedQualifiedIdentifier& qid_, const IndexedType& type_) : qid(qid_) , type(type_) , data(threadDataLocal()) { data.typeOverloads.insert(qid, type); } ~PushTypeOverload() { data.typeOverloads.remove(qid, type); } private: IndexedQualifiedIdentifier qid; IndexedType type; ThreadLocalData& data; }; /** * Replaces any DelayedTypes in interesting positions with their resolved versions, * if they can be resolved. */ struct DelayedTypeResolver : public KDevelop::TypeExchanger { const KDevelop::DUContext* searchContext; const KDevelop::TopDUContext* source; KDevelop::DUContext::SearchFlags searchFlags; DelayedTypeResolver(const DUContext* _searchContext, const TopDUContext* _source, DUContext::SearchFlags _searchFlags = DUContext::NoUndefinedTemplateParams) : searchContext(_searchContext) , source(_source) , searchFlags(_searchFlags) { } virtual AbstractType::Ptr exchange( const AbstractType::Ptr& type ) { ThreadLocalData& data = threadDataLocal(); PushValue inc(data.delayedDepth, data.delayedDepth + 1); if( data.delayedDepth > 30 ) { kDebug(9007) << "Too much depth in DelayedTypeResolver::exchange, while exchanging" << (type ? type->toString() : QString("(null)")); return type; } DelayedType::Ptr delayedType = type.cast(); if( delayedType && delayedType->kind() == DelayedType::Delayed ) { QualifiedIdentifier qid = delayedType->identifier().identifier().identifier(); if( !qid.isExpression() ) { // look for default template parameters IndexedType indexedType = data.typeOverloads.value(qid); if(!indexedType) { // fall back to normal DUContext search DUContext::SearchItem::PtrList identifiers; identifiers << DUContext::SearchItem::Ptr( new DUContext::SearchItem(qid) ); DUContext::DeclarationList decls; if( !searchContext->findDeclarationsInternal( identifiers, searchContext->range().end, AbstractType::Ptr(), decls, source, searchFlags, 0 ) ) return type; if( !decls.isEmpty() ) { indexedType = decls[0]->indexedType(); } } if( indexedType.isValid() ) { return applyPointerReference(indexedType.abstractType(), delayedType->identifier()); } } ///Resolution as type has failed, or is not appropriate. ///Resolve delayed expression, for example static numeric expressions ExpressionParser p; ExpressionEvaluationResult res; if( qid.isExpression() ) res = p.evaluateExpression( delayedType->identifier().toString().toUtf8(), DUContextPointer(const_cast(searchContext)), source ); else res = p.evaluateType( delayedType->identifier().toString().toUtf8(), DUContextPointer(const_cast(searchContext)), source ); // NOTE: This looks hacky, but see e.g. TestDUChain::testDecltypeTypedef - we really _never_ // can replace a delayed type with a CppTemplateParameterType. Instead we need to create // a new delayed type for that with its identifier. //TODO: try to add support for replacing CppTemplateParameterTypes directly eventually... // dunno if its actually doable though AbstractType::Ptr ret = res.type.abstractType(); if (CppTemplateParameterType::Ptr tplParam = ret.cast()) { DelayedType::Ptr delayed(new DelayedType); delayed->setIdentifier(IndexedTypeIdentifier(tplParam->declarationId().qualifiedIdentifier().last().toString())); return delayed.cast(); } return ret; }else{ if( containsDelayedType(type) ) { //Copy the type to keep the correct reference/pointer structure AbstractType::Ptr typeCopy( type->clone() ); PointerType::Ptr pType = typeCopy.cast(); ReferenceType::Ptr rType = typeCopy.cast(); TypeAliasType::Ptr aType = typeCopy.cast(); if( pType ) //Replace the base pType->exchangeTypes(this); if( rType ) //Replace the base rType->exchangeTypes(this); if( aType ) aType->exchangeTypes(this); return typeCopy; } } return type; } virtual bool exchangeMembers() const { return false; } private: AbstractType::Ptr keepAlive; }; // bool operator==( const ExpressionEvaluationResult& left, const ExpressionEvaluationResult& right ) { // return left.type == right.type && left.isInstance == right.isInstance; // } namespace Cpp { // bool operator==( const QList& left, const QList& right ) { // return true; // } TemplateDeclaration::TemplateDeclaration(const TemplateDeclaration& /*rhs*/) : m_instantiatedFrom(0) , m_instantiationDepth(0) { } TemplateDeclaration::TemplateDeclaration() : m_instantiatedFrom(0) , m_instantiationDepth(0) { } Declaration* TemplateDeclaration::specialize(const IndexedInstantiationInformation& specialization, const TopDUContext* topContext, int upDistance) { if(!specialization.isValid()) return dynamic_cast(this); else { InstantiationInformation information = IndexedInstantiationInformation( specialization ).information(); //Add empty elements until the specified depth for(int a = 0; a < upDistance; ++a) { InstantiationInformation nextInformation; nextInformation.previousInstantiationInformation = information.indexed(); information = nextInformation; } return instantiate(information, topContext); } } IndexedInstantiationInformation TemplateDeclaration::specialization() const { if(m_instantiatedWith.isValid()) return m_instantiatedWith; else return IndexedInstantiationInformation(); } // DeclarationId TemplateDeclaration::id() const { // const Declaration* thisDecl = dynamic_cast(this); // if(m_instantiatedFrom) { // DeclarationId ret = m_instantiatedFrom->id(); // ret.setSpecialization(m_instantiatedWith.index()); // return ret; // } else{ // thisDecl->topContext()->ownIndex(); // return thisDecl->Declaration::id(); // } // } DUContext* TemplateDeclaration::templateContext(const TopDUContext* source) const { return getTemplateContext(dynamic_cast(const_cast(this)), source); } TemplateDeclaration::~TemplateDeclaration() { InstantiationsHash instantiations; { ///Unregister at the declaration this one is instantiated from if( m_instantiatedFrom ) { InstantiationsHash::iterator it = m_instantiatedFrom->m_instantiations.find(m_instantiatedWith); if( it != m_instantiatedFrom->m_instantiations.end() ) { Q_ASSERT(*it == this); m_instantiatedFrom->m_instantiations.erase(it); } m_instantiatedFrom = 0; } } deleteAllInstantiations(); } TemplateDeclaration* TemplateDeclaration::instantiatedFrom() const { return m_instantiatedFrom; } void TemplateDeclaration::setSpecializedFrom(TemplateDeclaration* other) { if(other && other->instantiatedFrom()) { setSpecializedFrom(other->instantiatedFrom()); return; } if(other && other->specializedFrom().data()) { setSpecializedFrom(dynamic_cast(other->specializedFrom().data())); return; } IndexedDeclaration indexedSelf(dynamic_cast(this)); IndexedDeclaration indexedOther(dynamic_cast(other)); Q_ASSERT(indexedSelf.data()); if( TemplateDeclaration* tplDec = dynamic_cast(specializedFrom().data()) ) tplDec->removeSpecializationInternal(indexedSelf); setSpecializedFromInternal(indexedOther); if( TemplateDeclaration* otherTemplate = dynamic_cast(indexedOther.data()) ) { otherTemplate->addSpecializationInternal(indexedSelf); otherTemplate->deleteAllInstantiations(); } } void TemplateDeclaration::reserveInstantiation(const IndexedInstantiationInformation& info) { QMutexLocker l(&instantiationsMutex); Q_ASSERT(m_instantiations.find(info) == m_instantiations.end()); m_instantiations.insert(info, 0); } ///Reads the template-parameters from the template-context of the declaration, and puts them into the identifier. ///Must be called AFTER the declaration was instantiated. void updateIdentifierTemplateParameters( Identifier& identifier, Declaration* basicDeclaration, const TopDUContext* top ) { identifier.clearTemplateIdentifiers(); TemplateDeclaration* tempDecl = dynamic_cast(basicDeclaration); if(tempDecl) { InstantiationInformation specializedWith(tempDecl->specializedWith().information()); if(specializedWith.templateParametersSize()) { //Use the information from the specialization-information to build the template-identifiers FOREACH_FUNCTION(const IndexedType& indexedType, specializedWith.templateParameters) { AbstractType::Ptr type = indexedType.abstractType(); if(type) identifier.appendTemplateIdentifier( IndexedTypeIdentifier(type->toString()) ); else identifier.appendTemplateIdentifier( IndexedTypeIdentifier("(missing template type)") ); } return; } } DUContext* templateCtx = getTemplateContext(basicDeclaration, top); if( !templateCtx ) return; for( int a = 0; a < templateCtx->localDeclarations().count(); a++ ) { AbstractType::Ptr type = templateCtx->localDeclarations()[a]->abstractType(); if(type) identifier.appendTemplateIdentifier( IndexedTypeIdentifier(type->toString()) ); else identifier.appendTemplateIdentifier( IndexedTypeIdentifier("(missing template type)") ); } } void TemplateDeclaration::setInstantiatedFrom(TemplateDeclaration* from, const InstantiationInformation& instantiatedWith) { Q_ASSERT(from != this); //Change the identifier so it contains the template-parameters QMutexLocker l(&instantiationsMutex); if( m_instantiatedFrom ) { InstantiationsHash::iterator it = m_instantiatedFrom->m_instantiations.find(m_instantiatedWith); if( it != m_instantiatedFrom->m_instantiations.end() && *it == this ) m_instantiatedFrom->m_instantiations.erase(it); m_instantiatedFrom = 0; } m_instantiatedFrom = from; m_instantiatedWith = instantiatedWith.indexed(); //Only one instantiation is allowed if(from) { //Either it must be reserved, or not exist yet Q_ASSERT(from->m_instantiations.find(instantiatedWith.indexed()) == from->m_instantiations.end() || (*from->m_instantiations.find(instantiatedWith.indexed())) == 0); from->m_instantiations.insert(m_instantiatedWith, this); Q_ASSERT(from->m_instantiations.contains(m_instantiatedWith)); } } bool TemplateDeclaration::isInstantiatedFrom(const TemplateDeclaration* other) const { QMutexLocker l(&instantiationsMutex); InstantiationsHash::const_iterator it = other->m_instantiations.find(m_instantiatedWith); if( it != other->m_instantiations.end() && (*it) == this ) return true; else return false; } void TemplateDeclaration::setTemplateParameterContext(KDevelop::DUContext* context) { dynamicTemplateData()->m_parameterContext = context; } KDevelop::DUContext* TemplateDeclaration::templateParameterContext() const { return const_cast(templateData()->m_parameterContext.data()); ///@todo make data() const; return non-const pointer in duchain-pointer } bool isTemplateDeclaration(const KDevelop::Declaration* decl) { return (bool)dynamic_cast(decl); } ///@todo prevent endless recursion when resolving base-classes!(Parent is not yet in du-chain, so a base-class that references it will cause endless recursion) CppDUContext* instantiateDeclarationAndContext( KDevelop::DUContext* parentContext, const TopDUContext* source, KDevelop::DUContext* context, const InstantiationInformation& templateArguments, Declaration* instantiatedDeclaration, Declaration* instantiatedFrom, bool doNotRegister ) { Q_ASSERT(parentContext); TemplateDeclaration* instantiatedFromTemplate = dynamic_cast(instantiatedFrom); StandardCppDUContext* contextCopy = 0; if( context ) { ///Specialize involved contexts Q_ASSERT(context->parentContext()); //Top-context is not allowed contextCopy = new StandardCppDUContext(context->range(), parentContext, true); //We do not need to care about TopDUContext here, because a top-context can not be instantiated contextCopy->setType(context->type()); contextCopy->setLocalScopeIdentifier(context->localScopeIdentifier()); if( instantiatedDeclaration ) instantiatedDeclaration->setInternalContext(contextCopy); ///Now the created context is already partially functional and can be used for searching(not the instantiated template-params yet though) if( context->type() == KDevelop::DUContext::Template ) { ///We're in the template-parameter context, exchange the template-parameters with their values. ///Specialize the local template-declarations uint currentArgument = 0; InstantiationInformation parameterInstantiationInformation; parameterInstantiationInformation.previousInstantiationInformation = templateArguments.indexed(); foreach(Declaration* decl, context->localDeclarations()) { #ifdef QT_DEBUG TemplateDeclaration* tempDecl = dynamic_cast(decl); Q_ASSERT(tempDecl); // tempDecl->instantiate(parameterInstantiationInformation, source, true); #endif TemplateParameterDeclaration* templateDecl = dynamic_cast(decl); Q_ASSERT(templateDecl); //Only template-parameter declarations are allowed in template-contexts TemplateParameterDeclaration* declCopy = dynamic_cast(decl->clone()); Q_ASSERT(declCopy); TemplateDeclaration* tempCopyDecl = dynamic_cast(declCopy); Q_ASSERT(tempCopyDecl); if( currentArgument < templateArguments.templateParametersSize() && templateArguments.templateParameters()[currentArgument].abstractType() ) { declCopy->setAbstractType( templateArguments.templateParameters()[currentArgument].abstractType() ); } else { //Apply default-parameters, although these should have been applied before //Use the already available delayed-type resolution to resolve the value/type if( !templateDecl->defaultParameter().isEmpty() ) { DelayedType::Ptr delayed( new DelayedType() ); delayed->setIdentifier( IndexedTypeIdentifier(templateDecl->defaultParameter()) ); declCopy->setAbstractType( resolveDelayedTypes( delayed.cast(), contextCopy, source) ); }else{ //Parameter missing } } ///We mark the declaration as a specialization, so no index is created for it within the top-context(that needs a write-lock) tempCopyDecl->setInstantiatedFrom(0, parameterInstantiationInformation); ///This inserts the copied declaration into the copied context declCopy->setContext(contextCopy); ++currentArgument; } } ///Find the template-parameter context, and recurse into it, so we can replace the template parameters foreach( const DUContext::Import &importedContext, context->importedParentContexts() ) { CppDUContext* import = dynamic_cast*>(importedContext.context(source)); if( !import) continue; ///For functions, the Template-context is one level deeper(it is imported by the function-context) so also copy the function-context if( import->type() == KDevelop::DUContext::Template || import->type() == KDevelop::DUContext::Function ) { DUContext* ctx = import->instantiate(templateArguments, source); contextCopy->addImportedParentContext( ctx, CursorInRevision(), true ); if( instantiatedDeclaration && import->type() == KDevelop::DUContext::Template ) { TemplateDeclaration* tempDecl = dynamic_cast(instantiatedDeclaration); if( instantiatedDeclaration ) tempDecl->setTemplateParameterContext( ctx ); else kDebug(9007) << "instantiated declaration is not a template declaration"; } } else { //Import all other imported contexts contextCopy->addImportedParentContext( import, CursorInRevision::invalid(), true ); } } if( instantiatedDeclaration ) { ///We do not need to respect forward-declarations here, because they are not allowed as base-classes. ClassDeclaration* klass = dynamic_cast( instantiatedDeclaration ); if( klass ) { //It could also be a function ///Resolve template-dependent base-classes(They can not be found in the imports-list, because their type is DelayedType and those have no context) uint num = 0; FOREACH_FUNCTION( const BaseClassInstance& base, klass->baseClasses ) { DelayedType::Ptr delayed = base.baseClass.type(); if( delayed ) { ///Resolve the delayed type, and import the context DelayedTypeResolver res(contextCopy, source); AbstractType::Ptr newType( res.exchange(delayed.cast()) ); newType = TypeUtils::unAliasedType(newType); if( CppClassType::Ptr baseClass = newType.cast() ) { if( baseClass->declaration(source) && baseClass->declaration(source)->internalContext() ) { contextCopy->addImportedParentContext( baseClass->declaration(source)->internalContext(), CursorInRevision::invalid(), true ); } BaseClassInstance newInstance(base); newInstance.baseClass = newType->indexed(); klass->replaceBaseClass( num, newInstance ); } else { kWarning(9007) << "Resolved bad base-class" << delayed->toString() << (newType ? newType->toString() : QString()); } } ++num; } } } } else { ///Note: this is possible, for example for template function-declarations(They do not have an internal context, because they have no compound statement), for variables, etc.. ///Directly take their assigned template-parameter-context and specialize it. We need it at least for overload-resolution. TemplateDeclaration* fromTemplateDecl = dynamic_cast(instantiatedFrom); TemplateDeclaration* toTemplateDecl = dynamic_cast(instantiatedDeclaration); if( toTemplateDecl && fromTemplateDecl && fromTemplateDecl->templateParameterContext() ) { CppDUContext* templCtx = dynamic_cast*>(fromTemplateDecl->templateParameterContext()); DUContext* ctx = templCtx->instantiate(templateArguments, source); toTemplateDecl->setTemplateParameterContext( ctx ); } } if( contextCopy && !doNotRegister ) contextCopy->setInstantiatedFrom(dynamic_cast*>(context), templateArguments); ///Since now the context is accessible through the du-chain, so it must not be changed any more. if( instantiatedDeclaration && instantiatedDeclaration->abstractType() ) { ///an AliasDeclaration represents a C++ "using bla::bla;" declaration. if(AliasDeclaration* alias = dynamic_cast(instantiatedDeclaration)) { ThreadLocalData& data = threadDataLocal(); PushValue safety(data.aliasDepth, data.delayedDepth + 1); if(data.aliasDepth > 30) { kWarning() << "depth-limit reached while resolving alias-declaration" << alias->identifier().toString() << "within" << parentContext->scopeIdentifier(true).toString(); }else { ///For alias declaration, we resolve the declaration that is aliased instead of a type. ///For this reason, template alias-declarations have a DelayedType assigned DelayedType::Ptr delayed = alias->type(); if(delayed) { QList declarations = parentContext->findDeclarations(delayed->identifier().identifier().identifier()); if(!declarations.isEmpty()) alias->setAliasedDeclaration(declarations.first()); } } }else{ TemplateDeclaration* instantiatedTemplate = dynamic_cast(instantiatedDeclaration); InstantiationInformation globalTemplateArguments = templateArguments; if(instantiatedTemplate) { //Update the "specializedWith" information InstantiationInformation oldSpecializedWith = instantiatedTemplate->specializedWith().information(); if(oldSpecializedWith.templateParametersSize()) { //Replace the delayed types in the specialization-information with their real types InstantiationInformation newSpecializedWith(oldSpecializedWith); newSpecializedWith.templateParametersList().clear(); FOREACH_FUNCTION(const IndexedType& type, oldSpecializedWith.templateParameters) newSpecializedWith.addTemplateParameter(resolveDelayedTypes(type.abstractType(), instantiatedDeclaration->internalContext() ? instantiatedDeclaration->internalContext() : parentContext, source )); instantiatedTemplate->setSpecializedWith(newSpecializedWith.indexed()); globalTemplateArguments = newSpecializedWith; } } ///Resolve all involved delayed types AbstractType::Ptr t(instantiatedDeclaration->abstractType()); IdentifiedType* idType = dynamic_cast(t.unsafeData()); ///Use the internal context if it exists, so undefined template-arguments can be found and the DelayedType can be further delayed then. AbstractType::Ptr changedType = resolveDelayedTypes( instantiatedDeclaration->abstractType(), instantiatedDeclaration->internalContext() ? instantiatedDeclaration->internalContext() : parentContext, source ); if( idType && idType->declarationId() == instantiatedFrom->id() ) { if( changedType == instantiatedDeclaration->abstractType() ) changedType = instantiatedDeclaration->abstractType()->clone(); IdentifiedType* changedIdType = dynamic_cast(changedType.unsafeData()); if( changedIdType ) { DeclarationId base = instantiatedFrom->id(); if(instantiatedFromTemplate && instantiatedFromTemplate->specializedFrom().data()) base = instantiatedFromTemplate->specializedFrom().data()->id(); base.setSpecialization(globalTemplateArguments.indexed()); changedIdType->setDeclarationId(base); } } instantiatedDeclaration->setAbstractType( changedType ); } // } } if( instantiatedDeclaration ) { TemplateDeclaration* instantiatedTemplate = dynamic_cast(instantiatedDeclaration); if(instantiatedTemplate && templateArguments.templateParametersSize()) { //Since this is also called for normal members, this does not have to be the case. ///Change the identifier to reflect the used template-arguments KDevelop::Identifier id = instantiatedDeclaration->identifier(); updateIdentifierTemplateParameters( id, instantiatedDeclaration, source ); instantiatedDeclaration->setIdentifier(id); } ///Last step, because after this, the declaration will be potentially findable if(instantiatedTemplate && instantiatedFromTemplate) instantiatedTemplate->setInstantiatedFrom(instantiatedFromTemplate, templateArguments); ///@todo check for possible multi-threading issues when inserting visible declarations into anonymous contexts instantiatedDeclaration->setContext(parentContext, templateArguments.templateParametersSize() || parentContext->inSymbolTable()); } return contextCopy; } DeclarationId TemplateDeclaration::id(bool forceDirect) const { if(m_instantiatedFrom) { DeclarationId ret = m_instantiatedFrom->id(forceDirect); ret.setSpecialization(specialization()); return ret; }else{ return dynamic_cast(this)->Declaration::id(forceDirect); } } void TemplateDeclaration::deleteAllInstantiations() { if(m_instantiations.isEmpty() && m_defaultParameterInstantiations.isEmpty()) return; InstantiationsHash instantiations; { QMutexLocker l(&instantiationsMutex); instantiations = m_instantiations; m_defaultParameterInstantiations.clear(); m_instantiations.clear(); } foreach( TemplateDeclaration* decl, instantiations ) { Q_ASSERT(decl); decl->m_instantiatedFrom = 0; //Only delete real insantiations, not specializations //FIXME: before this checked for decl->isAnonymous //This was a problem because some instantiations are not anonymous, so they end up orphaned from their m_instantiatedFrom //If strange crashes start cropping up in template code, this needs more thought if(!decl->specializedFrom().isValid()) { Declaration* realDecl = dynamic_cast(decl); delete realDecl; } } } //TODO: QHash? typedef QMap TemplateParams; uint matchInstantiationParameters(const InstantiationInformation &info, const InstantiationInformation &matchAgainst, const TopDUContext *topCtxt, TemplateParams &requiredParams) { TemplateResolver resolver(topCtxt); uint matchQuality = 1; for(uint a = 0; a < info.templateParametersSize(); ++a) { uint parameterMatchQuality = resolver.matchTemplateParameterTypes(info.templateParameters()[a].abstractType(), matchAgainst.templateParameters()[a].abstractType(), requiredParams); if (!parameterMatchQuality) return 0; matchQuality += parameterMatchQuality; } return matchQuality; } //Returns the first (and barring bugs the only) imported template context. //A template decl imports one template context, which in turn imports the template context for the next scope, etc. DUContext* nextTemplateContext(const DUContext* importingContext, const TopDUContext *topCtxt) { foreach( const DUContext::Import &import, importingContext->importedParentContexts() ) { DUContext* c = import.context(topCtxt); if (c && c->type() == DUContext::Template) return c; } return 0; } uint TemplateDeclaration::matchInstantiation(IndexedInstantiationInformation indexedInfo, const TopDUContext* topCtxt, InstantiationInformation &instantiateWith, bool &instantiationRequired) const { DUContext *templateContext = this->templateParameterContext(); IndexedInstantiationInformation indexedSpecializedWith = this->specializedWith(); uint matchQuality = 1; instantiationRequired = false; while(indexedInfo.isValid() && templateContext) { if (templateContext->localDeclarations().size()) instantiationRequired = true; InstantiationInformation info = indexedInfo.information(); InstantiationInformation specializedWith = indexedSpecializedWith.information(); if (info.templateParametersSize() != specializedWith.templateParametersSize()) return 0; if (!info.templateParametersSize()) { indexedInfo = info.previousInstantiationInformation; indexedSpecializedWith = specializedWith.previousInstantiationInformation; continue; } TemplateParams requiredParameters; foreach(Declaration* parameterDecl, templateContext->localDeclarations()) requiredParameters[parameterDecl->identifier().identifier()] = AbstractType::Ptr(); uint match = matchInstantiationParameters(info, specializedWith, topCtxt, requiredParameters); if (!match) return 0; matchQuality += match; InstantiationInformation currentInstantiation; foreach( Declaration* decl, templateContext->localDeclarations() ) { if( decl->abstractType().cast() ) { IndexedString identifier = decl->identifier().identifier(); if (requiredParameters[identifier]) currentInstantiation.addTemplateParameter(requiredParameters[identifier]); //Take the type we have assigned. else return 0; } else currentInstantiation.addTemplateParameter(decl->abstractType()); //Use the specialized type } currentInstantiation.previousInstantiationInformation = instantiateWith.indexed(); instantiateWith = currentInstantiation; indexedSpecializedWith = specializedWith.previousInstantiationInformation; indexedInfo = info.previousInstantiationInformation; templateContext = nextTemplateContext(templateContext, topCtxt); } return matchQuality; } TemplateDeclaration* TemplateDeclaration::instantiateSpecialization(const InstantiationInformation& info, const TopDUContext* source) { TemplateDeclaration *specialization = 0; InstantiationInformation specializationInstantiationInfo; bool instantiationRequired; uint matchQuality = 0; FOREACH_FUNCTION(const IndexedDeclaration& decl, specializations) { //We only use visible specializations here if(source->recursiveImportIndices().contains(decl.indexedTopContext())) { TemplateDeclaration* curSpecialization = dynamic_cast(decl.data()); if(curSpecialization) { InstantiationInformation possibleInstantiation; uint match = curSpecialization->matchInstantiation(info.indexed(), source, possibleInstantiation, instantiationRequired); if(match > matchQuality) { matchQuality = match; specializationInstantiationInfo = possibleInstantiation; specialization = curSpecialization; } } } } if (specialization) { if (!instantiationRequired) return specialization; else return dynamic_cast(specialization->instantiate(specializationInstantiationInfo, source, true)); } return 0; } void applyDefaultParameters(const DUContext* templateContext, const TopDUContext* source, const DUContext* surroundingContext, InstantiationInformation* templateArguments) { Q_ASSERT(templateContext); Q_ASSERT(templateContext->type() == DUContext::Template); Q_ASSERT(source); Q_ASSERT(surroundingContext); const int totalParameters = templateContext->localDeclarations().count(); KDevVarLengthArray explicitParameters = templateArguments->templateParametersList(); if(totalParameters <= explicitParameters.size() //TODO: why is this required? && (explicitParameters.size() != 1 || explicitParameters.at(0).isValid())) { // nothing to do return; } KDevVarLengthArray appliedParameters; int currentArgument = 0; QVector typeOverloads; foreach(Declaration* decl, templateContext->localDeclarations()) { TemplateParameterDeclaration* templateDecl = dynamic_cast(decl); Q_ASSERT(templateDecl); //Only template-parameter declarations are allowed in template-contexts IndexedType type = decl->indexedType(); Q_ASSERT(type.isValid()); if( currentArgument < explicitParameters.size() && explicitParameters.at(currentArgument).isValid() ) { // use explicit parameter type = explicitParameters.at(currentArgument); Q_ASSERT(type); } else if(templateDecl->hasDefaultParameter()) { // Apply default-parameter Q_ASSERT(!templateDecl->defaultParameter().isEmpty()); DelayedType::Ptr delayed( new DelayedType() ); delayed->setIdentifier( IndexedTypeIdentifier(templateDecl->defaultParameter()) ); type = resolveDelayedTypes( delayed.cast(), surroundingContext, source)->indexed(); } // else the parameter is missing //TODO: why is this neccessary? if(type.abstractType().cast()) { ++currentArgument; continue; } appliedParameters << type; if(type != decl->indexedType()) { // add type overload typeOverloads << new PushTypeOverload(decl->qualifiedIdentifier(), type); } ++currentArgument; } qDeleteAll(typeOverloads); templateArguments->templateParametersList() = appliedParameters; } Declaration* TemplateDeclaration::instantiate( const InstantiationInformation& _templateArguments, const TopDUContext* source, bool forceLocal ) { InstantiationInformation templateArguments(_templateArguments); /* if(dynamic_cast(dynamic_cast(this)->context())) { Q_ASSERT(templateArguments.previousInstantiationInformation == 0); }*/ if( m_instantiatedFrom && !forceLocal) return m_instantiatedFrom->instantiate( templateArguments, source ); if ( specializedFrom().data() && !forceLocal ) return dynamic_cast(specializedFrom().declaration())->instantiate(templateArguments, source); { QMutexLocker l(&instantiationsMutex); { DefaultParameterInstantiationHash::const_iterator it = m_defaultParameterInstantiations.constFind(templateArguments.indexed()); if(it != m_defaultParameterInstantiations.constEnd()) templateArguments = (*it).information(); } InstantiationsHash::const_iterator it; it = m_instantiations.constFind( templateArguments.indexed() ); if( it != m_instantiations.constEnd() ) { if(*it) { return dynamic_cast(*it); }else{ ///@todo What if the same thing is instantiated twice in parralel? Then this may trigger as well, altough one side should wait ///We are currently instantiating this declaration with the same template arguments. This would lead to an assertion. kDebug() << "tried to recursively instantiate" << dynamic_cast(this)->toString() << "with" << templateArguments.toString(); ///Maybe problematic, because the returned declaration is not in the correct context etc. return 0; } } } if(!source) return 0; if (m_instantiationDepth > 5) { kWarning() << "depth-limit reached while instantiating template declaration with" << _templateArguments.toString(); return 0; } PushValue depthCounter(m_instantiationDepth, m_instantiationDepth + 1); DUContext* surroundingContext = dynamic_cast(this)->context(); if(!surroundingContext) { kDebug() << "Declaration has no context:" << dynamic_cast(this)->qualifiedIdentifier().toString() << dynamic_cast(this)->toString(); return dynamic_cast(this); } Declaration* decl = dynamic_cast(this); Q_ASSERT(decl); Q_ASSERT(decl->topContext()); DUContext* templateContext = getTemplateContext(dynamic_cast(this), source); // kDebug() << decl->qualifiedIdentifier().toString() << "got template-context" << templateContext << templateArguments.toString(); if(!forceLocal) { if(templateContext) { applyDefaultParameters(templateContext, source, surroundingContext, &templateArguments); } ///Check whether there is type-aliases in the parameters that need to be resolved ///Generally, resolve all type-aliases that are part of a template-class, and keep the others { InstantiationInformation newTemplateArguments = templateArguments; newTemplateArguments.templateParametersList().clear(); struct UnAliasExchanger : public KDevelop::TypeExchanger { UnAliasExchanger(const TopDUContext* _source) : source(_source) { } const TopDUContext* source; virtual KDevelop::AbstractType::Ptr exchange(const KDevelop::AbstractType::Ptr& type) { KDevelop::AbstractType::Ptr check = type; KDevelop::TypeAliasType::Ptr alias = type.cast(); if(alias) { //We exchange type-aliases with their real types only of the type-alias is in a template //class. In that case, we cannot be sure that it's not used for meta-programming. //All other aliases can be kept, for user-friendliness, even if it's not 100% C++ correct Declaration* decl = alias->declaration(source); if(!decl || dynamic_cast(decl)) { return exchange(alias->type()); } } if(check) check->exchangeTypes(this); return check; } }; UnAliasExchanger exchanger(source); for(uint a = 0; a < templateArguments.templateParametersSize(); ++a) newTemplateArguments.templateParametersList().append(exchanger.exchange(templateArguments.templateParameters()[a].abstractType())->indexed()); templateArguments = newTemplateArguments; } if(!(templateArguments == _templateArguments)) { QMutexLocker l(&instantiationsMutex); m_defaultParameterInstantiations[_templateArguments.indexed()] = templateArguments.indexed(); } } { //Now we have the final template-parameters. Once again check whether we have already instantiated this, //and if not, reserve the instantiation so we cannot crash later on ///@todo When the same declaration is instantuated multiple times, this sucks because one is returned invalid QMutexLocker l(&instantiationsMutex); InstantiationsHash::const_iterator it; it = m_instantiations.constFind( templateArguments.indexed() ); if( it != m_instantiations.constEnd() ) { if(*it) { return dynamic_cast(*it); }else{ //Problem return dynamic_cast(this); } } ///@warning Once we've called reserveInstantiation, we have to be 100% sure that we actually create the instantiation reserveInstantiation(templateArguments.indexed()); } TemplateDeclaration *instantiatedSpecialization = instantiateSpecialization(templateArguments, source); //We have reserved the instantiation, so it must have stayed untouched Q_ASSERT(m_instantiations[templateArguments.indexed()] == 0); if(instantiatedSpecialization) { //A specialization has been chosen and instantiated. Just register it here, and return it. instantiatedSpecialization->setInstantiatedFrom(this, templateArguments); return dynamic_cast(instantiatedSpecialization); } { //Check whether the instantiation also instantiates the parent context, and if it does, replace surroundingContext with the instantiated version CppDUContext* parent = dynamic_cast*>(surroundingContext); if(parent && templateArguments.previousInstantiationInformation.index() && templateArguments.previousInstantiationInformation.index() != parent->instantiatedWith().index()) { DUContext* surroundingCandidate = parent->instantiate(IndexedInstantiationInformation(templateArguments.previousInstantiationInformation).information(), source); if(surroundingCandidate) surroundingContext = surroundingCandidate; else kDebug() << "could not instantiate surrounding context for" << dynamic_cast(this)->qualifiedIdentifier().toString(); } } Declaration* clone = decl->clone(); Q_ASSERT(clone); #ifdef QT_DEBUG TemplateDeclaration* cloneTemplateDecl = dynamic_cast(clone); Q_ASSERT(cloneTemplateDecl); #endif ///Now eventually create the virtual contexts, and fill new information into the declaration instantiateDeclarationAndContext( surroundingContext, source, decl->internalContext(), templateArguments, clone, decl ); // cloneTemplateDecl->setInstantiatedFrom(this); Q_ASSERT(clone->topContext()); return clone; } AbstractType::Ptr resolveDelayedTypes( AbstractType::Ptr type, const KDevelop::DUContext* context, const KDevelop::TopDUContext* source, KDevelop::DUContext::SearchFlags searchFlags ) { if( !type ) return type; ///First, find out if delayed types are involved(Function return-type, function-arguments, reference/pointer target, base-class) DelayedTypeSearcher search; type->accept(&search); DelayedType::Ptr delayedType = type.cast(); if( search.found || delayedType ) { ///Delayed types were found. We must copy the whole type, and replace the delayed types. DelayedTypeResolver resolver(context, source, searchFlags); AbstractType::Ptr typeCopy; if( delayedType ) ///The type itself is a delayed type, resolve it typeCopy = resolver.exchange( type ); else { ///Resolve involved delayed types, now hopefully we know the template-parameters typeCopy = AbstractType::Ptr( type->clone() ); DelayedTypeSearcher testSearch; typeCopy->accept(&testSearch); Q_ASSERT(testSearch.found); typeCopy->exchangeTypes( &resolver ); } return typeCopy; } else { return type; } } IndexedInstantiationInformation TemplateDeclaration::instantiatedWith() const { return m_instantiatedWith; } TemplateDeclaration::InstantiationsHash TemplateDeclaration::instantiations() const { QMutexLocker l(&instantiationsMutex); return m_instantiations; } template<> Declaration* SpecialTemplateDeclaration::resolve(const TopDUContext* topContext) const { if( instantiatedFrom() ) { SpecialTemplateDeclaration* instantiatedFrom = dynamic_cast*>(this->instantiatedFrom()); if( instantiatedFrom ) { Declaration* baseResolved = instantiatedFrom->resolve(topContext); TemplateDeclaration* baseTemplate = dynamic_cast(baseResolved); if( baseResolved && baseTemplate ) { return baseTemplate->instantiate(instantiatedWith().information(), topContext ? topContext : this->topContext()); }else{ //Forward-declaration was not resolved return 0; } }else{ //TODO: report this in the problem reporter? kWarning(9007) << "Problem in template forward-declaration"; return 0; } }else{ return ForwardDeclaration::resolve(topContext); } } }