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 T>
class A {
public:
  template <class U> 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.

llvm-svn: 137573
This commit is contained in:
Francois Pichet 2011-08-14 03:52:19 +00:00
parent ae13df60a6
commit 00c7e6ceb1
21 changed files with 280 additions and 26 deletions

View File

@ -152,6 +152,11 @@ class ASTContext : public llvm::RefCountedBase<ASTContext> {
/// \brief Mapping from __block VarDecls to their copy initialization expr.
llvm::DenseMap<const VarDecl*, Expr*> BlockVarCopyInits;
/// \brief Mapping from class scope functions specialization to their
/// templateS pattern.
llvm::DenseMap<const FunctionDecl*, FunctionDecl*>
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,

View File

@ -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.

View File

@ -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 T>
/// class A {
/// template <class U> 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) { }

View File

@ -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, {

View File

@ -74,3 +74,4 @@ def Friend : Decl;
def FriendTemplate : Decl;
def StaticAssert : Decl;
def Block : Decl, DeclContext;
def ClassScopeFunctionSpecialization : Decl;

View File

@ -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<Microsoft>;
def ext_explicit_specialization_storage_class : ExtWarn<
"explicit specialization cannot have a storage class">;
def err_explicit_specialization_inconsistent_storage_class : Error<

View File

@ -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,

View File

@ -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) {

View File

@ -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.

View File

@ -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 FunctionDecl*, FunctionDecl *>::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<UsingDecl *, NamedDecl *>::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);
}

View File

@ -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

View File

@ -493,6 +493,7 @@ unsigned Decl::getIdentifierNamespaceForKind(Kind DeclKind) {
case UsingDirective:
case ClassTemplateSpecialization:
case ClassTemplatePartialSpecialization:
case ClassScopeFunctionSpecialization:
case ObjCImplementation:
case ObjCCategory:
case ObjCCategoryImpl:

View File

@ -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;

View File

@ -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<CXXMethodDecl>(NewFD));
CurContext->addDecl(NewSpec);
AddToScope = false;
}
return NewFD;
}

View File

@ -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);

View File

@ -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:

View File

@ -98,8 +98,9 @@ Sema::getTemplateInstantiationArgs(NamedDecl *D,
// Add template arguments from a function template specialization.
else if (FunctionDecl *Function = dyn_cast<FunctionDecl>(Ctx)) {
if (!RelativeToPrimary &&
Function->getTemplateSpecializationKind()
== TSK_ExplicitSpecialization)
(Function->getTemplateSpecializationKind() ==
TSK_ExplicitSpecialization &&
!Function->getClassScopeSpecializationPattern()))
break;
if (const TemplateArgumentList *TemplateArgs

View File

@ -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<CXXMethodDecl>(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<FunctionDecl>(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.

View File

@ -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;

View File

@ -0,0 +1,71 @@
// RUN: %clang_cc1 -fms-extensions -fsyntax-only -verify %s
class A {
public:
template <class U>
A(U p) {
}
template <>
A(int p) { // expected-warning{{explicit specialization of 'A' within class scope in a Microsoft extension}}
}
template <class U>
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<int>(99);
a.f(100);
}
template <class T>
class B {
public:
template <class U>
B(U p) {
}
template <>
B(int p) { // expected-warning{{explicit specialization of 'B<T>' within class scope in a Microsoft extension}}
}
template <class U>
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<char> b(3);
char* ptr;
b.f(ptr);
b.f<int>(99);
b.f(100);
}

View File

@ -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