Skip to content

Commit e638448

Browse files
committed
[Clang][Sema] Diagnose variable template explicit specializations with storage-class-specifiers
1 parent ed25d1a commit e638448

File tree

18 files changed

+527
-208
lines changed

18 files changed

+527
-208
lines changed

clang/lib/Parse/ParseDeclCXX.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3162,7 +3162,8 @@ Parser::DeclGroupPtrTy Parser::ParseCXXClassMemberDeclaration(
31623162
DeclSpec::SCS_static &&
31633163
DeclaratorInfo.getDeclSpec().getStorageClassSpec() !=
31643164
DeclSpec::SCS_typedef &&
3165-
!DS.isFriendSpecified()) {
3165+
!DS.isFriendSpecified() &&
3166+
TemplateInfo.Kind == ParsedTemplateInfo::NonTemplate) {
31663167
// It's a default member initializer.
31673168
if (BitfieldSize.get())
31683169
Diag(Tok, getLangOpts().CPlusPlus20
@@ -3261,7 +3262,7 @@ Parser::DeclGroupPtrTy Parser::ParseCXXClassMemberDeclaration(
32613262
} else if (ThisDecl)
32623263
Actions.AddInitializerToDecl(ThisDecl, Init.get(),
32633264
EqualLoc.isInvalid());
3264-
} else if (ThisDecl && DS.getStorageClassSpec() == DeclSpec::SCS_static)
3265+
} else if (ThisDecl && DeclaratorInfo.isStaticMember())
32653266
// No initializer.
32663267
Actions.ActOnUninitializedDecl(ThisDecl);
32673268

clang/lib/Sema/DeclSpec.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -416,6 +416,7 @@ bool Declarator::isDeclarationOfFunction() const {
416416
bool Declarator::isStaticMember() {
417417
assert(getContext() == DeclaratorContext::Member);
418418
return getDeclSpec().getStorageClassSpec() == DeclSpec::SCS_static ||
419+
(!isDeclarationOfFunction() && !getTemplateParameterLists().empty()) ||
419420
(getName().getKind() == UnqualifiedIdKind::IK_OperatorFunctionId &&
420421
CXXMethodDecl::isStaticOverloadedOperator(
421422
getName().OperatorFunctionId.Operator));

clang/lib/Sema/SemaDecl.cpp

Lines changed: 134 additions & 114 deletions
Original file line numberDiff line numberDiff line change
@@ -7601,89 +7601,16 @@ NamedDecl *Sema::ActOnVariableDeclarator(
76017601
NTCUC_AutoVar, NTCUK_Destruct);
76027602
} else {
76037603
bool Invalid = false;
7604-
7605-
if (DC->isRecord() && !CurContext->isRecord()) {
7606-
// This is an out-of-line definition of a static data member.
7607-
switch (SC) {
7608-
case SC_None:
7609-
break;
7610-
case SC_Static:
7611-
Diag(D.getDeclSpec().getStorageClassSpecLoc(),
7612-
diag::err_static_out_of_line)
7613-
<< FixItHint::CreateRemoval(D.getDeclSpec().getStorageClassSpecLoc());
7614-
break;
7615-
case SC_Auto:
7616-
case SC_Register:
7617-
case SC_Extern:
7618-
// [dcl.stc] p2: The auto or register specifiers shall be applied only
7619-
// to names of variables declared in a block or to function parameters.
7620-
// [dcl.stc] p6: The extern specifier cannot be used in the declaration
7621-
// of class members
7622-
7623-
Diag(D.getDeclSpec().getStorageClassSpecLoc(),
7624-
diag::err_storage_class_for_static_member)
7625-
<< FixItHint::CreateRemoval(D.getDeclSpec().getStorageClassSpecLoc());
7626-
break;
7627-
case SC_PrivateExtern:
7628-
llvm_unreachable("C storage class in c++!");
7629-
}
7630-
}
7631-
7632-
if (SC == SC_Static && CurContext->isRecord()) {
7633-
if (const CXXRecordDecl *RD = dyn_cast<CXXRecordDecl>(DC)) {
7634-
// Walk up the enclosing DeclContexts to check for any that are
7635-
// incompatible with static data members.
7636-
const DeclContext *FunctionOrMethod = nullptr;
7637-
const CXXRecordDecl *AnonStruct = nullptr;
7638-
for (DeclContext *Ctxt = DC; Ctxt; Ctxt = Ctxt->getParent()) {
7639-
if (Ctxt->isFunctionOrMethod()) {
7640-
FunctionOrMethod = Ctxt;
7641-
break;
7642-
}
7643-
const CXXRecordDecl *ParentDecl = dyn_cast<CXXRecordDecl>(Ctxt);
7644-
if (ParentDecl && !ParentDecl->getDeclName()) {
7645-
AnonStruct = ParentDecl;
7646-
break;
7647-
}
7648-
}
7649-
if (FunctionOrMethod) {
7650-
// C++ [class.static.data]p5: A local class shall not have static data
7651-
// members.
7652-
Diag(D.getIdentifierLoc(),
7653-
diag::err_static_data_member_not_allowed_in_local_class)
7654-
<< Name << RD->getDeclName()
7655-
<< llvm::to_underlying(RD->getTagKind());
7656-
} else if (AnonStruct) {
7657-
// C++ [class.static.data]p4: Unnamed classes and classes contained
7658-
// directly or indirectly within unnamed classes shall not contain
7659-
// static data members.
7660-
Diag(D.getIdentifierLoc(),
7661-
diag::err_static_data_member_not_allowed_in_anon_struct)
7662-
<< Name << llvm::to_underlying(AnonStruct->getTagKind());
7663-
Invalid = true;
7664-
} else if (RD->isUnion()) {
7665-
// C++98 [class.union]p1: If a union contains a static data member,
7666-
// the program is ill-formed. C++11 drops this restriction.
7667-
Diag(D.getIdentifierLoc(),
7668-
getLangOpts().CPlusPlus11
7669-
? diag::warn_cxx98_compat_static_data_member_in_union
7670-
: diag::ext_static_data_member_in_union) << Name;
7671-
}
7672-
}
7673-
}
7674-
76757604
// Match up the template parameter lists with the scope specifier, then
76767605
// determine whether we have a template or a template specialization.
7677-
bool InvalidScope = false;
76787606
TemplateParams = MatchTemplateParametersToScopeSpecifier(
76797607
D.getDeclSpec().getBeginLoc(), D.getIdentifierLoc(),
76807608
D.getCXXScopeSpec(),
76817609
D.getName().getKind() == UnqualifiedIdKind::IK_TemplateId
76827610
? D.getName().TemplateId
76837611
: nullptr,
76847612
TemplateParamLists,
7685-
/*never a friend*/ false, IsMemberSpecialization, InvalidScope);
7686-
Invalid |= InvalidScope;
7613+
/*never a friend*/ false, IsMemberSpecialization, Invalid);
76877614

76887615
if (TemplateParams) {
76897616
if (!TemplateParams->size() &&
@@ -7726,6 +7653,102 @@ NamedDecl *Sema::ActOnVariableDeclarator(
77267653
"should have a 'template<>' for this decl");
77277654
}
77287655

7656+
bool IsExplicitSpecialization =
7657+
IsVariableTemplateSpecialization && !IsPartialSpecialization;
7658+
7659+
// C++ [temp.expl.spec]p2:
7660+
// The declaration in an explicit-specialization shall not be an
7661+
// export-declaration. An explicit specialization shall not use a
7662+
// storage-class-specifier other than thread_local.
7663+
//
7664+
// We use the storage-class-specifier from DeclSpec because we may have
7665+
// added implicit 'extern' for declarations with __declspec(dllimport)!
7666+
if (SCSpec != DeclSpec::SCS_unspecified &&
7667+
(IsExplicitSpecialization || IsMemberSpecialization)) {
7668+
Diag(D.getDeclSpec().getStorageClassSpecLoc(),
7669+
diag::ext_explicit_specialization_storage_class)
7670+
<< FixItHint::CreateRemoval(D.getDeclSpec().getStorageClassSpecLoc());
7671+
}
7672+
7673+
if (CurContext->isRecord()) {
7674+
if (SC == SC_Static) {
7675+
if (const CXXRecordDecl *RD = dyn_cast<CXXRecordDecl>(DC)) {
7676+
// Walk up the enclosing DeclContexts to check for any that are
7677+
// incompatible with static data members.
7678+
const DeclContext *FunctionOrMethod = nullptr;
7679+
const CXXRecordDecl *AnonStruct = nullptr;
7680+
for (DeclContext *Ctxt = DC; Ctxt; Ctxt = Ctxt->getParent()) {
7681+
if (Ctxt->isFunctionOrMethod()) {
7682+
FunctionOrMethod = Ctxt;
7683+
break;
7684+
}
7685+
const CXXRecordDecl *ParentDecl = dyn_cast<CXXRecordDecl>(Ctxt);
7686+
if (ParentDecl && !ParentDecl->getDeclName()) {
7687+
AnonStruct = ParentDecl;
7688+
break;
7689+
}
7690+
}
7691+
if (FunctionOrMethod) {
7692+
// C++ [class.static.data]p5: A local class shall not have static
7693+
// data members.
7694+
Diag(D.getIdentifierLoc(),
7695+
diag::err_static_data_member_not_allowed_in_local_class)
7696+
<< Name << RD->getDeclName()
7697+
<< llvm::to_underlying(RD->getTagKind());
7698+
} else if (AnonStruct) {
7699+
// C++ [class.static.data]p4: Unnamed classes and classes contained
7700+
// directly or indirectly within unnamed classes shall not contain
7701+
// static data members.
7702+
Diag(D.getIdentifierLoc(),
7703+
diag::err_static_data_member_not_allowed_in_anon_struct)
7704+
<< Name << llvm::to_underlying(AnonStruct->getTagKind());
7705+
Invalid = true;
7706+
} else if (RD->isUnion()) {
7707+
// C++98 [class.union]p1: If a union contains a static data member,
7708+
// the program is ill-formed. C++11 drops this restriction.
7709+
Diag(D.getIdentifierLoc(),
7710+
getLangOpts().CPlusPlus11
7711+
? diag::warn_cxx98_compat_static_data_member_in_union
7712+
: diag::ext_static_data_member_in_union)
7713+
<< Name;
7714+
}
7715+
}
7716+
} else if (IsVariableTemplate || IsPartialSpecialization) {
7717+
// There is no such thing as a member field template.
7718+
Diag(D.getIdentifierLoc(), diag::err_template_member)
7719+
<< II << TemplateParams->getSourceRange();
7720+
// Recover by pretending this is a static data member template.
7721+
SC = SC_Static;
7722+
}
7723+
} else if (DC->isRecord()) {
7724+
// This is an out-of-line definition of a static data member.
7725+
switch (SC) {
7726+
case SC_None:
7727+
break;
7728+
case SC_Static:
7729+
Diag(D.getDeclSpec().getStorageClassSpecLoc(),
7730+
diag::err_static_out_of_line)
7731+
<< FixItHint::CreateRemoval(
7732+
D.getDeclSpec().getStorageClassSpecLoc());
7733+
break;
7734+
case SC_Auto:
7735+
case SC_Register:
7736+
case SC_Extern:
7737+
// [dcl.stc] p2: The auto or register specifiers shall be applied only
7738+
// to names of variables declared in a block or to function parameters.
7739+
// [dcl.stc] p6: The extern specifier cannot be used in the declaration
7740+
// of class members
7741+
7742+
Diag(D.getDeclSpec().getStorageClassSpecLoc(),
7743+
diag::err_storage_class_for_static_member)
7744+
<< FixItHint::CreateRemoval(
7745+
D.getDeclSpec().getStorageClassSpecLoc());
7746+
break;
7747+
case SC_PrivateExtern:
7748+
llvm_unreachable("C storage class in c++!");
7749+
}
7750+
}
7751+
77297752
if (IsVariableTemplateSpecialization) {
77307753
SourceLocation TemplateKWLoc =
77317754
TemplateParamLists.size() > 0
@@ -7771,8 +7794,6 @@ NamedDecl *Sema::ActOnVariableDeclarator(
77717794
// the variable (matching the scope specifier), store them.
77727795
// An explicit variable template specialization does not own any template
77737796
// parameter lists.
7774-
bool IsExplicitSpecialization =
7775-
IsVariableTemplateSpecialization && !IsPartialSpecialization;
77767797
unsigned VDTemplateParamLists =
77777798
(TemplateParams && !IsExplicitSpecialization) ? 1 : 0;
77787799
if (TemplateParamLists.size() > VDTemplateParamLists)
@@ -10202,25 +10223,45 @@ Sema::ActOnFunctionDeclarator(Scope *S, Declarator &D, DeclContext *DC,
1020210223
NewFD->setImplicitlyInline(ImplicitInlineCXX20);
1020310224
}
1020410225

10205-
if (SC == SC_Static && isa<CXXMethodDecl>(NewFD) &&
10206-
!CurContext->isRecord()) {
10207-
// C++ [class.static]p1:
10208-
// A data or function member of a class may be declared static
10209-
// in a class definition, in which case it is a static member of
10210-
// the class.
10226+
if (!isFriend && SC != SC_None) {
10227+
// C++ [temp.expl.spec]p2:
10228+
// The declaration in an explicit-specialization shall not be an
10229+
// export-declaration. An explicit specialization shall not use a
10230+
// storage-class-specifier other than thread_local.
10231+
//
10232+
// We diagnose friend declarations with storage-class-specifiers
10233+
// elsewhere.
10234+
if (isFunctionTemplateSpecialization || isMemberSpecialization) {
10235+
Diag(D.getDeclSpec().getStorageClassSpecLoc(),
10236+
diag::ext_explicit_specialization_storage_class)
10237+
<< FixItHint::CreateRemoval(
10238+
D.getDeclSpec().getStorageClassSpecLoc());
10239+
}
1021110240

10212-
// Complain about the 'static' specifier if it's on an out-of-line
10213-
// member function definition.
10241+
if (SC == SC_Static && !CurContext->isRecord() && DC->isRecord()) {
10242+
assert(isa<CXXMethodDecl>(NewFD) &&
10243+
"Out-of-line member function should be a CXXMethodDecl");
10244+
// C++ [class.static]p1:
10245+
// A data or function member of a class may be declared static
10246+
// in a class definition, in which case it is a static member of
10247+
// the class.
1021410248

10215-
// MSVC permits the use of a 'static' storage specifier on an out-of-line
10216-
// member function template declaration and class member template
10217-
// declaration (MSVC versions before 2015), warn about this.
10218-
Diag(D.getDeclSpec().getStorageClassSpecLoc(),
10219-
((!getLangOpts().isCompatibleWithMSVC(LangOptions::MSVC2015) &&
10220-
cast<CXXRecordDecl>(DC)->getDescribedClassTemplate()) ||
10221-
(getLangOpts().MSVCCompat && NewFD->getDescribedFunctionTemplate()))
10222-
? diag::ext_static_out_of_line : diag::err_static_out_of_line)
10223-
<< FixItHint::CreateRemoval(D.getDeclSpec().getStorageClassSpecLoc());
10249+
// Complain about the 'static' specifier if it's on an out-of-line
10250+
// member function definition.
10251+
10252+
// MSVC permits the use of a 'static' storage specifier on an
10253+
// out-of-line member function template declaration and class member
10254+
// template declaration (MSVC versions before 2015), warn about this.
10255+
Diag(D.getDeclSpec().getStorageClassSpecLoc(),
10256+
((!getLangOpts().isCompatibleWithMSVC(LangOptions::MSVC2015) &&
10257+
cast<CXXRecordDecl>(DC)->getDescribedClassTemplate()) ||
10258+
(getLangOpts().MSVCCompat &&
10259+
NewFD->getDescribedFunctionTemplate()))
10260+
? diag::ext_static_out_of_line
10261+
: diag::err_static_out_of_line)
10262+
<< FixItHint::CreateRemoval(
10263+
D.getDeclSpec().getStorageClassSpecLoc());
10264+
}
1022410265
}
1022510266

1022610267
// C++11 [except.spec]p15:
@@ -10588,27 +10629,6 @@ Sema::ActOnFunctionDeclarator(Scope *S, Declarator &D, DeclContext *DC,
1058810629
Previous))
1058910630
NewFD->setInvalidDecl();
1059010631
}
10591-
10592-
// C++ [dcl.stc]p1:
10593-
// A storage-class-specifier shall not be specified in an explicit
10594-
// specialization (14.7.3)
10595-
// FIXME: We should be checking this for dependent specializations.
10596-
FunctionTemplateSpecializationInfo *Info =
10597-
NewFD->getTemplateSpecializationInfo();
10598-
if (Info && SC != SC_None) {
10599-
if (SC != Info->getTemplate()->getTemplatedDecl()->getStorageClass())
10600-
Diag(NewFD->getLocation(),
10601-
diag::err_explicit_specialization_inconsistent_storage_class)
10602-
<< SC
10603-
<< FixItHint::CreateRemoval(
10604-
D.getDeclSpec().getStorageClassSpecLoc());
10605-
10606-
else
10607-
Diag(NewFD->getLocation(),
10608-
diag::ext_explicit_specialization_storage_class)
10609-
<< FixItHint::CreateRemoval(
10610-
D.getDeclSpec().getStorageClassSpecLoc());
10611-
}
1061210632
} else if (isMemberSpecialization && isa<CXXMethodDecl>(NewFD)) {
1061310633
if (CheckMemberSpecialization(NewFD, Previous))
1061410634
NewFD->setInvalidDecl();

clang/lib/Sema/SemaDeclCXX.cpp

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3489,9 +3489,9 @@ Sema::ActOnCXXMemberDeclarator(Scope *S, AccessSpecifier AS, Declarator &D,
34893489
break;
34903490
}
34913491

3492-
bool isInstField = ((DS.getStorageClassSpec() == DeclSpec::SCS_unspecified ||
3493-
DS.getStorageClassSpec() == DeclSpec::SCS_mutable) &&
3494-
!isFunc);
3492+
bool isInstField = (DS.getStorageClassSpec() == DeclSpec::SCS_unspecified ||
3493+
DS.getStorageClassSpec() == DeclSpec::SCS_mutable) &&
3494+
!isFunc && TemplateParameterLists.empty();
34953495

34963496
if (DS.hasConstexprSpecifier() && isInstField) {
34973497
SemaDiagnosticBuilder B =
@@ -3541,6 +3541,7 @@ Sema::ActOnCXXMemberDeclarator(Scope *S, AccessSpecifier AS, Declarator &D,
35413541

35423542
IdentifierInfo *II = Name.getAsIdentifierInfo();
35433543

3544+
#if 0
35443545
// Member field could not be with "template" keyword.
35453546
// So TemplateParameterLists should be empty in this case.
35463547
if (TemplateParameterLists.size()) {
@@ -3561,6 +3562,7 @@ Sema::ActOnCXXMemberDeclarator(Scope *S, AccessSpecifier AS, Declarator &D,
35613562
}
35623563
return nullptr;
35633564
}
3565+
#endif
35643566

35653567
if (D.getName().getKind() == UnqualifiedIdKind::IK_TemplateId) {
35663568
Diag(D.getIdentifierLoc(), diag::err_member_with_template_arguments)

clang/test/CXX/dcl.dcl/dcl.spec/dcl.stc/p1.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ template<typename T> void f(T) {}
77
template<typename T> static void g(T) {}
88

99

10-
template<> static void f<int>(int); // expected-error{{explicit specialization has extraneous, inconsistent storage class 'static'}}
10+
template<> static void f<int>(int); // expected-warning{{explicit specialization cannot have a storage class}}
1111
template static void f<float>(float); // expected-error{{explicit instantiation cannot have a storage class}}
1212

1313
template<> void f<double>(double);
@@ -29,4 +29,5 @@ int X<T>::value = 17;
2929

3030
template static int X<int>::value; // expected-error{{explicit instantiation cannot have a storage class}}
3131

32-
template<> static int X<float>::value; // expected-error{{'static' can only be specified inside the class definition}}
32+
template<> static int X<float>::value; // expected-warning{{explicit specialization cannot have a storage class}}
33+
// expected-error@-1{{'static' can only be specified inside the class definition}}

0 commit comments

Comments
 (0)