]> granicus.if.org Git - clang/commitdiff
Diagnose the various invalid decl-specifiers on nontype template parameters.
authorFaisal Vali <faisalv@yahoo.com>
Fri, 22 Dec 2017 03:50:55 +0000 (03:50 +0000)
committerFaisal Vali <faisalv@yahoo.com>
Fri, 22 Dec 2017 03:50:55 +0000 (03:50 +0000)
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

include/clang/Basic/DiagnosticSemaKinds.td
lib/Sema/SemaTemplate.cpp
test/CXX/temp/temp.param/p2.cpp

index 29236eab5446c7c56ed639b428dd86f4380e409b..01e819942f686fcfcff3de882234280fc91f169d 100644 (file)
@@ -3911,6 +3911,9 @@ def err_template_param_different_kind : Error<
   "%select{|template parameter }0redeclaration">;
 def note_template_param_different_kind : Note<
   "template parameter has a different kind in template argument">;
+
+def err_invalid_decl_specifier_in_nontype_parm : Error<
+  "invalid declaration specifier in template non-type parameter">;
   
 def err_template_nontype_parm_different_type : Error<
   "template non-type parameter has a different type %0 in template "
index c70a8ba8f1269d90f8fb514186f581461607c4cb..4ce4a90e55143c6df8f314a2cd40661ddcc66181 100644 (file)
@@ -929,6 +929,60 @@ Decl *Sema::ActOnNonTypeTemplateParameter(Scope *S, Declarator &D,
                                           Expr *Default) {
   TypeSourceInfo *TInfo = GetTypeForDeclarator(D, S);
 
+  // Check that we have valid decl-specifiers specified.
+  auto CheckValidDeclSpecifiers = [this, &D] {
+    // C++ [temp.param]
+    // p1 
+    //   template-parameter:\r
+    //     ...\r
+    //     parameter-declaration\r
+    // p2 
+    //   ... A storage class shall not be specified in a template-parameter
+    //   declaration.
+    // [dcl.typedef]p1: 
+    //   The typedef specifier [...] shall not be used in the decl-specifier-seq
+    //   of a parameter-declaration
+    const DeclSpec &DS = D.getDeclSpec();
+    auto EmitDiag = [this](SourceLocation Loc) {
+      Diag(Loc, diag::err_invalid_decl_specifier_in_nontype_parm)
+          << FixItHint::CreateRemoval(Loc);
+    };
+    if (DS.getStorageClassSpec() != DeclSpec::SCS_unspecified)
+      EmitDiag(DS.getStorageClassSpecLoc());
+    
+    if (DeclSpec::TSCS TSCS = DS.getThreadStorageClassSpec())
+      EmitDiag(DS.getThreadStorageClassSpecLoc());
+    
+    // [dcl.inline]p1: 
+    //   The inline specifier can be applied only to the declaration or 
+    //   definition of a variable or function.
+    
+    if (DS.isInlineSpecified())
+      EmitDiag(DS.getInlineSpecLoc());
+    
+    // [dcl.constexpr]p1:
+    //   The constexpr specifier shall be applied only to the definition of a 
+    //   variable or variable template or the declaration of a function or 
+    //   function template.
+    
+    if (DS.isConstexprSpecified())
+      EmitDiag(DS.getConstexprSpecLoc());
+
+    // [dcl.fct.spec]p1:
+    //   Function-specifiers can be used only in function declarations.
+
+    if (DS.isVirtualSpecified())
+      EmitDiag(DS.getVirtualSpecLoc());
+
+    if (DS.isExplicitSpecified())
+      EmitDiag(DS.getExplicitSpecLoc());
+
+    if (DS.isNoreturnSpecified())
+      EmitDiag(DS.getNoreturnSpecLoc());
+  };
+
+  CheckValidDeclSpecifiers();
+  
   if (TInfo->getType()->isUndeducedType()) {
     Diag(D.getIdentifierLoc(),
          diag::warn_cxx14_compat_template_nontype_parm_auto_type)
index 4eca05774ab5c0b0fb02b5cf418e3f3071e63a78..20e0b8ef35f8b1bba84598ca1a3f928e5fb7a8c2 100644 (file)
@@ -1,5 +1,6 @@
-// RUN: %clang_cc1 -fsyntax-only -verify %s
-// expected-no-diagnostics
+// RUN: %clang_cc1 -fsyntax-only -verify %s 
+// RUN: %clang_cc1 -std=c++11 -fsyntax-only -verify %s -DCPP11
+// RUN: %clang_cc1 -std=c++17 -fsyntax-only -verify %s -DCPP17
 
 // There is no semantic difference between class and typename in a
 // template-parameter. typename followed by an unqualified-id names a
@@ -13,7 +14,31 @@ template<typename T, typename T::type Value> struct Y0;
 template<typename T, typename X<T>::type Value> struct Y1;
 
 // A storage class shall not be specified in a template-parameter declaration.
-template<static int Value> struct Z; // FIXME: expect an error
+template<static int Value> struct Z; //expected-error{{invalid declaration specifier}}
+template<typedef int Value> struct Z0; //expected-error{{invalid declaration specifier}}
+template<extern inline int Value> struct Z0; //expected-error2{{invalid declaration specifier}}
+template<virtual int Value> struct Z0; //expected-error{{invalid declaration specifier}}
+template<explicit int Value> struct Z0; //expected-error{{invalid declaration specifier}}
+template<inline int Value> struct Z0; //expected-error{{invalid declaration specifier}}
+template<extern int> struct Z0; //expected-error{{invalid declaration specifier}}
+template<static int> struct Z0;  //expected-error{{invalid declaration specifier}}
+template<explicit int Value> struct Z0; //expected-error{{invalid declaration specifier}}
+template<mutable int> struct Z0; //expected-error{{invalid declaration specifier}}
+
+template<const int> struct Z0; // OK 
+template<volatile int> struct Z0; // OK
+
+
+
+#ifdef CPP11
+template<thread_local int> struct Z0; //expected-error{{invalid declaration specifier}}
+template<constexpr int> struct Z0; //expected-error{{invalid declaration specifier}}
+
+#endif
+
+#ifdef CPP17
+template<auto> struct Z1; // OK
+#endif
 
 // Make sure that we properly disambiguate non-type template parameters that
 // start with 'class'.