Skip to content
This repository was archived by the owner on Nov 1, 2021. It is now read-only.

Commit c6a6f0c

Browse files
committed
Diagnose the various invalid decl-specifiers on nontype template parameters.
The standard correctly forbids various decl-specifiers that dont make sense on non-type template parameters - such as the extern in: template<extern int> struct X; This patch implements those restrictions (in a fashion similar to the corresponding checks on function parameters within ActOnParamDeclarator). Credit goes to miyuki (Mikhail Maltsev) for drawing attention to this issue, authoring the initial versions of this patch, and supporting the effort to re-engineer it slightly. Thank you! For details of how this patch evolved please see: https://reviews.llvm.org/D40705 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@321339 91177308-0d34-0410-b5e6-96231b3b80d8
1 parent aea3aa1 commit c6a6f0c

File tree

3 files changed

+85
-3
lines changed

3 files changed

+85
-3
lines changed

include/clang/Basic/DiagnosticSemaKinds.td

+3
Original file line numberDiff line numberDiff line change
@@ -3911,6 +3911,9 @@ def err_template_param_different_kind : Error<
39113911
"%select{|template parameter }0redeclaration">;
39123912
def note_template_param_different_kind : Note<
39133913
"template parameter has a different kind in template argument">;
3914+
3915+
def err_invalid_decl_specifier_in_nontype_parm : Error<
3916+
"invalid declaration specifier in template non-type parameter">;
39143917

39153918
def err_template_nontype_parm_different_type : Error<
39163919
"template non-type parameter has a different type %0 in template "

lib/Sema/SemaTemplate.cpp

+54
Original file line numberDiff line numberDiff line change
@@ -929,6 +929,60 @@ Decl *Sema::ActOnNonTypeTemplateParameter(Scope *S, Declarator &D,
929929
Expr *Default) {
930930
TypeSourceInfo *TInfo = GetTypeForDeclarator(D, S);
931931

932+
// Check that we have valid decl-specifiers specified.
933+
auto CheckValidDeclSpecifiers = [this, &D] {
934+
// C++ [temp.param]
935+
// p1
936+
// template-parameter:
937+
// ...
938+
// parameter-declaration
939+
// p2
940+
// ... A storage class shall not be specified in a template-parameter
941+
// declaration.
942+
// [dcl.typedef]p1:
943+
// The typedef specifier [...] shall not be used in the decl-specifier-seq
944+
// of a parameter-declaration
945+
const DeclSpec &DS = D.getDeclSpec();
946+
auto EmitDiag = [this](SourceLocation Loc) {
947+
Diag(Loc, diag::err_invalid_decl_specifier_in_nontype_parm)
948+
<< FixItHint::CreateRemoval(Loc);
949+
};
950+
if (DS.getStorageClassSpec() != DeclSpec::SCS_unspecified)
951+
EmitDiag(DS.getStorageClassSpecLoc());
952+
953+
if (DeclSpec::TSCS TSCS = DS.getThreadStorageClassSpec())
954+
EmitDiag(DS.getThreadStorageClassSpecLoc());
955+
956+
// [dcl.inline]p1:
957+
// The inline specifier can be applied only to the declaration or
958+
// definition of a variable or function.
959+
960+
if (DS.isInlineSpecified())
961+
EmitDiag(DS.getInlineSpecLoc());
962+
963+
// [dcl.constexpr]p1:
964+
// The constexpr specifier shall be applied only to the definition of a
965+
// variable or variable template or the declaration of a function or
966+
// function template.
967+
968+
if (DS.isConstexprSpecified())
969+
EmitDiag(DS.getConstexprSpecLoc());
970+
971+
// [dcl.fct.spec]p1:
972+
// Function-specifiers can be used only in function declarations.
973+
974+
if (DS.isVirtualSpecified())
975+
EmitDiag(DS.getVirtualSpecLoc());
976+
977+
if (DS.isExplicitSpecified())
978+
EmitDiag(DS.getExplicitSpecLoc());
979+
980+
if (DS.isNoreturnSpecified())
981+
EmitDiag(DS.getNoreturnSpecLoc());
982+
};
983+
984+
CheckValidDeclSpecifiers();
985+
932986
if (TInfo->getType()->isUndeducedType()) {
933987
Diag(D.getIdentifierLoc(),
934988
diag::warn_cxx14_compat_template_nontype_parm_auto_type)

test/CXX/temp/temp.param/p2.cpp

+28-3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1-
// RUN: %clang_cc1 -fsyntax-only -verify %s
2-
// expected-no-diagnostics
1+
// RUN: %clang_cc1 -fsyntax-only -verify %s
2+
// RUN: %clang_cc1 -std=c++11 -fsyntax-only -verify %s -DCPP11
3+
// RUN: %clang_cc1 -std=c++17 -fsyntax-only -verify %s -DCPP17
34

45
// There is no semantic difference between class and typename in a
56
// template-parameter. typename followed by an unqualified-id names a
@@ -13,7 +14,31 @@ template<typename T, typename T::type Value> struct Y0;
1314
template<typename T, typename X<T>::type Value> struct Y1;
1415

1516
// A storage class shall not be specified in a template-parameter declaration.
16-
template<static int Value> struct Z; // FIXME: expect an error
17+
template<static int Value> struct Z; //expected-error{{invalid declaration specifier}}
18+
template<typedef int Value> struct Z0; //expected-error{{invalid declaration specifier}}
19+
template<extern inline int Value> struct Z0; //expected-error2{{invalid declaration specifier}}
20+
template<virtual int Value> struct Z0; //expected-error{{invalid declaration specifier}}
21+
template<explicit int Value> struct Z0; //expected-error{{invalid declaration specifier}}
22+
template<inline int Value> struct Z0; //expected-error{{invalid declaration specifier}}
23+
template<extern int> struct Z0; //expected-error{{invalid declaration specifier}}
24+
template<static int> struct Z0; //expected-error{{invalid declaration specifier}}
25+
template<explicit int Value> struct Z0; //expected-error{{invalid declaration specifier}}
26+
template<mutable int> struct Z0; //expected-error{{invalid declaration specifier}}
27+
28+
template<const int> struct Z0; // OK
29+
template<volatile int> struct Z0; // OK
30+
31+
32+
33+
#ifdef CPP11
34+
template<thread_local int> struct Z0; //expected-error{{invalid declaration specifier}}
35+
template<constexpr int> struct Z0; //expected-error{{invalid declaration specifier}}
36+
37+
#endif
38+
39+
#ifdef CPP17
40+
template<auto> struct Z1; // OK
41+
#endif
1742

1843
// Make sure that we properly disambiguate non-type template parameters that
1944
// start with 'class'.

0 commit comments

Comments
 (0)