From: Faisal Vali Date: Fri, 22 Dec 2017 03:50:55 +0000 (+0000) Subject: Diagnose the various invalid decl-specifiers on nontype template parameters. X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=c6a6f0c582ee63f3b04ee1a2e7591793531eae23;p=clang 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 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 --- diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td index 29236eab54..01e819942f 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.td +++ b/include/clang/Basic/DiagnosticSemaKinds.td @@ -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 " diff --git a/lib/Sema/SemaTemplate.cpp b/lib/Sema/SemaTemplate.cpp index c70a8ba8f1..4ce4a90e55 100644 --- a/lib/Sema/SemaTemplate.cpp +++ b/lib/Sema/SemaTemplate.cpp @@ -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: + // ... + // parameter-declaration + // 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) diff --git a/test/CXX/temp/temp.param/p2.cpp b/test/CXX/temp/temp.param/p2.cpp index 4eca05774a..20e0b8ef35 100644 --- a/test/CXX/temp/temp.param/p2.cpp +++ b/test/CXX/temp/temp.param/p2.cpp @@ -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 struct Y0; template::type Value> struct Y1; // A storage class shall not be specified in a template-parameter declaration. -template struct Z; // FIXME: expect an error +template struct Z; //expected-error{{invalid declaration specifier}} +template struct Z0; //expected-error{{invalid declaration specifier}} +template struct Z0; //expected-error2{{invalid declaration specifier}} +template struct Z0; //expected-error{{invalid declaration specifier}} +template struct Z0; //expected-error{{invalid declaration specifier}} +template struct Z0; //expected-error{{invalid declaration specifier}} +template struct Z0; //expected-error{{invalid declaration specifier}} +template struct Z0; //expected-error{{invalid declaration specifier}} +template struct Z0; //expected-error{{invalid declaration specifier}} +template struct Z0; //expected-error{{invalid declaration specifier}} + +template struct Z0; // OK +template struct Z0; // OK + + + +#ifdef CPP11 +template struct Z0; //expected-error{{invalid declaration specifier}} +template struct Z0; //expected-error{{invalid declaration specifier}} + +#endif + +#ifdef CPP17 +template struct Z1; // OK +#endif // Make sure that we properly disambiguate non-type template parameters that // start with 'class'.