From af0f4d0b2e38c810effc8b024ad2fb6604eec5d3 Mon Sep 17 00:00:00 2001 From: Francois Pichet Date: Sun, 14 Aug 2011 03:52:19 +0000 Subject: [PATCH] Implement function template specialization at class scope extension in Microsoft mode. A new AST node is introduced: ClassScopeFunctionSpecialization. This node holds a FunctionDecl that is not yet specialized; then during the class template instantiation the ClassScopeFunctionSpecialization will spawn the actual function specialization. Example: template class A { public: template void f(U p) { } template <> void f(int p) { } // <== class scope specialization }; This extension is necessary to parse MSVC standard C++ headers, MFC and ATL code. BTW, with this feature in, clang can parse (-fsyntax-only) all the MSVC 2010 standard header files without any error. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@137573 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/AST/ASTContext.h | 10 +++ include/clang/AST/Decl.h | 6 +- include/clang/AST/DeclTemplate.h | 52 ++++++++++++++ include/clang/AST/RecursiveASTVisitor.h | 4 ++ include/clang/Basic/DeclNodes.td | 1 + include/clang/Basic/DiagnosticSemaKinds.td | 3 + include/clang/Sema/Sema.h | 3 +- include/clang/Sema/Template.h | 5 +- include/clang/Serialization/ASTBitCodes.h | 5 +- lib/AST/ASTContext.cpp | 21 +++++- lib/AST/Decl.cpp | 14 +++- lib/AST/DeclBase.cpp | 1 + lib/CodeGen/CGDecl.cpp | 1 + lib/Sema/SemaDecl.cpp | 44 +++++++++--- lib/Sema/SemaDeclCXX.cpp | 3 +- lib/Sema/SemaTemplate.cpp | 15 +++- lib/Sema/SemaTemplateInstantiate.cpp | 5 +- lib/Sema/SemaTemplateInstantiateDecl.cpp | 37 ++++++++-- lib/Serialization/ASTReaderDecl.cpp | 4 ++ ...ms-function-specialization-class-scope.cpp | 71 +++++++++++++++++++ tools/libclang/CIndex.cpp | 1 + 21 files changed, 280 insertions(+), 26 deletions(-) create mode 100644 test/SemaTemplate/ms-function-specialization-class-scope.cpp diff --git a/include/clang/AST/ASTContext.h b/include/clang/AST/ASTContext.h index bb71503ee2..e44302491f 100644 --- a/include/clang/AST/ASTContext.h +++ b/include/clang/AST/ASTContext.h @@ -152,6 +152,11 @@ class ASTContext : public llvm::RefCountedBase { /// \brief Mapping from __block VarDecls to their copy initialization expr. llvm::DenseMap BlockVarCopyInits; + /// \brief Mapping from class scope functions specialization to their + /// templateS pattern. + llvm::DenseMap + ClassScopeSpecilizationPattern; + /// \brief Representation of a "canonical" template template parameter that /// is used in canonical template names. class CanonicalTemplateTemplateParm : public llvm::FoldingSetNode { @@ -382,6 +387,11 @@ public: MemberSpecializationInfo *getInstantiatedFromStaticDataMember( const VarDecl *Var); + FunctionDecl *getClassScopeSpecializationPattern(const FunctionDecl *FD); + + void setClassScopeSpecializationPattern(FunctionDecl *FD, + FunctionDecl *Pattern); + /// \brief Note that the static data member \p Inst is an instantiation of /// the static data member template \p Tmpl of a class template. void setInstantiatedFromStaticDataMember(VarDecl *Inst, VarDecl *Tmpl, diff --git a/include/clang/AST/Decl.h b/include/clang/AST/Decl.h index 3d14ed88c7..39b53dd071 100644 --- a/include/clang/AST/Decl.h +++ b/include/clang/AST/Decl.h @@ -1851,7 +1851,11 @@ public: bool isFunctionTemplateSpecialization() const { return getPrimaryTemplate() != 0; } - + + /// \brief Retrieve the class scope template pattern that this function + /// template specialization is instantiated from. + FunctionDecl *getClassScopeSpecializationPattern() const; + /// \brief If this function is actually a function template specialization, /// retrieve information about this function template specialization. /// Otherwise, returns NULL. diff --git a/include/clang/AST/DeclTemplate.h b/include/clang/AST/DeclTemplate.h index eb7a00ceae..0fa4a765c2 100644 --- a/include/clang/AST/DeclTemplate.h +++ b/include/clang/AST/DeclTemplate.h @@ -2097,6 +2097,58 @@ public: friend class ASTDeclWriter; }; +/// Declaration of a function specialization at template class scope. +/// This is a non standard extension needed to support MSVC. +/// For example: +/// template +/// class A { +/// template void foo(U a) { } +/// template<> void foo(int a) { } +/// } +/// +/// "template<> foo(int a)" will be saved in Specialization as a normal +/// CXXMethodDecl. Then during an instantiation of class A, it will be +/// transformed into an actual function specialization. +class ClassScopeFunctionSpecializationDecl : public Decl { +private: + ClassScopeFunctionSpecializationDecl(DeclContext *DC, SourceLocation Loc, + CXXMethodDecl *FD) + : Decl(Decl::ClassScopeFunctionSpecialization, DC, Loc), + Specialization(FD) {} + + ClassScopeFunctionSpecializationDecl(EmptyShell Empty) + : Decl(Decl::ClassScopeFunctionSpecialization, Empty) {} + + CXXMethodDecl *Specialization; + +public: + CXXMethodDecl *getSpecialization() const { return Specialization; } + + static ClassScopeFunctionSpecializationDecl *Create(ASTContext &C, + DeclContext *DC, + SourceLocation Loc, + CXXMethodDecl *FD) { + return new (C) ClassScopeFunctionSpecializationDecl(DC , Loc, FD); + } + + static ClassScopeFunctionSpecializationDecl *Create(ASTContext &Context, + EmptyShell Empty) { + return new (Context)ClassScopeFunctionSpecializationDecl(0, + SourceLocation(), 0); + } + // Implement isa/cast/dyncast/etc. + static bool classof(const Decl *D) { return classofKind(D->getKind()); } + static bool classofKind(Kind K) { + return K == Decl::ClassScopeFunctionSpecialization; + } + static bool classof(const ClassScopeFunctionSpecializationDecl *D) { + return true; + } + + friend class ASTDeclReader; + friend class ASTDeclWriter; +}; + /// Implementation of inline functions that require the template declarations inline AnyFunctionDecl::AnyFunctionDecl(FunctionTemplateDecl *FTD) : Function(FTD) { } diff --git a/include/clang/AST/RecursiveASTVisitor.h b/include/clang/AST/RecursiveASTVisitor.h index 85c5c08853..8f593c1264 100644 --- a/include/clang/AST/RecursiveASTVisitor.h +++ b/include/clang/AST/RecursiveASTVisitor.h @@ -1114,6 +1114,10 @@ DEF_TRAVERSE_DECL(FriendTemplateDecl, { } }) +DEF_TRAVERSE_DECL(ClassScopeFunctionSpecializationDecl, { + TRY_TO(TraverseDecl(D->getSpecialization())); + }) + DEF_TRAVERSE_DECL(LinkageSpecDecl, { }) DEF_TRAVERSE_DECL(ObjCClassDecl, { diff --git a/include/clang/Basic/DeclNodes.td b/include/clang/Basic/DeclNodes.td index ddd08278c3..a37dc10398 100644 --- a/include/clang/Basic/DeclNodes.td +++ b/include/clang/Basic/DeclNodes.td @@ -74,3 +74,4 @@ def Friend : Decl; def FriendTemplate : Decl; def StaticAssert : Decl; def Block : Decl, DeclContext; +def ClassScopeFunctionSpecialization : Decl; diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td index d8bbc2fca2..d0d48ab382 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.td +++ b/include/clang/Basic/DiagnosticSemaKinds.td @@ -2028,6 +2028,9 @@ def err_not_class_template_specialization : Error< "parameter}0">; def err_function_specialization_in_class : Error< "cannot specialize a function %0 within class scope">; +def ext_function_specialization_in_class : ExtWarn< + "explicit specialization of %0 within class scope in a Microsoft extension">, + InGroup; def ext_explicit_specialization_storage_class : ExtWarn< "explicit specialization cannot have a storage class">; def err_explicit_specialization_inconsistent_storage_class : Error< diff --git a/include/clang/Sema/Sema.h b/include/clang/Sema/Sema.h index 1258d596cc..3abdbd70ec 100644 --- a/include/clang/Sema/Sema.h +++ b/include/clang/Sema/Sema.h @@ -1002,7 +1002,8 @@ public: LookupResult &Previous, MultiTemplateParamsArg TemplateParamLists, bool IsFunctionDefinition, - bool &Redeclaration); + bool &Redeclaration, + bool &AddToScope); bool AddOverriddenMethods(CXXRecordDecl *DC, CXXMethodDecl *MD); void DiagnoseHiddenVirtualMethods(CXXRecordDecl *DC, CXXMethodDecl *MD); void CheckFunctionDeclaration(Scope *S, diff --git a/include/clang/Sema/Template.h b/include/clang/Sema/Template.h index 91567420ea..e68daed91f 100644 --- a/include/clang/Sema/Template.h +++ b/include/clang/Sema/Template.h @@ -350,7 +350,8 @@ namespace clang { TemplateParameterList *TemplateParams = 0); Decl *VisitCXXRecordDecl(CXXRecordDecl *D); Decl *VisitCXXMethodDecl(CXXMethodDecl *D, - TemplateParameterList *TemplateParams = 0); + TemplateParameterList *TemplateParams = 0, + bool IsClassScopeSpecialization = false); Decl *VisitCXXConstructorDecl(CXXConstructorDecl *D); Decl *VisitCXXDestructorDecl(CXXDestructorDecl *D); Decl *VisitCXXConversionDecl(CXXConversionDecl *D); @@ -367,6 +368,8 @@ namespace clang { Decl *VisitUsingShadowDecl(UsingShadowDecl *D); Decl *VisitUnresolvedUsingValueDecl(UnresolvedUsingValueDecl *D); Decl *VisitUnresolvedUsingTypenameDecl(UnresolvedUsingTypenameDecl *D); + Decl *VisitClassScopeFunctionSpecializationDecl( + ClassScopeFunctionSpecializationDecl *D); // Base case. FIXME: Remove once we can instantiate everything. Decl *VisitDecl(Decl *D) { diff --git a/include/clang/Serialization/ASTBitCodes.h b/include/clang/Serialization/ASTBitCodes.h index 6699b13293..0a0695e24c 100644 --- a/include/clang/Serialization/ASTBitCodes.h +++ b/include/clang/Serialization/ASTBitCodes.h @@ -826,7 +826,10 @@ namespace clang { DECL_INDIRECTFIELD, /// \brief A NonTypeTemplateParmDecl record that stores an expanded /// non-type template parameter pack. - DECL_EXPANDED_NON_TYPE_TEMPLATE_PARM_PACK + DECL_EXPANDED_NON_TYPE_TEMPLATE_PARM_PACK, + /// \brief A ClassScopeFunctionSpecializationDecl record a class scope + /// function specialization. (Microsoft extension). + DECL_CLASS_SCOPE_FUNCTION_SPECIALIZATION }; /// \brief Record codes for each kind of statement or expression. diff --git a/lib/AST/ASTContext.cpp b/lib/AST/ASTContext.cpp index 7acc22abcc..d01e2a12ab 100644 --- a/lib/AST/ASTContext.cpp +++ b/lib/AST/ASTContext.cpp @@ -518,6 +518,24 @@ ASTContext::setInstantiatedFromStaticDataMember(VarDecl *Inst, VarDecl *Tmpl, = new (*this) MemberSpecializationInfo(Tmpl, TSK, PointOfInstantiation); } +FunctionDecl *ASTContext::getClassScopeSpecializationPattern( + const FunctionDecl *FD){ + assert(FD && "Specialization is 0"); + llvm::DenseMap::const_iterator Pos + = ClassScopeSpecilizationPattern.find(FD); + if (Pos == ClassScopeSpecilizationPattern.end()) + return 0; + + return Pos->second; +} + +void ASTContext::setClassScopeSpecializationPattern(FunctionDecl *FD, + FunctionDecl *Pattern) { + assert(FD && "Specialization is 0"); + assert(Pattern && "Class scope specialization pattern is 0"); + ClassScopeSpecilizationPattern[FD] = Pattern; +} + NamedDecl * ASTContext::getInstantiatedFromUsingDecl(UsingDecl *UUD) { llvm::DenseMap::const_iterator Pos @@ -6439,5 +6457,6 @@ size_t ASTContext::getSideTableAllocatedMemory() const { + llvm::capacity_in_bytes(InstantiatedFromUnnamedFieldDecl) + llvm::capacity_in_bytes(OverriddenMethods) + llvm::capacity_in_bytes(Types) - + llvm::capacity_in_bytes(VariableArrayTypes); + + llvm::capacity_in_bytes(VariableArrayTypes) + + llvm::capacity_in_bytes(ClassScopeSpecilizationPattern); } diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp index 6c7deca5fd..f917d32f5e 100644 --- a/lib/AST/Decl.cpp +++ b/lib/AST/Decl.cpp @@ -1922,13 +1922,17 @@ bool FunctionDecl::isImplicitlyInstantiable() const { switch (getTemplateSpecializationKind()) { case TSK_Undeclared: - case TSK_ExplicitSpecialization: case TSK_ExplicitInstantiationDefinition: return false; case TSK_ImplicitInstantiation: return true; + // It is possible to instantiate TSK_ExplicitSpecialization kind + // if the FunctionDecl has a class scope specialization pattern. + case TSK_ExplicitSpecialization: + return getClassScopeSpecializationPattern() != 0; + case TSK_ExplicitInstantiationDeclaration: // Handled below. break; @@ -1951,6 +1955,10 @@ bool FunctionDecl::isImplicitlyInstantiable() const { } FunctionDecl *FunctionDecl::getTemplateInstantiationPattern() const { + // Handle class scope explicit specialization special case. + if (getTemplateSpecializationKind() == TSK_ExplicitSpecialization) + return getClassScopeSpecializationPattern(); + if (FunctionTemplateDecl *Primary = getPrimaryTemplate()) { while (Primary->getInstantiatedFromMemberTemplate()) { // If we have hit a point where the user provided a specialization of @@ -1976,6 +1984,10 @@ FunctionTemplateDecl *FunctionDecl::getPrimaryTemplate() const { return 0; } +FunctionDecl *FunctionDecl::getClassScopeSpecializationPattern() const { + return getASTContext().getClassScopeSpecializationPattern(this); +} + const TemplateArgumentList * FunctionDecl::getTemplateSpecializationArgs() const { if (FunctionTemplateSpecializationInfo *Info diff --git a/lib/AST/DeclBase.cpp b/lib/AST/DeclBase.cpp index 27d05baca2..5e5ec93e17 100644 --- a/lib/AST/DeclBase.cpp +++ b/lib/AST/DeclBase.cpp @@ -493,6 +493,7 @@ unsigned Decl::getIdentifierNamespaceForKind(Kind DeclKind) { case UsingDirective: case ClassTemplateSpecialization: case ClassTemplatePartialSpecialization: + case ClassScopeFunctionSpecialization: case ObjCImplementation: case ObjCCategory: case ObjCCategoryImpl: diff --git a/lib/CodeGen/CGDecl.cpp b/lib/CodeGen/CGDecl.cpp index 870fe811f1..6e65e88bce 100644 --- a/lib/CodeGen/CGDecl.cpp +++ b/lib/CodeGen/CGDecl.cpp @@ -70,6 +70,7 @@ void CodeGenFunction::EmitDecl(const Decl &D) { case Decl::Friend: case Decl::FriendTemplate: case Decl::Block: + case Decl::ClassScopeFunctionSpecialization: assert(0 && "Declaration should not be in declstmts!"); case Decl::Function: // void X(); case Decl::Record: // struct/union/class X; diff --git a/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp index b67c3d3e95..c1a6c60d28 100644 --- a/lib/Sema/SemaDecl.cpp +++ b/lib/Sema/SemaDecl.cpp @@ -1722,9 +1722,16 @@ bool Sema::MergeFunctionDecl(FunctionDecl *New, Decl *OldD) { // Preserve triviality. NewMethod->setTrivial(OldMethod->isTrivial()); + // MSVC allows explicit template specialization at class scope: + // 2 CXMethodDecls referring to the same function will be injected. + // We don't want a redeclartion error. + bool IsClassScopeExplicitSpecialization = + OldMethod->isFunctionTemplateSpecialization() && + NewMethod->isFunctionTemplateSpecialization(); bool isFriend = NewMethod->getFriendObjectKind(); - if (!isFriend && NewMethod->getLexicalDeclContext()->isRecord()) { + if (!isFriend && NewMethod->getLexicalDeclContext()->isRecord() && + !IsClassScopeExplicitSpecialization) { // -- Member function declarations with the same name and the // same parameter types cannot be overloaded if any of them // is a static member function declaration. @@ -3226,6 +3233,7 @@ Decl *Sema::HandleDeclarator(Scope *S, Declarator &D, Previous.clear(); bool Redeclaration = false; + bool AddToScope = true; if (D.getDeclSpec().getStorageClassSpec() == DeclSpec::SCS_typedef) { if (TemplateParamLists.size()) { Diag(D.getIdentifierLoc(), diag::err_template_typedef); @@ -3236,7 +3244,8 @@ Decl *Sema::HandleDeclarator(Scope *S, Declarator &D, } else if (R->isFunctionType()) { New = ActOnFunctionDeclarator(S, D, DC, R, TInfo, Previous, move(TemplateParamLists), - IsFunctionDefinition, Redeclaration); + IsFunctionDefinition, Redeclaration, + AddToScope); } else { New = ActOnVariableDeclarator(S, D, DC, R, TInfo, Previous, move(TemplateParamLists), @@ -3248,7 +3257,8 @@ Decl *Sema::HandleDeclarator(Scope *S, Declarator &D, // If this has an identifier and is not an invalid redeclaration or // function template specialization, add it to the scope stack. - if (New->getDeclName() && !(Redeclaration && New->isInvalidDecl())) + if (New->getDeclName() && AddToScope && + !(Redeclaration && New->isInvalidDecl())) PushOnScopeChains(New, S); return New; @@ -4201,7 +4211,8 @@ Sema::ActOnFunctionDeclarator(Scope *S, Declarator &D, DeclContext *DC, QualType R, TypeSourceInfo *TInfo, LookupResult &Previous, MultiTemplateParamsArg TemplateParamLists, - bool IsFunctionDefinition, bool &Redeclaration) { + bool IsFunctionDefinition, bool &Redeclaration, + bool &AddToScope) { assert(R.getTypePtr()->isFunctionType()); // TODO: consider using NameInfo for diagnostic. @@ -4266,6 +4277,7 @@ Sema::ActOnFunctionDeclarator(Scope *S, Declarator &D, DeclContext *DC, FunctionTemplateDecl *FunctionTemplate = 0; bool isExplicitSpecialization = false; bool isFunctionTemplateSpecialization = false; + bool isDependentClassScopeExplicitSpecialization = false; if (!getLangOptions().CPlusPlus) { // Determine whether the function was written with a @@ -4769,10 +4781,11 @@ Sema::ActOnFunctionDeclarator(Scope *S, Declarator &D, DeclContext *DC, } else if (isFunctionTemplateSpecialization) { if (CurContext->isDependentContext() && CurContext->isRecord() && !isFriend) { - Diag(NewFD->getLocation(), diag::err_function_specialization_in_class) + isDependentClassScopeExplicitSpecialization = true; + Diag(NewFD->getLocation(), getLangOptions().Microsoft ? + diag::ext_function_specialization_in_class : + diag::err_function_specialization_in_class) << NewFD->getDeclName(); - NewFD->setInvalidDecl(); - return 0; } else if (CheckFunctionTemplateSpecialization(NewFD, (HasExplicitTemplateArgs ? &TemplateArgs : 0), Previous)) @@ -4802,8 +4815,9 @@ Sema::ActOnFunctionDeclarator(Scope *S, Declarator &D, DeclContext *DC, } // Perform semantic checking on the function declaration. - CheckFunctionDeclaration(S, NewFD, Previous, isExplicitSpecialization, - Redeclaration); + if (!isDependentClassScopeExplicitSpecialization) + CheckFunctionDeclaration(S, NewFD, Previous, isExplicitSpecialization, + Redeclaration); assert((NewFD->isInvalidDecl() || !Redeclaration || Previous.getResultKind() != LookupResult::FoundOverloaded) && @@ -4984,6 +4998,18 @@ Sema::ActOnFunctionDeclarator(Scope *S, Declarator &D, DeclContext *DC, Context.setcudaConfigureCallDecl(NewFD); } } + + // Here we have an function template explicit specialization at class scope. + // The actually specialization will be postponed to template instatiation + // time via the ClassScopeFunctionSpecializationDecl node. + if (isDependentClassScopeExplicitSpecialization) { + ClassScopeFunctionSpecializationDecl *NewSpec = + ClassScopeFunctionSpecializationDecl::Create( + Context, CurContext, SourceLocation(), + cast(NewFD)); + CurContext->addDecl(NewSpec); + AddToScope = false; + } return NewFD; } diff --git a/lib/Sema/SemaDeclCXX.cpp b/lib/Sema/SemaDeclCXX.cpp index 05706d7d42..76461a0ca6 100644 --- a/lib/Sema/SemaDeclCXX.cpp +++ b/lib/Sema/SemaDeclCXX.cpp @@ -8652,10 +8652,11 @@ Decl *Sema::ActOnFriendFunctionDecl(Scope *S, Declarator &D, bool IsDefinition, } bool Redeclaration = false; + bool AddToScope = true; NamedDecl *ND = ActOnFunctionDeclarator(DCScope, D, DC, T, TInfo, Previous, move(TemplateParams), IsDefinition, - Redeclaration); + Redeclaration, AddToScope); if (!ND) return 0; assert(ND->getDeclContext() == DC); diff --git a/lib/Sema/SemaTemplate.cpp b/lib/Sema/SemaTemplate.cpp index ded352047a..a7d74ce7e8 100644 --- a/lib/Sema/SemaTemplate.cpp +++ b/lib/Sema/SemaTemplate.cpp @@ -4511,9 +4511,18 @@ static bool CheckTemplateSpecializationScope(Sema &S, } if (S.CurContext->isRecord() && !IsPartialSpecialization) { - S.Diag(Loc, diag::err_template_spec_decl_class_scope) - << Specialized; - return true; + if (S.getLangOptions().Microsoft) { + // Do not warn for class scope explicit specialization during + // instantiation, warning was already emitted during pattern + // semantic analysis. + if (!S.ActiveTemplateInstantiations.size()) + S.Diag(Loc, diag::ext_function_specialization_in_class) + << Specialized; + } else { + S.Diag(Loc, diag::err_template_spec_decl_class_scope) + << Specialized; + return true; + } } // C++ [temp.class.spec]p6: diff --git a/lib/Sema/SemaTemplateInstantiate.cpp b/lib/Sema/SemaTemplateInstantiate.cpp index 67e2d871c3..830f06ae20 100644 --- a/lib/Sema/SemaTemplateInstantiate.cpp +++ b/lib/Sema/SemaTemplateInstantiate.cpp @@ -98,8 +98,9 @@ Sema::getTemplateInstantiationArgs(NamedDecl *D, // Add template arguments from a function template specialization. else if (FunctionDecl *Function = dyn_cast(Ctx)) { if (!RelativeToPrimary && - Function->getTemplateSpecializationKind() - == TSK_ExplicitSpecialization) + (Function->getTemplateSpecializationKind() == + TSK_ExplicitSpecialization && + !Function->getClassScopeSpecializationPattern())) break; if (const TemplateArgumentList *TemplateArgs diff --git a/lib/Sema/SemaTemplateInstantiateDecl.cpp b/lib/Sema/SemaTemplateInstantiateDecl.cpp index 872bb87457..2554b82d97 100644 --- a/lib/Sema/SemaTemplateInstantiateDecl.cpp +++ b/lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -1288,7 +1288,8 @@ Decl *TemplateDeclInstantiator::VisitFunctionDecl(FunctionDecl *D, Decl * TemplateDeclInstantiator::VisitCXXMethodDecl(CXXMethodDecl *D, - TemplateParameterList *TemplateParams) { + TemplateParameterList *TemplateParams, + bool IsClassScopeSpecialization) { FunctionTemplateDecl *FunctionTemplate = D->getDescribedFunctionTemplate(); void *InsertPos = 0; if (FunctionTemplate && !TemplateParams) { @@ -1493,7 +1494,8 @@ TemplateDeclInstantiator::VisitCXXMethodDecl(CXXMethodDecl *D, } bool Redeclaration = false; - SemaRef.CheckFunctionDeclaration(0, Method, Previous, false, Redeclaration); + if (!IsClassScopeSpecialization) + SemaRef.CheckFunctionDeclaration(0, Method, Previous, false, Redeclaration); if (D->isPure()) SemaRef.CheckPureMethod(Method, SourceRange()); @@ -1512,7 +1514,7 @@ TemplateDeclInstantiator::VisitCXXMethodDecl(CXXMethodDecl *D, : Method); if (isFriend) Record->makeDeclVisibleInContext(DeclToAdd); - else + else if (!IsClassScopeSpecialization) Owner->addDecl(DeclToAdd); } @@ -1907,6 +1909,29 @@ Decl * TemplateDeclInstantiator return UD; } + +Decl *TemplateDeclInstantiator::VisitClassScopeFunctionSpecializationDecl( + ClassScopeFunctionSpecializationDecl *Decl) { + CXXMethodDecl *OldFD = Decl->getSpecialization(); + CXXMethodDecl *NewFD = cast(VisitCXXMethodDecl(OldFD, 0, true)); + + LookupResult Previous(SemaRef, NewFD->getNameInfo(), Sema::LookupOrdinaryName, + Sema::ForRedeclaration); + + SemaRef.LookupQualifiedName(Previous, SemaRef.CurContext); + if (SemaRef.CheckFunctionTemplateSpecialization(NewFD, 0, Previous)) { + NewFD->setInvalidDecl(); + return NewFD; + } + + // Associate the specialization with the pattern. + FunctionDecl *Specialization = cast(Previous.getFoundDecl()); + assert(Specialization && "Class scope Specialization is null"); + SemaRef.Context.setClassScopeSpecializationPattern(Specialization, OldFD); + + return NewFD; +} + Decl *Sema::SubstDecl(Decl *D, DeclContext *Owner, const MultiLevelTemplateArgumentList &TemplateArgs) { TemplateDeclInstantiator Instantiator(*this, Owner, TemplateArgs); @@ -2335,8 +2360,10 @@ void Sema::InstantiateFunctionDefinition(SourceLocation PointOfInstantiation, if (Function->isInvalidDecl() || Function->isDefined()) return; - // Never instantiate an explicit specialization. - if (Function->getTemplateSpecializationKind() == TSK_ExplicitSpecialization) + // Never instantiate an explicit specialization except if it is a class scope + // explicit specialization. + if (Function->getTemplateSpecializationKind() == TSK_ExplicitSpecialization && + !Function->getClassScopeSpecializationPattern()) return; // Find the function body that we'll be substituting. diff --git a/lib/Serialization/ASTReaderDecl.cpp b/lib/Serialization/ASTReaderDecl.cpp index 02bd21fef0..e9d421f0ff 100644 --- a/lib/Serialization/ASTReaderDecl.cpp +++ b/lib/Serialization/ASTReaderDecl.cpp @@ -1568,6 +1568,10 @@ Decl *ASTReader::ReadDeclRecord(DeclID ID) { D = ClassTemplatePartialSpecializationDecl::Create(*Context, Decl::EmptyShell()); break; + case DECL_CLASS_SCOPE_FUNCTION_SPECIALIZATION: + D = ClassScopeFunctionSpecializationDecl::Create(*Context, + Decl::EmptyShell()); + break; case DECL_FUNCTION_TEMPLATE: D = FunctionTemplateDecl::Create(*Context, Decl::EmptyShell()); break; diff --git a/test/SemaTemplate/ms-function-specialization-class-scope.cpp b/test/SemaTemplate/ms-function-specialization-class-scope.cpp new file mode 100644 index 0000000000..8821501600 --- /dev/null +++ b/test/SemaTemplate/ms-function-specialization-class-scope.cpp @@ -0,0 +1,71 @@ +// RUN: %clang_cc1 -fms-extensions -fsyntax-only -verify %s + + +class A { +public: + template + A(U p) { + } + template <> + A(int p) { // expected-warning{{explicit specialization of 'A' within class scope in a Microsoft extension}} + } + + template + void f(U p) { + } + + template <> + void f(int p) { // expected-warning{{explicit specialization of 'f' within class scope in a Microsoft extension}} + } + + void f(int p) { + } +}; + +void test1() +{ + A a(3); + char* b ; + a.f(b); + a.f(99); + a.f(100); +} + + + + +template +class B { +public: + template + B(U p) { + } + template <> + B(int p) { // expected-warning{{explicit specialization of 'B' within class scope in a Microsoft extension}} + } + + template + void f(U p) { + T y = 9; + } + + + template <> + void f(int p) { // expected-warning{{explicit specialization of 'f' within class scope in a Microsoft extension}} + T a = 3; + } + + void f(int p) { + T a = 3; + } +}; + +void test2() +{ + B b(3); + char* ptr; + b.f(ptr); + b.f(99); + b.f(100); +} + diff --git a/tools/libclang/CIndex.cpp b/tools/libclang/CIndex.cpp index 5607621f53..cd4467c523 100644 --- a/tools/libclang/CIndex.cpp +++ b/tools/libclang/CIndex.cpp @@ -4048,6 +4048,7 @@ CXCursor clang_getCursorDefinition(CXCursor C) { case Decl::StaticAssert: case Decl::Block: case Decl::Label: // FIXME: Is this right?? + case Decl::ClassScopeFunctionSpecialization: return C; // Declaration kinds that don't make any sense here, but are -- 2.40.0