From: Douglas Gregor Date: Thu, 29 Oct 2009 00:04:11 +0000 (+0000) Subject: Implement support for semantic checking and template instantiation of X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=ed9c0f90b7e0811c209b95e39fe07c211c531285;p=clang Implement support for semantic checking and template instantiation of class template partial specializations of member templates. Also, fixes a silly little bug in the marking of "used" template parameters in member templates. Fixes PR5236. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@85447 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang/AST/DeclTemplate.h b/include/clang/AST/DeclTemplate.h index 8d44676124..0de33023bf 100644 --- a/include/clang/AST/DeclTemplate.h +++ b/include/clang/AST/DeclTemplate.h @@ -91,6 +91,13 @@ public: /// arguments or if there is a parameter pack. unsigned getMinRequiredArguments() const; + /// \brief Get the depth of this template parameter list in the set of + /// template parameter lists. + /// + /// The first template parameter list in a declaration will have depth 0, + /// the second template parameter list will have depth 1, etc. + unsigned getDepth() const; + SourceLocation getTemplateLoc() const { return TemplateLoc; } SourceLocation getLAngleLoc() const { return LAngleLoc; } SourceLocation getRAngleLoc() const { return RAngleLoc; } @@ -859,6 +866,12 @@ public: InheritedDefault = Inherited; } + /// \brief Retrieve the depth of the template parameter. + unsigned getDepth() const; + + /// \brief Retrieve the index of the template parameter. + unsigned getIndex() const; + /// \brief Returns whether this is a parameter pack. bool isParameterPack() const { return ParameterPack; } @@ -1150,6 +1163,14 @@ class ClassTemplatePartialSpecializationDecl /// \brief The list of template parameters TemplateParameterList* TemplateParams; + /// \brief The class template partial specialization from which this + /// class template partial specialization was instantiated. + /// + /// The boolean value will be true to indicate that this class template + /// partial specialization was specialized at this level. + llvm::PointerIntPair + InstantiatedFromMember; + ClassTemplatePartialSpecializationDecl(ASTContext &Context, DeclContext *DC, SourceLocation L, TemplateParameterList *Params, @@ -1160,7 +1181,7 @@ class ClassTemplatePartialSpecializationDecl ClassTemplatePartialSpecialization, DC, L, SpecializedTemplate, Builder, PrevDecl), - TemplateParams(Params) { } + TemplateParams(Params), InstantiatedFromMember(0, false) { } public: static ClassTemplatePartialSpecializationDecl * @@ -1175,6 +1196,70 @@ public: return TemplateParams; } + /// \brief Retrieve the member class template partial specialization from + /// which this particular class template partial specialization was + /// instantiated. + /// + /// \code + /// template + /// struct Outer { + /// template struct Inner; + /// template struct Inner { }; // #1 + /// }; + /// + /// Outer::Inner ii; + /// \endcode + /// + /// In this example, the instantiation of \c Outer::Inner will + /// end up instantiating the partial specialization + /// \c Outer::Inner, which itself was instantiated from the class + /// template partial specialization \c Outer::Inner. Given + /// \c Outer::Inner, this function would return + /// \c Outer::Inner. + ClassTemplatePartialSpecializationDecl *getInstantiatedFromMember() { + ClassTemplatePartialSpecializationDecl *First + = cast(getFirstDeclaration()); + return First->InstantiatedFromMember.getPointer(); + } + + void setInstantiatedFromMember( + ClassTemplatePartialSpecializationDecl *PartialSpec) { + ClassTemplatePartialSpecializationDecl *First + = cast(getFirstDeclaration()); + First->InstantiatedFromMember.setPointer(PartialSpec); + } + + /// \brief Determines whether this class template partial specialization + /// template was a specialization of a member partial specialization. + /// + /// In the following example, the member template partial specialization + /// \c X::Inner is a member specialization. + /// + /// \code + /// template + /// struct X { + /// template struct Inner; + /// template struct Inner; + /// }; + /// + /// template<> template + /// struct X::Inner { /* ... */ }; + /// \endcode + bool isMemberSpecialization() { + ClassTemplatePartialSpecializationDecl *First + = cast(getFirstDeclaration()); + return First->InstantiatedFromMember.getInt(); + } + + /// \brief Note that this member template is a specialization. + void setMemberSpecialization() { + ClassTemplatePartialSpecializationDecl *First + = cast(getFirstDeclaration()); + assert(First->InstantiatedFromMember.getPointer() && + "Only member templates can be member template specializations"); + return First->InstantiatedFromMember.setInt(true); + } + // FIXME: Add Profile support! static bool classof(const Decl *D) { diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td index 033c90c538..c5e86e7761 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.td +++ b/include/clang/Basic/DiagnosticSemaKinds.td @@ -1014,7 +1014,11 @@ def note_partial_spec_unused_parameter : Note< def err_partial_spec_ordering_ambiguous : Error< "ambiguous partial specializations of %0">; def note_partial_spec_match : Note<"partial specialization matches %0">; - +def err_partial_spec_redeclared : Error< + "class template partial specialization %0 cannot be redeclared">; +def note_prev_partial_spec_here : Note< + "previous declaration of class template partial specialization %0 is here">; + // C++ Function template specializations def err_function_template_spec_no_match : Error< "no function template matches function template specialization %0">; diff --git a/lib/AST/DeclTemplate.cpp b/lib/AST/DeclTemplate.cpp index 1a40dd7e61..fb3bd09ff4 100644 --- a/lib/AST/DeclTemplate.cpp +++ b/lib/AST/DeclTemplate.cpp @@ -67,6 +67,21 @@ unsigned TemplateParameterList::getMinRequiredArguments() const { return NumRequiredArgs; } +unsigned TemplateParameterList::getDepth() const { + if (size() == 0) + return 0; + + const NamedDecl *FirstParm = getParam(0); + if (const TemplateTypeParmDecl *TTP + = dyn_cast(FirstParm)) + return TTP->getDepth(); + else if (const NonTypeTemplateParmDecl *NTTP + = dyn_cast(FirstParm)) + return NTTP->getDepth(); + else + return cast(FirstParm)->getDepth(); +} + //===----------------------------------------------------------------------===// // TemplateDecl Implementation //===----------------------------------------------------------------------===// @@ -229,6 +244,14 @@ TemplateTypeParmDecl::Create(ASTContext &C, DeclContext *DC, return new (C) TemplateTypeParmDecl(DC, L, Id, Typename, Type, ParameterPack); } +unsigned TemplateTypeParmDecl::getDepth() const { + return TypeForDecl->getAs()->getDepth(); +} + +unsigned TemplateTypeParmDecl::getIndex() const { + return TypeForDecl->getAs()->getIndex(); +} + //===----------------------------------------------------------------------===// // NonTypeTemplateParmDecl Method Implementations //===----------------------------------------------------------------------===// diff --git a/lib/Sema/Sema.h b/lib/Sema/Sema.h index 710e7d35c4..66ee81833e 100644 --- a/lib/Sema/Sema.h +++ b/lib/Sema/Sema.h @@ -2869,6 +2869,7 @@ public: void MarkUsedTemplateParameters(const TemplateArgumentList &TemplateArgs, bool OnlyDeduced, + unsigned Depth, llvm::SmallVectorImpl &Used); void MarkDeducedTemplateParameters(FunctionTemplateDecl *FunctionTemplate, llvm::SmallVectorImpl &Deduced); diff --git a/lib/Sema/SemaTemplate.cpp b/lib/Sema/SemaTemplate.cpp index 638e00cb1d..36a158d3cd 100644 --- a/lib/Sema/SemaTemplate.cpp +++ b/lib/Sema/SemaTemplate.cpp @@ -2880,8 +2880,6 @@ Sema::ActOnClassTemplateSpecialization(Scope *S, unsigned TagSpec, Converted.flatSize()); // Create a new class template partial specialization declaration node. - TemplateParameterList *TemplateParams - = static_cast(*TemplateParameterLists.get()); ClassTemplatePartialSpecializationDecl *PrevPartial = cast_or_null(PrevDecl); ClassTemplatePartialSpecializationDecl *Partial @@ -2901,6 +2899,11 @@ Sema::ActOnClassTemplateSpecialization(Scope *S, unsigned TagSpec, } Specialization = Partial; + // If we are providing an explicit specialization of a member class + // template specialization, make a note of that. + if (PrevPartial && PrevPartial->getInstantiatedFromMember()) + PrevPartial->setMemberSpecialization(); + // Check that all of the template parameters of the class template // partial specialization are deducible from the template // arguments. If not, this class template partial specialization @@ -2908,6 +2911,7 @@ Sema::ActOnClassTemplateSpecialization(Scope *S, unsigned TagSpec, llvm::SmallVector DeducibleParams; DeducibleParams.resize(TemplateParams->size()); MarkUsedTemplateParameters(Partial->getTemplateArgs(), true, + TemplateParams->getDepth(), DeducibleParams); unsigned NumNonDeducible = 0; for (unsigned I = 0, N = DeducibleParams.size(); I != N; ++I) diff --git a/lib/Sema/SemaTemplateDeduction.cpp b/lib/Sema/SemaTemplateDeduction.cpp index a7ca8e874e..14373dee73 100644 --- a/lib/Sema/SemaTemplateDeduction.cpp +++ b/lib/Sema/SemaTemplateDeduction.cpp @@ -1690,6 +1690,7 @@ DeduceTemplateArgumentsDuringPartialOrdering(ASTContext &Context, static void MarkUsedTemplateParameters(Sema &SemaRef, QualType T, bool OnlyDeduced, + unsigned Level, llvm::SmallVectorImpl &Deduced); /// \brief Determine whether the function template \p FT1 is at least as @@ -1782,18 +1783,22 @@ static bool isAtLeastAsSpecializedAs(Sema &S, case TPOC_Call: { unsigned NumParams = std::min(Proto1->getNumArgs(), Proto2->getNumArgs()); for (unsigned I = 0; I != NumParams; ++I) - ::MarkUsedTemplateParameters(S, Proto2->getArgType(I), false, + ::MarkUsedTemplateParameters(S, Proto2->getArgType(I), false, + TemplateParams->getDepth(), UsedParameters); break; } case TPOC_Conversion: - ::MarkUsedTemplateParameters(S, Proto2->getResultType(), false, + ::MarkUsedTemplateParameters(S, Proto2->getResultType(), false, + TemplateParams->getDepth(), UsedParameters); break; case TPOC_Other: - ::MarkUsedTemplateParameters(S, FD2->getType(), false, UsedParameters); + ::MarkUsedTemplateParameters(S, FD2->getType(), false, + TemplateParams->getDepth(), + UsedParameters); break; } @@ -2065,6 +2070,7 @@ static void MarkUsedTemplateParameters(Sema &SemaRef, const TemplateArgument &TemplateArg, bool OnlyDeduced, + unsigned Depth, llvm::SmallVectorImpl &Used); /// \brief Mark the template parameters that are used by the given @@ -2073,6 +2079,7 @@ static void MarkUsedTemplateParameters(Sema &SemaRef, const Expr *E, bool OnlyDeduced, + unsigned Depth, llvm::SmallVectorImpl &Used) { // FIXME: if !OnlyDeduced, we have to walk the whole subexpression to // find other occurrences of template parameters. @@ -2085,7 +2092,8 @@ MarkUsedTemplateParameters(Sema &SemaRef, if (!NTTP) return; - Used[NTTP->getIndex()] = true; + if (NTTP->getDepth() == Depth) + Used[NTTP->getIndex()] = true; } /// \brief Mark the template parameters that are used by the given @@ -2094,13 +2102,15 @@ static void MarkUsedTemplateParameters(Sema &SemaRef, NestedNameSpecifier *NNS, bool OnlyDeduced, + unsigned Depth, llvm::SmallVectorImpl &Used) { if (!NNS) return; - MarkUsedTemplateParameters(SemaRef, NNS->getPrefix(), OnlyDeduced, Used); + MarkUsedTemplateParameters(SemaRef, NNS->getPrefix(), OnlyDeduced, Depth, + Used); MarkUsedTemplateParameters(SemaRef, QualType(NNS->getAsType(), 0), - OnlyDeduced, Used); + OnlyDeduced, Depth, Used); } /// \brief Mark the template parameters that are used by the given @@ -2109,16 +2119,20 @@ static void MarkUsedTemplateParameters(Sema &SemaRef, TemplateName Name, bool OnlyDeduced, + unsigned Depth, llvm::SmallVectorImpl &Used) { if (TemplateDecl *Template = Name.getAsTemplateDecl()) { if (TemplateTemplateParmDecl *TTP - = dyn_cast(Template)) - Used[TTP->getIndex()] = true; + = dyn_cast(Template)) { + if (TTP->getDepth() == Depth) + Used[TTP->getIndex()] = true; + } return; } if (DependentTemplateName *DTN = Name.getAsDependentTemplateName()) - MarkUsedTemplateParameters(SemaRef, DTN->getQualifier(), OnlyDeduced, Used); + MarkUsedTemplateParameters(SemaRef, DTN->getQualifier(), OnlyDeduced, + Depth, Used); } /// \brief Mark the template parameters that are used by the given @@ -2126,6 +2140,7 @@ MarkUsedTemplateParameters(Sema &SemaRef, static void MarkUsedTemplateParameters(Sema &SemaRef, QualType T, bool OnlyDeduced, + unsigned Depth, llvm::SmallVectorImpl &Used) { if (T.isNull()) return; @@ -2140,6 +2155,7 @@ MarkUsedTemplateParameters(Sema &SemaRef, QualType T, MarkUsedTemplateParameters(SemaRef, cast(T)->getPointeeType(), OnlyDeduced, + Depth, Used); break; @@ -2147,6 +2163,7 @@ MarkUsedTemplateParameters(Sema &SemaRef, QualType T, MarkUsedTemplateParameters(SemaRef, cast(T)->getPointeeType(), OnlyDeduced, + Depth, Used); break; @@ -2155,69 +2172,74 @@ MarkUsedTemplateParameters(Sema &SemaRef, QualType T, MarkUsedTemplateParameters(SemaRef, cast(T)->getPointeeType(), OnlyDeduced, + Depth, Used); break; case Type::MemberPointer: { const MemberPointerType *MemPtr = cast(T.getTypePtr()); MarkUsedTemplateParameters(SemaRef, MemPtr->getPointeeType(), OnlyDeduced, - Used); + Depth, Used); MarkUsedTemplateParameters(SemaRef, QualType(MemPtr->getClass(), 0), - OnlyDeduced, Used); + OnlyDeduced, Depth, Used); break; } case Type::DependentSizedArray: MarkUsedTemplateParameters(SemaRef, cast(T)->getSizeExpr(), - OnlyDeduced, Used); + OnlyDeduced, Depth, Used); // Fall through to check the element type case Type::ConstantArray: case Type::IncompleteArray: MarkUsedTemplateParameters(SemaRef, cast(T)->getElementType(), - OnlyDeduced, Used); + OnlyDeduced, Depth, Used); break; case Type::Vector: case Type::ExtVector: MarkUsedTemplateParameters(SemaRef, cast(T)->getElementType(), - OnlyDeduced, Used); + OnlyDeduced, Depth, Used); break; case Type::DependentSizedExtVector: { const DependentSizedExtVectorType *VecType = cast(T); MarkUsedTemplateParameters(SemaRef, VecType->getElementType(), OnlyDeduced, - Used); + Depth, Used); MarkUsedTemplateParameters(SemaRef, VecType->getSizeExpr(), OnlyDeduced, - Used); + Depth, Used); break; } case Type::FunctionProto: { const FunctionProtoType *Proto = cast(T); MarkUsedTemplateParameters(SemaRef, Proto->getResultType(), OnlyDeduced, - Used); + Depth, Used); for (unsigned I = 0, N = Proto->getNumArgs(); I != N; ++I) MarkUsedTemplateParameters(SemaRef, Proto->getArgType(I), OnlyDeduced, - Used); + Depth, Used); break; } - case Type::TemplateTypeParm: - Used[cast(T)->getIndex()] = true; + case Type::TemplateTypeParm: { + const TemplateTypeParmType *TTP = cast(T); + if (TTP->getDepth() == Depth) + Used[TTP->getIndex()] = true; break; + } case Type::TemplateSpecialization: { const TemplateSpecializationType *Spec = cast(T); MarkUsedTemplateParameters(SemaRef, Spec->getTemplateName(), OnlyDeduced, - Used); + Depth, Used); for (unsigned I = 0, N = Spec->getNumArgs(); I != N; ++I) - MarkUsedTemplateParameters(SemaRef, Spec->getArg(I), OnlyDeduced, Used); + MarkUsedTemplateParameters(SemaRef, Spec->getArg(I), OnlyDeduced, Depth, + Used); break; } @@ -2225,14 +2247,14 @@ MarkUsedTemplateParameters(Sema &SemaRef, QualType T, if (!OnlyDeduced) MarkUsedTemplateParameters(SemaRef, cast(T)->getElementType(), - OnlyDeduced, Used); + OnlyDeduced, Depth, Used); break; case Type::Typename: if (!OnlyDeduced) MarkUsedTemplateParameters(SemaRef, cast(T)->getQualifier(), - OnlyDeduced, Used); + OnlyDeduced, Depth, Used); break; // None of these types have any template parameters in them. @@ -2259,6 +2281,7 @@ static void MarkUsedTemplateParameters(Sema &SemaRef, const TemplateArgument &TemplateArg, bool OnlyDeduced, + unsigned Depth, llvm::SmallVectorImpl &Used) { switch (TemplateArg.getKind()) { case TemplateArgument::Null: @@ -2267,25 +2290,27 @@ MarkUsedTemplateParameters(Sema &SemaRef, case TemplateArgument::Type: MarkUsedTemplateParameters(SemaRef, TemplateArg.getAsType(), OnlyDeduced, - Used); + Depth, Used); break; case TemplateArgument::Declaration: if (TemplateTemplateParmDecl *TTP - = dyn_cast(TemplateArg.getAsDecl())) - Used[TTP->getIndex()] = true; + = dyn_cast(TemplateArg.getAsDecl())) { + if (TTP->getDepth() == Depth) + Used[TTP->getIndex()] = true; + } break; case TemplateArgument::Expression: MarkUsedTemplateParameters(SemaRef, TemplateArg.getAsExpr(), OnlyDeduced, - Used); + Depth, Used); break; case TemplateArgument::Pack: for (TemplateArgument::pack_iterator P = TemplateArg.pack_begin(), PEnd = TemplateArg.pack_end(); P != PEnd; ++P) - MarkUsedTemplateParameters(SemaRef, *P, OnlyDeduced, Used); + MarkUsedTemplateParameters(SemaRef, *P, OnlyDeduced, Depth, Used); break; } } @@ -2301,10 +2326,11 @@ MarkUsedTemplateParameters(Sema &SemaRef, /// deduced. void Sema::MarkUsedTemplateParameters(const TemplateArgumentList &TemplateArgs, - bool OnlyDeduced, + bool OnlyDeduced, unsigned Depth, llvm::SmallVectorImpl &Used) { for (unsigned I = 0, N = TemplateArgs.size(); I != N; ++I) - ::MarkUsedTemplateParameters(*this, TemplateArgs[I], OnlyDeduced, Used); + ::MarkUsedTemplateParameters(*this, TemplateArgs[I], OnlyDeduced, + Depth, Used); } /// \brief Marks all of the template parameters that will be deduced by a @@ -2319,5 +2345,5 @@ void Sema::MarkDeducedTemplateParameters(FunctionTemplateDecl *FunctionTemplate, FunctionDecl *Function = FunctionTemplate->getTemplatedDecl(); for (unsigned I = 0, N = Function->getNumParams(); I != N; ++I) ::MarkUsedTemplateParameters(*this, Function->getParamDecl(I)->getType(), - true, Deduced); + true, TemplateParams->getDepth(), Deduced); } diff --git a/lib/Sema/SemaTemplateInstantiate.cpp b/lib/Sema/SemaTemplateInstantiate.cpp index 14930134da..794ec2a9ef 100644 --- a/lib/Sema/SemaTemplateInstantiate.cpp +++ b/lib/Sema/SemaTemplateInstantiate.cpp @@ -981,61 +981,72 @@ Sema::InstantiateClassTemplateSpecialization( } } - if (Matched.size() == 1) { - // -- If exactly one matching specialization is found, the - // instantiation is generated from that specialization. - Pattern = Matched[0].first; - ClassTemplateSpec->setInstantiationOf(Matched[0].first, Matched[0].second); - } else if (Matched.size() > 1) { - // -- If more than one matching specialization is found, the - // partial order rules (14.5.4.2) are used to determine - // whether one of the specializations is more specialized - // than the others. If none of the specializations is more - // specialized than all of the other matching - // specializations, then the use of the class template is - // ambiguous and the program is ill-formed. + if (Matched.size() >= 1) { llvm::SmallVector::iterator Best = Matched.begin(); - for (llvm::SmallVector::iterator P = Best + 1, - PEnd = Matched.end(); - P != PEnd; ++P) { - if (getMoreSpecializedPartialSpecialization(P->first, Best->first) - == P->first) - Best = P; - } - - // Determine if the best partial specialization is more specialized than - // the others. - bool Ambiguous = false; - for (llvm::SmallVector::iterator P = Matched.begin(), - PEnd = Matched.end(); - P != PEnd; ++P) { - if (P != Best && - getMoreSpecializedPartialSpecialization(P->first, Best->first) - != Best->first) { - Ambiguous = true; - break; + if (Matched.size() == 1) { + // -- If exactly one matching specialization is found, the + // instantiation is generated from that specialization. + // We don't need to do anything for this. + } else { + // -- If more than one matching specialization is found, the + // partial order rules (14.5.4.2) are used to determine + // whether one of the specializations is more specialized + // than the others. If none of the specializations is more + // specialized than all of the other matching + // specializations, then the use of the class template is + // ambiguous and the program is ill-formed. + for (llvm::SmallVector::iterator P = Best + 1, + PEnd = Matched.end(); + P != PEnd; ++P) { + if (getMoreSpecializedPartialSpecialization(P->first, Best->first) + == P->first) + Best = P; } - } - - if (Ambiguous) { - // Partial ordering did not produce a clear winner. Complain. - ClassTemplateSpec->setInvalidDecl(); - Diag(PointOfInstantiation, diag::err_partial_spec_ordering_ambiguous) - << ClassTemplateSpec; - // Print the matching partial specializations. + // Determine if the best partial specialization is more specialized than + // the others. + bool Ambiguous = false; for (llvm::SmallVector::iterator P = Matched.begin(), PEnd = Matched.end(); - P != PEnd; ++P) - Diag(P->first->getLocation(), diag::note_partial_spec_match) - << getTemplateArgumentBindingsText(P->first->getTemplateParameters(), - *P->second); - - return true; + P != PEnd; ++P) { + if (P != Best && + getMoreSpecializedPartialSpecialization(P->first, Best->first) + != Best->first) { + Ambiguous = true; + break; + } + } + + if (Ambiguous) { + // Partial ordering did not produce a clear winner. Complain. + ClassTemplateSpec->setInvalidDecl(); + Diag(PointOfInstantiation, diag::err_partial_spec_ordering_ambiguous) + << ClassTemplateSpec; + + // Print the matching partial specializations. + for (llvm::SmallVector::iterator P = Matched.begin(), + PEnd = Matched.end(); + P != PEnd; ++P) + Diag(P->first->getLocation(), diag::note_partial_spec_match) + << getTemplateArgumentBindingsText(P->first->getTemplateParameters(), + *P->second); + + return true; + } } // Instantiate using the best class template partial specialization. - Pattern = Best->first; + ClassTemplatePartialSpecializationDecl *OrigPartialSpec = Best->first; + while (OrigPartialSpec->getInstantiatedFromMember()) { + // If we've found an explicit specialization of this class template, + // stop here and use that as the pattern. + if (OrigPartialSpec->isMemberSpecialization()) + break; + + OrigPartialSpec = OrigPartialSpec->getInstantiatedFromMember(); + } + + Pattern = OrigPartialSpec; ClassTemplateSpec->setInstantiationOf(Best->first, Best->second); } else { // -- If no matches are found, the instantiation is generated diff --git a/lib/Sema/SemaTemplateInstantiateDecl.cpp b/lib/Sema/SemaTemplateInstantiateDecl.cpp index 17b99bca95..50b0fb81d7 100644 --- a/lib/Sema/SemaTemplateInstantiateDecl.cpp +++ b/lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -82,6 +82,10 @@ namespace { TemplateParameterList * SubstTemplateParams(TemplateParameterList *List); + + bool InstantiateClassTemplatePartialSpecialization( + ClassTemplateDecl *ClassTemplate, + ClassTemplatePartialSpecializationDecl *PartialSpec); }; } @@ -389,6 +393,21 @@ Decl *TemplateDeclInstantiator::VisitEnumConstantDecl(EnumConstantDecl *D) { return 0; } +namespace { + class SortDeclByLocation { + SourceManager &SourceMgr; + + public: + explicit SortDeclByLocation(SourceManager &SourceMgr) + : SourceMgr(SourceMgr) { } + + bool operator()(const Decl *X, const Decl *Y) const { + return SourceMgr.isBeforeInTranslationUnit(X->getLocation(), + Y->getLocation()); + } + }; +} + Decl *TemplateDeclInstantiator::VisitClassTemplateDecl(ClassTemplateDecl *D) { TemplateParameterList *TempParams = D->getTemplateParameters(); TemplateParameterList *InstParams = SubstTemplateParams(TempParams); @@ -413,13 +432,52 @@ Decl *TemplateDeclInstantiator::VisitClassTemplateDecl(ClassTemplateDecl *D) { SemaRef.Context.getTypeDeclType(RecordInst); Owner->addDecl(Inst); + + // First, we sort the partial specializations by location, so + // that we instantiate them in the order they were declared. + llvm::SmallVector PartialSpecs; + for (llvm::FoldingSet::iterator + P = D->getPartialSpecializations().begin(), + PEnd = D->getPartialSpecializations().end(); + P != PEnd; ++P) + PartialSpecs.push_back(&*P); + std::sort(PartialSpecs.begin(), PartialSpecs.end(), + SortDeclByLocation(SemaRef.SourceMgr)); + + // Instantiate all of the partial specializations of this member class + // template. + for (unsigned I = 0, N = PartialSpecs.size(); I != N; ++I) + InstantiateClassTemplatePartialSpecialization(Inst, PartialSpecs[I]); + return Inst; } Decl * TemplateDeclInstantiator::VisitClassTemplatePartialSpecializationDecl( ClassTemplatePartialSpecializationDecl *D) { - assert(false &&"Partial specializations of member templates are unsupported"); + ClassTemplateDecl *ClassTemplate = D->getSpecializedTemplate(); + + // Lookup the already-instantiated declaration in the instantiation + // of the class template and return that. + DeclContext::lookup_result Found + = Owner->lookup(ClassTemplate->getDeclName()); + if (Found.first == Found.second) + return 0; + + ClassTemplateDecl *InstClassTemplate + = dyn_cast(*Found.first); + if (!InstClassTemplate) + return 0; + + Decl *DCanon = D->getCanonicalDecl(); + for (llvm::FoldingSet::iterator + P = InstClassTemplate->getPartialSpecializations().begin(), + PEnd = InstClassTemplate->getPartialSpecializations().end(); + P != PEnd; ++P) { + if (P->getInstantiatedFromMember()->getCanonicalDecl() == DCanon) + return &*P; + } + return 0; } @@ -431,7 +489,7 @@ TemplateDeclInstantiator::VisitFunctionTemplateDecl(FunctionTemplateDecl *D) { TemplateParameterList *InstParams = SubstTemplateParams(TempParams); if (!InstParams) return NULL; - + FunctionDecl *Instantiated = 0; if (CXXMethodDecl *DMethod = dyn_cast(D->getTemplatedDecl())) Instantiated = cast_or_null(VisitCXXMethodDecl(DMethod, @@ -945,6 +1003,130 @@ TemplateDeclInstantiator::SubstTemplateParams(TemplateParameterList *L) { return InstL; } +/// \brief Instantiate the declaration of a class template partial +/// specialization. +/// +/// \param ClassTemplate the (instantiated) class template that is partially +// specialized by the instantiation of \p PartialSpec. +/// +/// \param PartialSpec the (uninstantiated) class template partial +/// specialization that we are instantiating. +/// +/// \returns true if there was an error, false otherwise. +bool +TemplateDeclInstantiator::InstantiateClassTemplatePartialSpecialization( + ClassTemplateDecl *ClassTemplate, + ClassTemplatePartialSpecializationDecl *PartialSpec) { + // Substitute into the template parameters of the class template partial + // specialization. + TemplateParameterList *TempParams = PartialSpec->getTemplateParameters(); + TemplateParameterList *InstParams = SubstTemplateParams(TempParams); + if (!InstParams) + return true; + + // Substitute into the template arguments of the class template partial + // specialization. + const TemplateArgumentList &PartialSpecTemplateArgs + = PartialSpec->getTemplateInstantiationArgs(); + llvm::SmallVector InstTemplateArgs; + for (unsigned I = 0, N = PartialSpecTemplateArgs.size(); I != N; ++I) { + TemplateArgument Inst = SemaRef.Subst(PartialSpecTemplateArgs[I], + TemplateArgs); + if (Inst.isNull()) + return true; + + InstTemplateArgs.push_back(Inst); + } + + + // Check that the template argument list is well-formed for this + // class template. + TemplateArgumentListBuilder Converted(ClassTemplate->getTemplateParameters(), + InstTemplateArgs.size()); + if (SemaRef.CheckTemplateArgumentList(ClassTemplate, + PartialSpec->getLocation(), + /*FIXME:*/PartialSpec->getLocation(), + InstTemplateArgs.data(), + InstTemplateArgs.size(), + /*FIXME:*/PartialSpec->getLocation(), + false, + Converted)) + return true; + + // Figure out where to insert this class template partial specialization + // in the member template's set of class template partial specializations. + llvm::FoldingSetNodeID ID; + ClassTemplatePartialSpecializationDecl::Profile(ID, + Converted.getFlatArguments(), + Converted.flatSize(), + SemaRef.Context); + void *InsertPos = 0; + ClassTemplateSpecializationDecl *PrevDecl + = ClassTemplate->getPartialSpecializations().FindNodeOrInsertPos(ID, + InsertPos); + + // Build the canonical type that describes the converted template + // arguments of the class template partial specialization. + QualType CanonType + = SemaRef.Context.getTemplateSpecializationType(TemplateName(ClassTemplate), + Converted.getFlatArguments(), + Converted.flatSize()); + + // Build the fully-sugared type for this class template + // specialization as the user wrote in the specialization + // itself. This means that we'll pretty-print the type retrieved + // from the specialization's declaration the way that the user + // actually wrote the specialization, rather than formatting the + // name based on the "canonical" representation used to store the + // template arguments in the specialization. + QualType WrittenTy + = SemaRef.Context.getTemplateSpecializationType(TemplateName(ClassTemplate), + InstTemplateArgs.data(), + InstTemplateArgs.size(), + CanonType); + + if (PrevDecl) { + // We've already seen a partial specialization with the same template + // parameters and template arguments. This can happen, for example, when + // substituting the outer template arguments ends up causing two + // class template partial specializations of a member class template + // to have identical forms, e.g., + // + // template + // struct Outer { + // template struct Inner; + // template struct Inner; + // template struct Inner; + // }; + // + // Outer outer; // error: the partial specializations of Inner + // // have the same signature. + SemaRef.Diag(PartialSpec->getLocation(), diag::err_partial_spec_redeclared) + << WrittenTy; + SemaRef.Diag(PrevDecl->getLocation(), diag::note_prev_partial_spec_here) + << SemaRef.Context.getTypeDeclType(PrevDecl); + return true; + } + + + // Create the class template partial specialization declaration. + ClassTemplatePartialSpecializationDecl *InstPartialSpec + = ClassTemplatePartialSpecializationDecl::Create(SemaRef.Context, Owner, + PartialSpec->getLocation(), + InstParams, + ClassTemplate, + Converted, + 0); + InstPartialSpec->setInstantiatedFromMember(PartialSpec); + InstPartialSpec->setTypeAsWritten(WrittenTy); + + // Add this partial specialization to the set of class template partial + // specializations. + ClassTemplate->getPartialSpecializations().InsertNode(InstPartialSpec, + InsertPos); + return false; +} + /// \brief Does substitution on the type of the given function, including /// all of the function parameters. /// @@ -1391,6 +1573,22 @@ static bool isInstantiationOf(FunctionTemplateDecl *Pattern, return false; } +static bool +isInstantiationOf(ClassTemplatePartialSpecializationDecl *Pattern, + ClassTemplatePartialSpecializationDecl *Instance) { + Pattern + = cast(Pattern->getCanonicalDecl()); + do { + Instance = cast( + Instance->getCanonicalDecl()); + if (Pattern == Instance) + return true; + Instance = Instance->getInstantiatedFromMember(); + } while (Instance); + + return false; +} + static bool isInstantiationOf(CXXRecordDecl *Pattern, CXXRecordDecl *Instance) { Pattern = Pattern->getCanonicalDecl(); @@ -1481,6 +1679,11 @@ static bool isInstantiationOf(ASTContext &Ctx, NamedDecl *D, Decl *Other) { if (FunctionTemplateDecl *Temp = dyn_cast(Other)) return isInstantiationOf(cast(D), Temp); + if (ClassTemplatePartialSpecializationDecl *PartialSpec + = dyn_cast(Other)) + return isInstantiationOf(cast(D), + PartialSpec); + if (FieldDecl *Field = dyn_cast(Other)) { if (!Field->getDeclName()) { // This is an unnamed field. diff --git a/test/CXX/temp/temp.decls/temp.class.spec/p6.cpp b/test/CXX/temp/temp.decls/temp.class.spec/p6.cpp index afe6ab2b96..79d6c54e29 100644 --- a/test/CXX/temp/temp.decls/temp.class.spec/p6.cpp +++ b/test/CXX/temp/temp.decls/temp.class.spec/p6.cpp @@ -1,5 +1,6 @@ // RUN: clang-cc -fsyntax-only -verify %s +// Test class template partial specializations of member templates. template struct X0 { template struct Inner0 { @@ -16,5 +17,39 @@ struct X0::Inner0 { static const unsigned value = 2; }; -// FIXME: Test instantiation of these partial specializations (once they are -// implemented). +int array0[X0::Inner0::value == 0? 1 : -1]; +int array1[X0::Inner0::value == 1? 1 : -1]; +int array2[X0::Inner0::value == 2? 1 : -1]; + +// Make sure we can provide out-of-line class template partial specializations +// for member templates (and instantiate them). +template struct A { + struct C { + template struct B; + }; +}; + +// partial specialization of A::C::B +template template struct A::C::B { }; + +A::C::B absip; + +// Check for conflicts during template instantiation. +template +struct Outer { + template struct Inner; + template struct Inner {}; // expected-note{{previous}} + template struct Inner {}; // expected-error{{cannot be redeclared}} +}; + +Outer outer; // expected-note{{instantiation}} + +// Test specialization of class template partial specialization members. +template<> template +struct X0::Inner0 { + static const unsigned value = 3; +}; + +int array3[X0::Inner0::value == 0? 1 : -1]; +int array4[X0::Inner0::value == 3? 1 : -1]; +int array5[X0::Inner0::value == 2? 1 : -1];