iains created this revision.
Herald added a subscriber: ChuanqiXu.
Herald added a project: All.
iains added a reviewer: ChuanqiXu.
iains added subscribers: clang-modules, h-vetinari.
iains published this revision for review.
iains added a comment.
Herald added a project: clang.
Herald added a subscriber: cfe-commits.

I think it is a good idea to keep this part of P2615R1 separate from the 
unnamed exports diagnostics [the parent commit] - it seems likely that there 
will be more fallout from this (given that there were already PRs about the 
behaviour of exported specialisations).


P2615R1 introduces diagnostics for partial and explicit specializations in 
module export declarations. This is applied as a DR to C++20.

Two testcases are removed since they were testing behaviour of imported 
specialisations (which are no longer permitted as exports).
Three testcases are amended to remove exports of specialisations, but retain 
the remainder of the test.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D153542

Files:
  clang/include/clang/Basic/DiagnosticSemaKinds.td
  clang/lib/Sema/SemaModule.cpp
  clang/test/CXX/module/module.interface/p1-p2615r1.cpp
  clang/test/CXX/temp/temp.explicit/p2-p2615r1.cpp
  clang/test/Modules/merge-var-template-spec-cxx-modules.cppm
  clang/test/Modules/pr59780.cppm
  clang/test/Modules/pr60890.cppm
  clang/test/Modules/pr62796.cppm
  clang/test/Modules/template-function-specialization.cpp
  clang/unittests/Serialization/VarDeclConstantInitTest.cpp
  clang/www/cxx_status.html

Index: clang/www/cxx_status.html
===================================================================
--- clang/www/cxx_status.html
+++ clang/www/cxx_status.html
@@ -772,7 +772,7 @@
       </tr>
       <tr> <!-- from Kona 2022 -->
         <td><a href="https://wg21.link/P2615R1";>P2615R1</a> (<a href="#dr">DR</a>)</td>
-        <td class="none" align="center">No</td>
+        <td class="none" align="center">Clang 17</td>
       </tr>
       <tr> <!-- from Issaquah 2023 -->
         <td><a href="https://wg21.link/P2788R0";>P2788R0</a> (<a href="#dr">DR</a>)</td>
Index: clang/unittests/Serialization/VarDeclConstantInitTest.cpp
===================================================================
--- clang/unittests/Serialization/VarDeclConstantInitTest.cpp
+++ clang/unittests/Serialization/VarDeclConstantInitTest.cpp
@@ -86,7 +86,6 @@
 	template<unsigned long N, typename Strategy = DefaultStrategy>
 	constexpr unsigned long Cache = Compute(Number<N>{}, Strategy{});
 
-  template constexpr unsigned long Cache<10ul>;
 }
   )cpp");
 
@@ -116,6 +115,7 @@
   std::unique_ptr<ASTUnit> AST = tooling::buildASTFromCodeWithArgs(
       R"cpp(
 import Fibonacci.Cache;
+template constexpr unsigned long Fibonacci::Cache<10ul>;
         )cpp",
       /*Args=*/{"-std=c++20", DepArg.c_str()});
 
Index: clang/test/Modules/template-function-specialization.cpp
===================================================================
--- clang/test/Modules/template-function-specialization.cpp
+++ clang/test/Modules/template-function-specialization.cpp
@@ -39,10 +39,6 @@
 void foo4() {
 }
 
-export template <>
-void foo4<int>() {
-}
-
 //--- Use.cpp
 import foo;
 void use() {
Index: clang/test/Modules/pr62796.cppm
===================================================================
--- clang/test/Modules/pr62796.cppm
+++ clang/test/Modules/pr62796.cppm
@@ -38,8 +38,6 @@
 
 	template<unsigned long N, typename Strategy = DefaultStrategy>
 	constexpr unsigned long Cache = Compute(Number<N>{}, Strategy{});
-
-    template constexpr unsigned long Cache<10ul>;
 }
 
 //--- Use.cpp
Index: clang/test/Modules/pr60890.cppm
===================================================================
--- clang/test/Modules/pr60890.cppm
+++ clang/test/Modules/pr60890.cppm
@@ -18,8 +18,6 @@
 	void aaa() requires(true) {}
 };
 
-export template struct a<double>;
-
 export template<typename T>
 void foo(T) requires(true) {}
 
Index: clang/test/Modules/pr59780.cppm
===================================================================
--- clang/test/Modules/pr59780.cppm
+++ /dev/null
@@ -1,46 +0,0 @@
-// https://github.com/llvm/llvm-project/issues/59780
-//
-// RUN: rm -rf %t
-// RUN: mkdir %t
-// RUN: split-file %s %t
-//
-// RUN: %clang_cc1 -std=c++20 %t/a.cppm -triple %itanium_abi_triple -emit-module-interface -o %t/a.pcm
-// RUN: %clang_cc1 -std=c++20 %t/use.cpp -fprebuilt-module-path=%t -S \
-// RUN:     -triple %itanium_abi_triple -emit-llvm -o - | FileCheck %t/use.cpp
-// RUN: %clang_cc1 -std=c++20 %t/a.pcm -triple %itanium_abi_triple -emit-llvm -o - | FileCheck %t/a.cppm
-
-//--- a.cppm
-export module a;
-
-export template<typename T>
-int x = 0;
-
-export template<>
-int x<int> = 0;
-
-export template<typename T>
-struct Y {
-    static int value;
-};
-
-template <typename T>
-int Y<T>::value = 0;
-
-export template<>
-struct Y<int> {
-    static int value;
-};
-
-int Y<int>::value = 0;
-
-// CHECK-NOT: @_ZW1a1xIiE = {{.*}}external{{.*}}global
-// CHECK-NOT: @_ZNW1a1YIiE5valueE = {{.*}}external{{.*}}global
-
-//--- use.cpp
-import a;
-int foo() {
-    return x<int> + Y<int>::value;
-}
-
-// CHECK: @_ZW1a1xIiE = {{.*}}external{{.*}}global
-// CHECK: @_ZNW1a1YIiE5valueE = {{.*}}external{{.*}}global
Index: clang/test/Modules/merge-var-template-spec-cxx-modules.cppm
===================================================================
--- clang/test/Modules/merge-var-template-spec-cxx-modules.cppm
+++ /dev/null
@@ -1,44 +0,0 @@
-// RUN: rm -rf %t
-// RUN: mkdir %t
-// RUN: split-file %s %t
-//
-// RUN: %clang_cc1 -std=c++20 -emit-module-interface %t/var_def.cppm -o %t/var_def.pcm
-// RUN: %clang_cc1 -std=c++20 -emit-module-interface -fprebuilt-module-path=%t %t/reexport1.cppm -o %t/reexport1.pcm
-// RUN: %clang_cc1 -std=c++20 -emit-module-interface -fprebuilt-module-path=%t %t/reexport2.cppm -o %t/reexport2.pcm
-// RUN: %clang_cc1 -std=c++20 -fprebuilt-module-path=%t %t/use.cppm -fsyntax-only -verify
-
-//--- use.cppm
-import reexport1;
-import reexport2;
-
-auto foo = zero<Int>;
-auto bar = zero<int*>;
-auto baz = zero<int>;
-
-template <class T> constexpr T zero = 0; // expected-error-re {{declaration{{.*}}in the global module follows declaration in module var_def}}
-                                         // expected-note@* {{previous}}
-template <> constexpr Int zero<Int> = {0}; // expected-error-re {{declaration{{.*}}in the global module follows declaration in module var_def}}
-                                           // expected-note@* {{previous}}
-template <class T> constexpr T* zero<T*> = nullptr; // expected-error-re {{declaration{{.*}}in the global module follows declaration in module var_def}}
-                                                    // expected-note@* {{previous}}
-
-template <> constexpr int** zero<int**> = nullptr; // ok, new specialization.
-template <class T> constexpr T** zero<T**> = nullptr; // ok, new partial specilization.
-
-//--- var_def.cppm
-export module var_def;
-
-export template <class T> constexpr T zero = 0;
-export struct Int {
-    int value;
-};
-export template <> constexpr Int zero<Int> = {0};
-export template <class T> constexpr T* zero<T*> = nullptr;
-
-//--- reexport1.cppm
-export module reexport1;
-export import var_def;
-
-//--- reexport2.cppm
-export module reexport2;
-export import var_def;
Index: clang/test/CXX/temp/temp.explicit/p2-p2615r1.cpp
===================================================================
--- /dev/null
+++ clang/test/CXX/temp/temp.explicit/p2-p2615r1.cpp
@@ -0,0 +1,20 @@
+// RUN: %clang_cc1 -std=c++20 %s -fsyntax-only -verify -pedantic-errors
+
+export module P2615R1;
+
+export template<typename T> void fA (T) { };
+
+export template<>
+void fA<int> (int) { }; // expected-error {{explicit specialization 'fA<int>' cannot be exported}}
+
+export template<typename T>
+class B { };
+
+export template<>
+class B<int> { }; // expected-error {{explicit specialization 'B<int>' cannot be exported}}
+
+export template<typename T> T C;
+
+export template<>
+int C<int>;  // expected-error {{explicit specialization 'C<int>' cannot be exported}}
+
Index: clang/test/CXX/module/module.interface/p1-p2615r1.cpp
===================================================================
--- /dev/null
+++ clang/test/CXX/module/module.interface/p1-p2615r1.cpp
@@ -0,0 +1,22 @@
+// RUN: %clang_cc1 -std=c++20 %s -emit-module-interface -o p2615r1.pcm \
+// RUN: -verify -pedantic-errors
+
+export module P2615R1;
+
+export template<class T1, class T2, int I>
+class A { };
+
+export template<class T, int I>
+class A<T, T*, I> { }; // expected-error {{partial class specialization 'A<T, T *, I>' cannot be exported}}
+
+export template<class T1, class T2, int I>
+struct B { };
+
+export template<class T, int I>
+struct B<T, T*, I> { }; // expected-error {{partial class specialization 'B<T, T *, I>' cannot be exported}}
+
+export template<class T1, class T2, int I>
+T1 C;
+
+export template<class T, int I>
+T C<T, T*, I>;  // expected-error {{partial variable specialization 'C<T, T *, I>' cannot be exported}}
Index: clang/lib/Sema/SemaModule.cpp
===================================================================
--- clang/lib/Sema/SemaModule.cpp
+++ clang/lib/Sema/SemaModule.cpp
@@ -828,6 +828,39 @@
 /// Check that it's valid to export \p D.
 static bool checkExportedDecl(Sema &S, Decl *D, SourceLocation BlockStart) {
 
+  if (isa<ClassTemplatePartialSpecializationDecl>(D) ||
+      isa<VarTemplatePartialSpecializationDecl>(D)) {
+    // C++20 [module.interface]p1:
+    //   [...] shall not declare a partial specialization.
+    int Kind = isa<ClassTemplatePartialSpecializationDecl>(D) ? 0 : 1;
+    auto *ND = dyn_cast<NamedDecl>(D);
+    S.Diag(ND->getLocation(), diag::err_export_partial_specialization)
+        << Kind << ND;
+    if (BlockStart.isValid())
+      S.Diag(BlockStart, diag::note_export);
+  } else if (auto *ND = dyn_cast<NamedDecl>(D)) {
+    // C++20 [temp.expl.spec]p2
+    //   The declaration in an explicit-specialization shall not be an
+    //   export-declaration.
+    bool BadExport = isa<ClassTemplateSpecializationDecl>(ND) ||
+                     isa<VarTemplateSpecializationDecl>(ND);
+    if (auto *FD = dyn_cast<FunctionDecl>(D)) {
+      if (FD->getTemplateSpecializationKind() == TSK_ExplicitSpecialization)
+        BadExport = true;
+    } else if (auto *VD = dyn_cast<VarDecl>(D)) {
+      if (VD->getTemplateSpecializationKind() == TSK_ExplicitSpecialization)
+        BadExport = true;
+    } else if (auto *ED = dyn_cast<EnumDecl>(D)) {
+      if (ED->getTemplateSpecializationKind() == TSK_ExplicitSpecialization)
+        BadExport = true;
+    }
+    if (BadExport) {
+      S.Diag(ND->getLocation(), diag::err_export_explicit_specialization) << ND;
+      if (BlockStart.isValid())
+        S.Diag(BlockStart, diag::note_export);
+    }
+  }
+
   //  C++20 [module.interface]p3:
   //   [...] it shall not declare a name with internal linkage.
   bool HasName = false;
@@ -843,7 +876,7 @@
     }
   }
 
-  // C++2a [module.interface]p5:
+  // C++20 [module.interface]p5:
   //   all entities to which all of the using-declarators ultimately refer
   //   shall have been introduced with a name having external linkage
   if (auto *USD = dyn_cast<UsingShadowDecl>(D)) {
Index: clang/include/clang/Basic/DiagnosticSemaKinds.td
===================================================================
--- clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -11272,6 +11272,10 @@
 def err_export_using_internal : Error<
   "using declaration referring to %1 with %select{internal|module|unknown}0 "
   "linkage cannot be exported">;
+def err_export_partial_specialization : Error<
+  "partial %select{class|variable}0 specialization %1 cannot be exported">;
+def err_export_explicit_specialization : Error<
+  "explicit specialization %0 cannot be exported">;
 def err_export_not_in_module_interface : Error<
   "export declaration can only be used within a module purview">;
 def err_export_inline_not_defined : Error<
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to