Skip to content

Commit

Permalink
Implement function template specialization at class scope extension i…
Browse files Browse the repository at this point in the history
…n 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
  • Loading branch information
fpichet committed Aug 14, 2011
1 parent ae13df6 commit 00c7e6c
Show file tree
Hide file tree
Showing 21 changed files with 280 additions and 26 deletions.
10 changes: 10 additions & 0 deletions clang/include/clang/AST/ASTContext.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -382,6 +387,11 @@ class ASTContext : public llvm::RefCountedBase<ASTContext> {
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,
Expand Down
6 changes: 5 additions & 1 deletion clang/include/clang/AST/Decl.h
Original file line number Diff line number Diff line change
Expand Up @@ -1851,7 +1851,11 @@ class FunctionDecl : public DeclaratorDecl, public DeclContext,
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.
Expand Down
52 changes: 52 additions & 0 deletions clang/include/clang/AST/DeclTemplate.h
Original file line number Diff line number Diff line change
Expand Up @@ -2097,6 +2097,58 @@ class TypeAliasTemplateDecl : public RedeclarableTemplateDecl,
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) { }
Expand Down
4 changes: 4 additions & 0 deletions clang/include/clang/AST/RecursiveASTVisitor.h
Original file line number Diff line number Diff line change
Expand Up @@ -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, {
Expand Down
1 change: 1 addition & 0 deletions clang/include/clang/Basic/DeclNodes.td
Original file line number Diff line number Diff line change
Expand Up @@ -74,3 +74,4 @@ def Friend : Decl;
def FriendTemplate : Decl;
def StaticAssert : Decl;
def Block : Decl, DeclContext;
def ClassScopeFunctionSpecialization : Decl;
3 changes: 3 additions & 0 deletions clang/include/clang/Basic/DiagnosticSemaKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -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<
Expand Down
3 changes: 2 additions & 1 deletion clang/include/clang/Sema/Sema.h
Original file line number Diff line number Diff line change
Expand Up @@ -1002,7 +1002,8 @@ class Sema {
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,
Expand Down
5 changes: 4 additions & 1 deletion clang/include/clang/Sema/Template.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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) {
Expand Down
5 changes: 4 additions & 1 deletion clang/include/clang/Serialization/ASTBitCodes.h
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
21 changes: 20 additions & 1 deletion clang/lib/AST/ASTContext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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);
}
14 changes: 13 additions & 1 deletion clang/lib/AST/Decl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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
Expand All @@ -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
Expand Down
1 change: 1 addition & 0 deletions clang/lib/AST/DeclBase.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -493,6 +493,7 @@ unsigned Decl::getIdentifierNamespaceForKind(Kind DeclKind) {
case UsingDirective:
case ClassTemplateSpecialization:
case ClassTemplatePartialSpecialization:
case ClassScopeFunctionSpecialization:
case ObjCImplementation:
case ObjCCategory:
case ObjCCategoryImpl:
Expand Down
1 change: 1 addition & 0 deletions clang/lib/CodeGen/CGDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
44 changes: 35 additions & 9 deletions clang/lib/Sema/SemaDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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);
Expand All @@ -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),
Expand All @@ -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;
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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))
Expand Down Expand Up @@ -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) &&
Expand Down Expand Up @@ -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;
}
Expand Down
3 changes: 2 additions & 1 deletion clang/lib/Sema/SemaDeclCXX.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
15 changes: 12 additions & 3 deletions clang/lib/Sema/SemaTemplate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
Loading

0 comments on commit 00c7e6c

Please sign in to comment.