[clang] a648834 - PR46729: Reject explicit and partial specializations with C linkage.

Richard Smith via cfe-commits cfe-commits at lists.llvm.org
Wed Jul 29 17:44:41 PDT 2020


Author: Richard Smith
Date: 2020-07-29T17:44:32-07:00
New Revision: a64883431369f28f3fac311c496a4dfad480058f

URL: https://github.com/llvm/llvm-project/commit/a64883431369f28f3fac311c496a4dfad480058f
DIFF: https://github.com/llvm/llvm-project/commit/a64883431369f28f3fac311c496a4dfad480058f.diff

LOG: PR46729: Reject explicit and partial specializations with C linkage.

Added: 
    clang/test/CXX/temp/temp.pre/p6.cpp

Modified: 
    clang/lib/Sema/SemaDecl.cpp
    clang/lib/Sema/SemaTemplate.cpp
    clang/test/SemaTemplate/class-template-decl.cpp

Removed: 
    


################################################################################
diff  --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index 9b9d164dcbc1..531c2801bf92 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -6994,19 +6994,18 @@ NamedDecl *Sema::ActOnVariableDeclarator(
                          TemplateParams->getRAngleLoc());
         TemplateParams = nullptr;
       } else {
+        // Check that we can declare a template here.
+        if (CheckTemplateDeclScope(S, TemplateParams))
+          return nullptr;
+
         if (D.getName().getKind() == UnqualifiedIdKind::IK_TemplateId) {
           // This is an explicit specialization or a partial specialization.
-          // FIXME: Check that we can declare a specialization here.
           IsVariableTemplateSpecialization = true;
           IsPartialSpecialization = TemplateParams->size() > 0;
         } else { // if (TemplateParams->size() > 0)
           // This is a template declaration.
           IsVariableTemplate = true;
 
-          // Check that we can declare a template here.
-          if (CheckTemplateDeclScope(S, TemplateParams))
-            return nullptr;
-
           // Only C++1y supports variable templates (N3651).
           Diag(D.getIdentifierLoc(),
                getLangOpts().CPlusPlus14
@@ -7015,6 +7014,10 @@ NamedDecl *Sema::ActOnVariableDeclarator(
         }
       }
     } else {
+      // Check that we can declare a member specialization here.
+      if (!TemplateParamLists.empty() && IsMemberSpecialization &&
+          CheckTemplateDeclScope(S, TemplateParamLists.back()))
+        return nullptr;
       assert((Invalid ||
               D.getName().getKind() != UnqualifiedIdKind::IK_TemplateId) &&
              "should have a 'template<>' for this decl");
@@ -8941,13 +8944,13 @@ Sema::ActOnFunctionDeclarator(Scope *S, Declarator &D, DeclContext *DC,
             TemplateParamLists, isFriend, isMemberSpecialization,
             Invalid);
     if (TemplateParams) {
+      // Check that we can declare a template here.
+      if (CheckTemplateDeclScope(S, TemplateParams))
+        NewFD->setInvalidDecl();
+
       if (TemplateParams->size() > 0) {
         // This is a function template
 
-        // Check that we can declare a template here.
-        if (CheckTemplateDeclScope(S, TemplateParams))
-          NewFD->setInvalidDecl();
-
         // A destructor cannot be a template.
         if (Name.getNameKind() == DeclarationName::CXXDestructorName) {
           Diag(NewFD->getLocation(), diag::err_destructor_template);
@@ -9006,6 +9009,11 @@ Sema::ActOnFunctionDeclarator(Scope *S, Declarator &D, DeclContext *DC,
         }
       }
     } else {
+      // Check that we can declare a template here.
+      if (!TemplateParamLists.empty() && isMemberSpecialization &&
+          CheckTemplateDeclScope(S, TemplateParamLists.back()))
+        NewFD->setInvalidDecl();
+
       // All template param lists were matched against the scope specifier:
       // this is NOT (an explicit specialization of) a template.
       if (TemplateParamLists.size() > 0)
@@ -15301,6 +15309,10 @@ Decl *Sema::ActOnTag(Scope *S, unsigned TagSpec, TagUseKind TUK,
         isMemberSpecialization = true;
       }
     }
+
+    if (!TemplateParameterLists.empty() && isMemberSpecialization &&
+        CheckTemplateDeclScope(S, TemplateParameterLists.back()))
+      return nullptr;
   }
 
   // Figure out the underlying type if this a enum declaration. We need to do
@@ -17300,7 +17312,7 @@ void Sema::ActOnFields(Scope *S, SourceLocation RecLoc, Decl *EnclosingDecl,
       CXXRecordDecl *CXXRecord = cast<CXXRecordDecl>(Record);
       CheckForZeroSize =
           CXXRecord->getLexicalDeclContext()->isExternCContext() &&
-          !CXXRecord->isDependentType() &&
+          !CXXRecord->isDependentType() && !inTemplateInstantiation() &&
           CXXRecord->isCLike();
     }
     if (CheckForZeroSize) {

diff  --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp
index 76ba12303d84..3991f2b47977 100644
--- a/clang/lib/Sema/SemaTemplate.cpp
+++ b/clang/lib/Sema/SemaTemplate.cpp
@@ -7771,8 +7771,9 @@ Sema::CheckTemplateDeclScope(Scope *S, TemplateParameterList *TemplateParams) {
          (S->getFlags() & Scope::TemplateParamScope) != 0)
     S = S->getParent();
 
-  // C++ [temp]p4:
-  //   A template [...] shall not have C linkage.
+  // C++ [temp.pre]p6: [P2096]
+  //   A template, explicit specialization, or partial specialization shall not
+  //   have C linkage.
   DeclContext *Ctx = S->getEntity();
   if (Ctx && Ctx->isExternCContext()) {
     Diag(TemplateParams->getTemplateLoc(), diag::err_template_linkage)
@@ -7786,6 +7787,12 @@ Sema::CheckTemplateDeclScope(Scope *S, TemplateParameterList *TemplateParams) {
   // C++ [temp]p2:
   //   A template-declaration can appear only as a namespace scope or
   //   class scope declaration.
+  // C++ [temp.expl.spec]p3:
+  //   An explicit specialization may be declared in any scope in which the
+  //   corresponding primary template may be defined.
+  // C++ [temp.class.spec]p6: [P2096]
+  //   A partial specialization may be declared in any scope in which the
+  //   corresponding primary template may be defined.
   if (Ctx) {
     if (Ctx->isFileContext())
       return false;
@@ -8105,6 +8112,10 @@ DeclResult Sema::ActOnClassTemplateSpecialization(
   if (Invalid)
     return true;
 
+  // Check that we can declare a template specialization here.
+  if (TemplateParams && CheckTemplateDeclScope(S, TemplateParams))
+    return true;
+
   if (TemplateParams && TemplateParams->size() > 0) {
     isPartialSpecialization = true;
 

diff  --git a/clang/test/CXX/temp/temp.pre/p6.cpp b/clang/test/CXX/temp/temp.pre/p6.cpp
new file mode 100644
index 000000000000..cb8c70ca3abe
--- /dev/null
+++ b/clang/test/CXX/temp/temp.pre/p6.cpp
@@ -0,0 +1,79 @@
+// RUN: %clang_cc1 -std=c++20 -verify %s
+
+// Templates and partial and explicit specializations can't have C linkage.
+namespace extern_c_templates {
+
+template<typename T> struct A {
+  static int a;
+  struct b;
+  void c();
+  enum class d;
+
+  template<typename U> static int e;
+  template<typename U> struct f;
+  template<typename U> void g();
+};
+
+template<typename T> int B;
+template<typename T> void C();
+
+extern "C" { // expected-note 1+{{begins here}}
+  // templates
+  template<typename T> struct A; // expected-error {{templates must have C++ linkage}}
+  template<typename T> int B; // expected-error {{templates must have C++ linkage}}
+  template<typename T> void C(); // expected-error {{templates must have C++ linkage}}
+
+  // non-template members of a template
+  // FIXME: Should these really be valid?
+  template<typename T> int A<T>::a;
+  template<typename T> struct A<T>::b {};
+  template<typename T> void A<T>::c() {}
+  template<typename T> enum class A<T>::d {};
+
+  // templates
+  template<typename T> template<typename U> int A<T>::e; // expected-error {{templates must have C++ linkage}}
+  template<typename T> template<typename U> struct A<T>::f {}; // expected-error {{templates must have C++ linkage}}
+  template<typename T> template<typename U> void A<T>::g() {} // expected-error {{templates must have C++ linkage}}
+
+  // partial specializations
+  template<typename T> struct A<int*>; // expected-error {{templates must have C++ linkage}}
+  template<typename T> int B<int*>; // expected-error {{templates must have C++ linkage}}
+  template<typename T> template<typename U> int A<T>::e<U*>; // expected-error {{templates must have C++ linkage}}
+  template<typename T> template<typename U> struct A<T>::f<U*> {}; // expected-error {{templates must have C++ linkage}}
+
+  // explicit specializations of templates
+  template<> struct A<char> {}; // expected-error {{templates must have C++ linkage}}
+  template<> int B<char>; // expected-error {{templates must have C++ linkage}}
+  template<> void C<char>() {} // expected-error {{templates must have C++ linkage}}
+
+  // explicit specializations of members of a template
+  template<> int A<int>::a; // expected-error {{templates must have C++ linkage}}
+  template<> struct A<int>::b {}; // expected-error {{templates must have C++ linkage}}
+  template<> void A<int>::c() {} // expected-error {{templates must have C++ linkage}}
+  template<> enum class A<int>::d {}; // expected-error {{templates must have C++ linkage}}
+
+  // explicit specializations of member templates
+  template<> template<typename U> int A<int>::e; // expected-error {{templates must have C++ linkage}}
+  template<> template<typename U> struct A<int>::f {}; // expected-error {{templates must have C++ linkage}}
+  template<> template<typename U> void A<int>::g() {} // expected-error {{templates must have C++ linkage}}
+}
+
+// Provide valid definitions for the explicit instantiations below.
+// FIXME: Our recovery from the invalid definitions above isn't very good.
+template<typename T> template<typename U> int A<T>::e;
+template<typename T> template<typename U> struct A<T>::f {};
+template<typename T> template<typename U> void A<T>::g() {}
+
+extern "C" {
+  // explicit instantiations
+  // FIXME: Should these really be valid?
+  template struct A<double>;
+  template int A<float>::a;
+  template struct A<float>::b;
+  template void A<float>::c();
+  template int A<float>::e<float>;
+  template struct A<float>::f<float>;
+  template void A<float>::g<float>();
+}
+
+}

diff  --git a/clang/test/SemaTemplate/class-template-decl.cpp b/clang/test/SemaTemplate/class-template-decl.cpp
index dd9dcd2de9f4..453218ac3b40 100644
--- a/clang/test/SemaTemplate/class-template-decl.cpp
+++ b/clang/test/SemaTemplate/class-template-decl.cpp
@@ -1,17 +1,26 @@
 // RUN: %clang_cc1 -std=c++14 -fsyntax-only -verify %s
 
-template<typename T> class A;
+template<typename T> class A {};
 
 extern "C++" {
-  template<typename T> class B;
+  template<typename T> class B {};
+  template<typename T> class A<T *>;
+  template<> class A<int[1]>;
+  template class A<int[2]>;
+  template<typename T> class B<T *>;
+  template<> class B<int[1]>;
+  template class B<int[2]>;
 }
 
 namespace N {
   template<typename T> class C;
 }
 
-extern "C" { // expected-note {{extern "C" language linkage specification begins here}}
+extern "C" { // expected-note 3 {{extern "C" language linkage specification begins here}}
   template<typename T> class D; // expected-error{{templates must have C++ linkage}}
+  template<typename T> class A<T **>; // expected-error{{templates must have C++ linkage}}
+  template<> class A<int[3]>; // expected-error{{templates must have C++ linkage}}
+  template class A<int[4]>; // OK (surprisingly) FIXME: Should we warn on this?
 }
 
 extern "C" { // expected-note 2 {{extern "C" language linkage specification begins here}}


        


More information about the cfe-commits mailing list