[clang] [clang][Modules] Complete the implementation of P2615: Meaningful exports (PR #194201)

Victor Chernyakin via cfe-commits cfe-commits at lists.llvm.org
Mon Apr 27 00:15:05 PDT 2026


https://github.com/localspook updated https://github.com/llvm/llvm-project/pull/194201

>From b8f38d51fef8dfd2cbaecbf1fd2b6252573cb0f9 Mon Sep 17 00:00:00 2001
From: Victor Chernyakin <chernyakin.victor.j at outlook.com>
Date: Sat, 25 Apr 2026 21:16:00 -0700
Subject: [PATCH 1/6] [clang][Modules] Complete the implementation of P2615:
 Meaningful exports

---
 clang/docs/ReleaseNotes.rst                   |  4 +
 .../clang/Basic/DiagnosticSemaKinds.td        |  9 ++
 clang/include/clang/Parse/Parser.h            | 27 +++++-
 clang/lib/Parse/ParseDeclCXX.cpp              | 55 +++++++++++-
 .../Modules/explicit-specializations.cppm     | 12 +--
 .../test/Modules/export-language-linkage.cppm |  2 +-
 .../merge-var-template-spec-cxx-modules.cppm  | 14 ++-
 clang/test/Modules/pr59780.cppm               |  4 +-
 clang/test/Modules/pr60890.cppm               |  4 +-
 clang/test/Modules/pr97313.cppm               |  6 +-
 .../template-function-specialization.cpp      |  2 +-
 clang/test/SemaCXX/P2615.cpp                  | 89 +++++++++++++++++++
 .../groupsharedArgs/ExportNoInlineTest.hlsl   |  2 +-
 .../Serialization/LoadSpecLazilyTest.cpp      |  2 +-
 clang/www/cxx_status.html                     |  2 +-
 15 files changed, 208 insertions(+), 26 deletions(-)

diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index c5c8c1fa12e7a..357ef60d8bdc1 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -49,6 +49,10 @@ C++ Specific Potentially Breaking Changes
 - Clang now correctly rejects ``export`` declarations in module implementation
   partitions. (#GH107602)
 
+- Clang now correctly rejects explicit instantiations and specializations
+  marked with ``export`` or a language linkage specification, completing
+  its implementation of P2615. (#GH160016)
+
 ABI Changes in This Version
 ---------------------------
 
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 7ed4684c8359e..941d27ce1b11c 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -12854,6 +12854,15 @@ def err_invalid_module_name : Error<"%0 is an invalid name for a module">;
 def err_extern_def_in_header_unit : Error<
   "non-inline external definitions are not permitted in C++ header units">;
 
+def err_meaningless_export : Error<
+  "%select{an explicit instantiation|a specialization}0 cannot be "
+  "marked 'export'">;
+def note_meaningless_export_explanation : Note<
+  "as long as its primary template is exported, it will be too">;
+def err_invalid_decl_in_linkage_spec : Error<
+  "language linkage specification cannot be applied to "
+  "%select{an explicit instantiation|a specialization|an export declaration}0">;
+
 def warn_exposure : Warning <
   "TU local entity %0 is exposed">,
   InGroup<DiagGroup<"TU-local-entity-exposure">>;
diff --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h
index 1d07d8dbcfa01..e4d91451db09e 100644
--- a/clang/include/clang/Parse/Parser.h
+++ b/clang/include/clang/Parse/Parser.h
@@ -3192,7 +3192,7 @@ class Parser : public CodeCompletionHandler {
   /// \verbatim
   ///       linkage-specification: [C++ 7.5p2: dcl.link]
   ///         'extern' string-literal '{' declaration-seq[opt] '}'
-  ///         'extern' string-literal declaration
+  ///         'extern' string-literal name-declaration
   /// \endverbatim
   ///
   Decl *ParseLinkage(ParsingDeclSpec &DS, DeclaratorContext Context);
@@ -3201,7 +3201,7 @@ class Parser : public CodeCompletionHandler {
   ///
   /// \verbatim
   ///       export-declaration:
-  ///         'export' declaration
+  ///         'export' name-declaration
   ///         'export' '{' declaration-seq[opt] '}'
   /// \endverbatim
   ///
@@ -3217,6 +3217,29 @@ class Parser : public CodeCompletionHandler {
   ///
   Decl *ParseExportDeclaration();
 
+  /// Ensure the declaration in an unbraced linkage-specification or
+  /// export-declaration is not an explicit-instantiation,
+  /// explicit-specialization, or export-declaration:
+  ///
+  /// \verbatim
+  ///       export-declaration: [C++: module.interface]
+  ///         export name-declaration
+  ///
+  ///       linkage-specification: [C++: dcl.link]
+  ///         export name-declaration
+  ///
+  ///       declaration: [C++: dcl.pre]
+  ///         name-declaration
+  ///         special-declaration
+  ///
+  ///       special-declaration: [C++: dcl.pre]
+  ///         explicit-instantiation
+  ///         explicit-specialization
+  ///         export-declaration
+  /// \endverbatim
+  ///
+  void CheckUnbracedLinkageOrExportDeclaration(Decl *LinkageOrExportDecl);
+
   /// ParseUsingDirectiveOrDeclaration - Parse C++ using using-declaration or
   /// using-directive. Assumes that current token is 'using'.
   DeclGroupPtrTy ParseUsingDirectiveOrDeclaration(
diff --git a/clang/lib/Parse/ParseDeclCXX.cpp b/clang/lib/Parse/ParseDeclCXX.cpp
index d1e2e2c2c6ce1..7ee1b7d4d9e75 100644
--- a/clang/lib/Parse/ParseDeclCXX.cpp
+++ b/clang/lib/Parse/ParseDeclCXX.cpp
@@ -339,9 +339,11 @@ Decl *Parser::ParseLinkage(ParsingDeclSpec &DS, DeclaratorContext Context) {
     // ... but anyway remember that such an "extern" was seen.
     DS.setExternInLinkageSpec(true);
     ParseExternalDeclaration(DeclAttrs, DeclSpecAttrs, &DS);
-    return LinkageSpec ? Actions.ActOnFinishLinkageSpecification(
-                             getCurScope(), LinkageSpec, SourceLocation())
-                       : nullptr;
+    if (!LinkageSpec)
+      return nullptr;
+    CheckUnbracedLinkageOrExportDeclaration(LinkageSpec);
+    return Actions.ActOnFinishLinkageSpecification(getCurScope(), LinkageSpec,
+                                                   SourceLocation());
   }
 
   DS.abort();
@@ -420,6 +422,7 @@ Decl *Parser::ParseExportDeclaration() {
     MaybeParseCXX11Attributes(DeclAttrs);
     ParsedAttributes EmptyDeclSpecAttrs(AttrFactory);
     ParseExternalDeclaration(DeclAttrs, EmptyDeclSpecAttrs);
+    CheckUnbracedLinkageOrExportDeclaration(ExportDecl);
     return Actions.ActOnFinishExportDecl(getCurScope(), ExportDecl,
                                          SourceLocation());
   }
@@ -440,6 +443,52 @@ Decl *Parser::ParseExportDeclaration() {
                                        T.getCloseLocation());
 }
 
+void Parser::CheckUnbracedLinkageOrExportDeclaration(
+    Decl *LinkageOrExportDecl) {
+  const auto *DC = cast<DeclContext>(LinkageOrExportDecl);
+  if (DC->decls_empty())
+    return;
+
+  const Decl *D = *DC->decls_begin();
+
+  // Nested export declarations are diagnosed elsewhere.
+  if (isa<LinkageSpecDecl>(LinkageOrExportDecl) && isa<ExportDecl>(D)) {
+    Diag(LinkageOrExportDecl->getLocation(),
+         diag::err_invalid_decl_in_linkage_spec)
+        << 2;
+    return;
+  }
+
+  TemplateSpecializationKind TSK = [&] {
+    if (const auto *EID = dyn_cast<ExplicitInstantiationDecl>(D))
+      return EID->getTemplateSpecializationKind();
+    if (const auto *CTSD = dyn_cast<ClassTemplateSpecializationDecl>(D))
+      return CTSD->getTemplateSpecializationKind();
+    if (const auto *VTSD = dyn_cast<VarTemplateSpecializationDecl>(D))
+      return VTSD->getTemplateSpecializationKind();
+    if (const auto *FD = dyn_cast<FunctionDecl>(D))
+      return FD->getTemplateSpecializationKind();
+    return TSK_Undeclared;
+  }();
+
+  if (TSK == TSK_Undeclared)
+    return;
+
+  if (const auto *ED = dyn_cast<ExportDecl>(LinkageOrExportDecl)) {
+    Diag(ED->getExportLoc(), diag::err_meaningless_export)
+        << (TSK == TSK_ExplicitSpecialization)
+        << FixItHint::CreateRemoval(ED->getExportLoc());
+    Diag(ED->getExportLoc(), diag::note_meaningless_export_explanation);
+    return;
+  }
+
+  if (const auto *LS = dyn_cast<LinkageSpecDecl>(LinkageOrExportDecl)) {
+    Diag(LS->getLocation(), diag::err_invalid_decl_in_linkage_spec)
+        << (TSK == TSK_ExplicitSpecialization);
+    return;
+  }
+}
+
 Parser::DeclGroupPtrTy Parser::ParseUsingDirectiveOrDeclaration(
     DeclaratorContext Context, const ParsedTemplateInfo &TemplateInfo,
     SourceLocation &DeclEnd, ParsedAttributes &Attrs) {
diff --git a/clang/test/Modules/explicit-specializations.cppm b/clang/test/Modules/explicit-specializations.cppm
index 914144018e880..b5b8d152e3a7b 100644
--- a/clang/test/Modules/explicit-specializations.cppm
+++ b/clang/test/Modules/explicit-specializations.cppm
@@ -20,7 +20,7 @@ struct S {
 
 export struct A {};
 
-export template <>
+template <>
 struct S<A> {
     static constexpr bool selected = true;
 };
@@ -34,7 +34,7 @@ struct V {
     static constexpr bool selected = false;
 };
 
-export template <>
+template<>
 struct V<S> {
     static constexpr bool selected = true;
 };
@@ -46,7 +46,7 @@ struct Numbers {
     static constexpr int value = X;
 };
 
-export template<>
+template<>
 struct Numbers<43> {
     static constexpr bool selected = true;
     static constexpr int value = 43;
@@ -58,7 +58,7 @@ struct Pointers {
 };
 
 export int IntegralValue = 0;
-export template<>
+template<>
 struct Pointers<&IntegralValue> {
     static constexpr bool selected = true;
 };
@@ -68,7 +68,7 @@ struct NullPointers {
     static constexpr bool selected = false;
 };
 
-export template<>
+template<>
 struct NullPointers<nullptr> {
     static constexpr bool selected = true;
 };
@@ -79,7 +79,7 @@ struct Array {
 };
 
 export int array[5];
-export template<>
+template<>
 struct Array<array> {
     static constexpr bool selected = true;
 };
diff --git a/clang/test/Modules/export-language-linkage.cppm b/clang/test/Modules/export-language-linkage.cppm
index f389d9604ef3a..e13d0003f248b 100644
--- a/clang/test/Modules/export-language-linkage.cppm
+++ b/clang/test/Modules/export-language-linkage.cppm
@@ -55,7 +55,7 @@ extern "C++" {
     int h();
 }
 
-extern "C++" export int g();
+export int g();
 
 //--- d.cpp
 import c;
diff --git a/clang/test/Modules/merge-var-template-spec-cxx-modules.cppm b/clang/test/Modules/merge-var-template-spec-cxx-modules.cppm
index db3f4cd518716..e09fcbaa92d95 100644
--- a/clang/test/Modules/merge-var-template-spec-cxx-modules.cppm
+++ b/clang/test/Modules/merge-var-template-spec-cxx-modules.cppm
@@ -28,7 +28,7 @@ template <class T> constexpr T* zero<T*> = nullptr; // expected-error-re {{decla
                                                     // expected-note@* {{previous}}
 
 template <> constexpr int** zero<int**> = nullptr; // ok, new specialization.
-template <class T> constexpr T** zero<T**> = nullptr; // ok, new partial specilization.
+template <class T> constexpr T** zero<T**> = nullptr; // ok, new partial specialization.
 
 //--- var_def.cppm
 export module var_def;
@@ -37,8 +37,16 @@ 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;
+
+// FIXME: it should make no difference whether a specialization is
+// exported or not, but currently, removing this 'export'
+// leads to an assertion failure in Sema::shouldLinkPossiblyHiddenDecl.
+export {
+
+template <> constexpr Int zero<Int> = {0};
+template <class T> constexpr T* zero<T*> = nullptr;
+
+}
 
 //--- reexport1.cppm
 export module reexport1;
diff --git a/clang/test/Modules/pr59780.cppm b/clang/test/Modules/pr59780.cppm
index ea5fdd5c4ce26..8e2685e328133 100644
--- a/clang/test/Modules/pr59780.cppm
+++ b/clang/test/Modules/pr59780.cppm
@@ -25,7 +25,7 @@ export module a;
 export template<typename T>
 int x = 0;
 
-export template<>
+template<>
 int x<int> = 0;
 
 export template<typename T>
@@ -36,7 +36,7 @@ struct Y {
 template <typename T>
 int Y<T>::value = 0;
 
-export template<>
+template<>
 struct Y<int> {
     static int value;
 };
diff --git a/clang/test/Modules/pr60890.cppm b/clang/test/Modules/pr60890.cppm
index b1d9114bf1ebe..5df2f2ea4bdf2 100644
--- a/clang/test/Modules/pr60890.cppm
+++ b/clang/test/Modules/pr60890.cppm
@@ -24,12 +24,12 @@ struct a {
 	void aaa() requires(true) {}
 };
 
-export template struct a<double>;
+template struct a<double>;
 
 export template<typename T>
 void foo(T) requires(true) {}
 
-export template void foo<double>(double);
+template void foo<double>(double);
 
 export template <typename T>
 class A {
diff --git a/clang/test/Modules/pr97313.cppm b/clang/test/Modules/pr97313.cppm
index 99795d6e43030..833e7ff416247 100644
--- a/clang/test/Modules/pr97313.cppm
+++ b/clang/test/Modules/pr97313.cppm
@@ -76,7 +76,7 @@ public:
     virtual ~Template();
 };
 
-export template<>
+template<>
 class Template<char> {
 public:
     virtual ~Template();
@@ -84,11 +84,11 @@ public:
 
 // CHECK: @_ZTIW3Mod8TemplateIcE = {{.*}}constant
 
-export template class Template<unsigned>;
+template class Template<unsigned>;
 
 // CHECK: @_ZTIW3Mod8TemplateIjE = {{.*}}weak_odr
 
-export extern template class Template<double>;
+extern template class Template<double>;
 
 auto v = new Template<signed int>();
 
diff --git a/clang/test/Modules/template-function-specialization.cpp b/clang/test/Modules/template-function-specialization.cpp
index d5d7d7e812398..7619f76ab5634 100644
--- a/clang/test/Modules/template-function-specialization.cpp
+++ b/clang/test/Modules/template-function-specialization.cpp
@@ -42,7 +42,7 @@ export template <typename T>
 void foo4() {
 }
 
-export template <>
+template <>
 void foo4<int>() {
 }
 
diff --git a/clang/test/SemaCXX/P2615.cpp b/clang/test/SemaCXX/P2615.cpp
index fed177c21bc79..947d7e4bf459f 100644
--- a/clang/test/SemaCXX/P2615.cpp
+++ b/clang/test/SemaCXX/P2615.cpp
@@ -3,9 +3,98 @@
 
 
 // RUN: %clang_cc1 -std=c++20 -verify -fsyntax-only %t/A.cpp
+// RUN: %clang_cc1 -std=c++20 -verify -fsyntax-only %t/B.cpp
 
 //--- A.cpp
 // expected-no-diagnostics
 export module A;
 export namespace N {int x = 42;}
 export using namespace N;
+
+//--- B.cpp
+export module B;
+
+export template <typename T> class s1 {};
+export template <typename T> class s1<T *> {}; // expected-error {{a specialization cannot be marked 'export'}}
+// expected-note at -1 {{as long as its primary template is exported, it will be too}}
+export template <> class s1<int> {}; // expected-error {{a specialization cannot be marked 'export'}}
+// expected-note at -1 {{as long as its primary template is exported, it will be too}}
+export template class s1<char>; // expected-error {{an explicit instantiation cannot be marked 'export'}}
+// expected-note at -1 {{as long as its primary template is exported, it will be too}}
+export extern template class s1<void>; // expected-error {{an explicit instantiation cannot be marked 'export'}}
+// expected-note at -1 {{as long as its primary template is exported, it will be too}}
+
+export template <typename T> int v1 = 0;
+export template <typename T> int v1<T *> = 0; // expected-error {{a specialization cannot be marked 'export'}}
+// expected-note at -1 {{as long as its primary template is exported, it will be too}}
+export template <> int v1<int> = 0; // expected-error {{a specialization cannot be marked 'export'}}
+// expected-note at -1 {{as long as its primary template is exported, it will be too}}
+export template int v1<char>; // expected-error {{an explicit instantiation cannot be marked 'export'}}
+// expected-note at -1 {{as long as its primary template is exported, it will be too}}
+export extern template int v1<void>; // expected-error {{an explicit instantiation cannot be marked 'export'}}
+// expected-note at -1 {{as long as its primary template is exported, it will be too}}
+
+export template <typename T> void f1() {}
+export template <> void f1<int>() {} // expected-error {{a specialization cannot be marked 'export'}}
+// expected-note at -1 {{as long as its primary template is exported, it will be too}}
+export template void f1<char>(); // expected-error {{an explicit instantiation cannot be marked 'export'}}
+// expected-note at -1 {{as long as its primary template is exported, it will be too}}
+export extern template void f1<void>(); // expected-error {{an explicit instantiation cannot be marked 'export'}}
+// expected-note at -1 {{as long as its primary template is exported, it will be too}}
+
+
+export { template <typename T> class s2 {}; }
+export { template <typename T> class s2<T *> {}; }
+export { template <> class s2<int> {}; }
+export { template class s2<char>; }
+export { extern template class s2<void>; }
+
+export { template <typename T> int v2 = 0; }
+export { template <typename T> int v2<T *> = 0; }
+export { template <> int v2<int> = 0; }
+export { template int v2<char>; }
+export { extern template int v2<void>; }
+
+export { template <typename T> void f2() {} }
+export { template <> void f2<int>() {} }
+export { template void f2<char>(); }
+export { extern template void f2<void>(); }
+
+
+extern "C++" template <typename T> class s3 {};
+extern "C++" template <typename T> class s3<T *> {}; // expected-error {{language linkage specification cannot be applied to a specialization}}
+extern "C++" template <> class s3<int> {}; // expected-error {{language linkage specification cannot be applied to a specialization}}
+extern "C++" template class s3<char>; // expected-error {{language linkage specification cannot be applied to an explicit instantiation}}
+extern "C++" extern template class s3<void>; // expected-error {{language linkage specification cannot be applied to an explicit instantiation}}
+
+extern "C++" template <typename T> int v3 = 0;
+extern "C++" template <typename T> int v3<T *> = 0; // expected-error {{language linkage specification cannot be applied to a specialization}}
+extern "C++" template <> int v3<int> = 0; // expected-error {{language linkage specification cannot be applied to a specialization}}
+extern "C++" template int v3<char>; // expected-error {{language linkage specification cannot be applied to an explicit instantiation}}
+extern "C++" extern template int v3<void>; // expected-error {{language linkage specification cannot be applied to an explicit instantiation}}
+
+extern "C++" template <typename T> void f3() {}
+extern "C++" template <> void f3<int>() {} // expected-error {{language linkage specification cannot be applied to a specialization}}
+extern "C++" template void f3<char>(); // expected-error {{language linkage specification cannot be applied to an explicit instantiation}}
+extern "C++" extern template void f3<void>(); // expected-error {{language linkage specification cannot be applied to an explicit instantiation}}
+
+extern "C++" export int i; // expected-error {{language linkage specification cannot be applied to an export declaration}}
+extern "C++" export {} // expected-error {{language linkage specification cannot be applied to an export declaration}}
+
+
+extern "C++" { template <typename T> class s4 {}; }
+extern "C++" { template <typename T> class s4<T *> {}; }
+extern "C++" { template <> class s4<int> {}; }
+extern "C++" { template class s4<char>; }
+extern "C++" { extern template class s4<void>; }
+
+extern "C++" { template <typename T> int v4 = 0; }
+extern "C++" { template <typename T> int v4<T *> = 0; }
+extern "C++" { template <> int v4<int> = 0; }
+extern "C++" { template int v4<char>; }
+extern "C++" { extern template int v4<void>; }
+
+extern "C++" { template <typename T> void f4() {} }
+extern "C++" { template <> void f4<int>() {} }
+extern "C++" { template void f4<char>(); }
+extern "C++" { extern template void f4<void>(); }
diff --git a/clang/test/SemaHLSL/Language/groupsharedArgs/ExportNoInlineTest.hlsl b/clang/test/SemaHLSL/Language/groupsharedArgs/ExportNoInlineTest.hlsl
index c120adb4a4fbc..9881b9d43897c 100644
--- a/clang/test/SemaHLSL/Language/groupsharedArgs/ExportNoInlineTest.hlsl
+++ b/clang/test/SemaHLSL/Language/groupsharedArgs/ExportNoInlineTest.hlsl
@@ -15,6 +15,6 @@ void fn3(groupshared T A, groupshared T B) {
   A = B;
 }
 
-export template void fn3<uint>(groupshared uint A, groupshared uint B);
+template void fn3<uint>(groupshared uint A, groupshared uint B);
 template __attribute__((noinline)) void fn3<float>(groupshared float A, groupshared float B);
 // expected-error at -1{{'noinline' attribute is not compatible with 'groupshared' parameter attribute}}
diff --git a/clang/unittests/Serialization/LoadSpecLazilyTest.cpp b/clang/unittests/Serialization/LoadSpecLazilyTest.cpp
index f55925aeae1f2..33fabf451de70 100644
--- a/clang/unittests/Serialization/LoadSpecLazilyTest.cpp
+++ b/clang/unittests/Serialization/LoadSpecLazilyTest.cpp
@@ -238,7 +238,7 @@ export class Temp {
 
 export class ExportedClass {};
 
-export template<> class A<ExportedClass> {
+template<> class A<ExportedClass> {
    A<MayBeLoaded> AS;
    A<B>           AB;
 };
diff --git a/clang/www/cxx_status.html b/clang/www/cxx_status.html
index 3096e55803b84..8a0f3a9b28439 100755
--- a/clang/www/cxx_status.html
+++ b/clang/www/cxx_status.html
@@ -988,7 +988,7 @@ <h2 id="cxx20">C++20 implementation status</h2>
       </tr>
       <tr> <!-- from Kona 2022 -->
         <td><a href="https://wg21.link/P2615R1">P2615R1</a> (<a href="#dr">DR</a>)</td>
-        <td class="full" align="center">Clang 17</td>
+        <td class="unreleased" align="center">Clang 23</td>
       </tr>
       <tr> <!-- from Issaquah 2023 -->
         <td><a href="https://wg21.link/P2788R0">P2788R0</a> (<a href="#dr">DR</a>)</td>

>From b2e7ab497d8e9f01b521b01b33ebbd756a977a75 Mon Sep 17 00:00:00 2001
From: Victor Chernyakin <chernyakin.victor.j at outlook.com>
Date: Sun, 26 Apr 2026 11:19:49 -0700
Subject: [PATCH 2/6] Add fix-it tests

---
 clang/test/SemaCXX/P2615.cpp | 12 ++++++++++++
 1 file changed, 12 insertions(+)

diff --git a/clang/test/SemaCXX/P2615.cpp b/clang/test/SemaCXX/P2615.cpp
index 947d7e4bf459f..ea57bfd790a1e 100644
--- a/clang/test/SemaCXX/P2615.cpp
+++ b/clang/test/SemaCXX/P2615.cpp
@@ -4,6 +4,7 @@
 
 // RUN: %clang_cc1 -std=c++20 -verify -fsyntax-only %t/A.cpp
 // RUN: %clang_cc1 -std=c++20 -verify -fsyntax-only %t/B.cpp
+// RUN: not %clang_cc1 -std=c++20 -fsyntax-only -fdiagnostics-parseable-fixits %t/B.cpp 2>&1 | FileCheck %t/B.cpp
 
 //--- A.cpp
 // expected-no-diagnostics
@@ -17,30 +18,41 @@ export module B;
 export template <typename T> class s1 {};
 export template <typename T> class s1<T *> {}; // expected-error {{a specialization cannot be marked 'export'}}
 // expected-note at -1 {{as long as its primary template is exported, it will be too}}
+// CHECK: fix-it:"{{.*}}":{[[@LINE-2]]:1-[[@LINE-2]]:8}:""
 export template <> class s1<int> {}; // expected-error {{a specialization cannot be marked 'export'}}
 // expected-note at -1 {{as long as its primary template is exported, it will be too}}
+// CHECK: fix-it:"{{.*}}":{[[@LINE-2]]:1-[[@LINE-2]]:8}:""
 export template class s1<char>; // expected-error {{an explicit instantiation cannot be marked 'export'}}
 // expected-note at -1 {{as long as its primary template is exported, it will be too}}
+// CHECK: fix-it:"{{.*}}":{[[@LINE-2]]:1-[[@LINE-2]]:8}:""
 export extern template class s1<void>; // expected-error {{an explicit instantiation cannot be marked 'export'}}
 // expected-note at -1 {{as long as its primary template is exported, it will be too}}
+// CHECK: fix-it:"{{.*}}":{[[@LINE-2]]:1-[[@LINE-2]]:8}:""
 
 export template <typename T> int v1 = 0;
 export template <typename T> int v1<T *> = 0; // expected-error {{a specialization cannot be marked 'export'}}
 // expected-note at -1 {{as long as its primary template is exported, it will be too}}
+// CHECK: fix-it:"{{.*}}":{[[@LINE-2]]:1-[[@LINE-2]]:8}:""
 export template <> int v1<int> = 0; // expected-error {{a specialization cannot be marked 'export'}}
 // expected-note at -1 {{as long as its primary template is exported, it will be too}}
+// CHECK: fix-it:"{{.*}}":{[[@LINE-2]]:1-[[@LINE-2]]:8}:""
 export template int v1<char>; // expected-error {{an explicit instantiation cannot be marked 'export'}}
 // expected-note at -1 {{as long as its primary template is exported, it will be too}}
+// CHECK: fix-it:"{{.*}}":{[[@LINE-2]]:1-[[@LINE-2]]:8}:""
 export extern template int v1<void>; // expected-error {{an explicit instantiation cannot be marked 'export'}}
 // expected-note at -1 {{as long as its primary template is exported, it will be too}}
+// CHECK: fix-it:"{{.*}}":{[[@LINE-2]]:1-[[@LINE-2]]:8}:""
 
 export template <typename T> void f1() {}
 export template <> void f1<int>() {} // expected-error {{a specialization cannot be marked 'export'}}
 // expected-note at -1 {{as long as its primary template is exported, it will be too}}
+// CHECK: fix-it:"{{.*}}":{[[@LINE-2]]:1-[[@LINE-2]]:8}:""
 export template void f1<char>(); // expected-error {{an explicit instantiation cannot be marked 'export'}}
 // expected-note at -1 {{as long as its primary template is exported, it will be too}}
+// CHECK: fix-it:"{{.*}}":{[[@LINE-2]]:1-[[@LINE-2]]:8}:""
 export extern template void f1<void>(); // expected-error {{an explicit instantiation cannot be marked 'export'}}
 // expected-note at -1 {{as long as its primary template is exported, it will be too}}
+// CHECK: fix-it:"{{.*}}":{[[@LINE-2]]:1-[[@LINE-2]]:8}:""
 
 
 export { template <typename T> class s2 {}; }

>From fc4e1c3cfa4cb5d4f8a577e00d7928daf676a0a1 Mon Sep 17 00:00:00 2001
From: Victor Chernyakin <chernyakin.victor.j at outlook.com>
Date: Sun, 26 Apr 2026 11:33:11 -0700
Subject: [PATCH 3/6] Allow linkage-specification on partial specializations

---
 clang/lib/Parse/ParseDeclCXX.cpp | 13 +++++++++++++
 clang/test/SemaCXX/P2615.cpp     |  4 ++--
 2 files changed, 15 insertions(+), 2 deletions(-)

diff --git a/clang/lib/Parse/ParseDeclCXX.cpp b/clang/lib/Parse/ParseDeclCXX.cpp
index 7ee1b7d4d9e75..efbfa43a38794 100644
--- a/clang/lib/Parse/ParseDeclCXX.cpp
+++ b/clang/lib/Parse/ParseDeclCXX.cpp
@@ -459,6 +459,17 @@ void Parser::CheckUnbracedLinkageOrExportDeclaration(
     return;
   }
 
+  // [module.interface]/1 says:
+  //
+  //     The name-declaration of an export-declaration shall not declare a
+  //     partial specialization.
+  //
+  // There's no equivalent wording for linkage-specification.
+  if (isa<ClassTemplatePartialSpecializationDecl,
+          VarTemplatePartialSpecializationDecl>(D) &&
+      isa<LinkageSpecDecl>(LinkageOrExportDecl))
+    return;
+
   TemplateSpecializationKind TSK = [&] {
     if (const auto *EID = dyn_cast<ExplicitInstantiationDecl>(D))
       return EID->getTemplateSpecializationKind();
@@ -487,6 +498,8 @@ void Parser::CheckUnbracedLinkageOrExportDeclaration(
         << (TSK == TSK_ExplicitSpecialization);
     return;
   }
+
+  llvm_unreachable("Expected either an ExportDecl or a LinkageSpecDecl");
 }
 
 Parser::DeclGroupPtrTy Parser::ParseUsingDirectiveOrDeclaration(
diff --git a/clang/test/SemaCXX/P2615.cpp b/clang/test/SemaCXX/P2615.cpp
index ea57bfd790a1e..e338ded05c94c 100644
--- a/clang/test/SemaCXX/P2615.cpp
+++ b/clang/test/SemaCXX/P2615.cpp
@@ -74,13 +74,13 @@ export { extern template void f2<void>(); }
 
 
 extern "C++" template <typename T> class s3 {};
-extern "C++" template <typename T> class s3<T *> {}; // expected-error {{language linkage specification cannot be applied to a specialization}}
+extern "C++" template <typename T> class s3<T *> {};
 extern "C++" template <> class s3<int> {}; // expected-error {{language linkage specification cannot be applied to a specialization}}
 extern "C++" template class s3<char>; // expected-error {{language linkage specification cannot be applied to an explicit instantiation}}
 extern "C++" extern template class s3<void>; // expected-error {{language linkage specification cannot be applied to an explicit instantiation}}
 
 extern "C++" template <typename T> int v3 = 0;
-extern "C++" template <typename T> int v3<T *> = 0; // expected-error {{language linkage specification cannot be applied to a specialization}}
+extern "C++" template <typename T> int v3<T *> = 0;
 extern "C++" template <> int v3<int> = 0; // expected-error {{language linkage specification cannot be applied to a specialization}}
 extern "C++" template int v3<char>; // expected-error {{language linkage specification cannot be applied to an explicit instantiation}}
 extern "C++" extern template int v3<void>; // expected-error {{language linkage specification cannot be applied to an explicit instantiation}}

>From 012415d30920c8892d6391dd16fb1577a505a336 Mon Sep 17 00:00:00 2001
From: Victor Chernyakin <chernyakin.victor.j at outlook.com>
Date: Sun, 26 Apr 2026 13:02:32 -0700
Subject: [PATCH 4/6] Add workaround for MSVC STL

---
 clang/lib/Parse/ParseDeclCXX.cpp | 22 ++++++++++++++++++++++
 clang/test/SemaCXX/P2615.cpp     | 26 ++++++++++++++++++++++++++
 2 files changed, 48 insertions(+)

diff --git a/clang/lib/Parse/ParseDeclCXX.cpp b/clang/lib/Parse/ParseDeclCXX.cpp
index efbfa43a38794..92e76a11ed02a 100644
--- a/clang/lib/Parse/ParseDeclCXX.cpp
+++ b/clang/lib/Parse/ParseDeclCXX.cpp
@@ -494,6 +494,28 @@ void Parser::CheckUnbracedLinkageOrExportDeclaration(
   }
 
   if (const auto *LS = dyn_cast<LinkageSpecDecl>(LinkageOrExportDecl)) {
+    // HACK: Old versions of the MSVC STL used to have linkage specifications
+    // on some template specializations, but it would be too disruptive to
+    // reject them. This was fixed in
+    // https://github.com/microsoft/STL/pull/6074, merged on 2026-02-11.
+    bool IsStandardLibrarySymbolInOldMSVCSTL = [&] {
+      const MacroInfo *MSVCSTLUpdateMacro =
+          PP.getMacroInfo(PP.getIdentifierInfo("_MSVC_STL_UPDATE"));
+
+      if (!MSVCSTLUpdateMacro || MSVCSTLUpdateMacro->getNumTokens() != 1)
+        return false;
+
+      bool Invalid;
+      std::string MSVCSTLUpdate =
+          PP.getSpelling(MSVCSTLUpdateMacro->getReplacementToken(0), &Invalid);
+      return !Invalid && MSVCSTLUpdate.size() == 7 &&
+             MSVCSTLUpdate < "202603L" &&
+             LinkageOrExportDecl->isInStdNamespace();
+    }();
+
+    if (IsStandardLibrarySymbolInOldMSVCSTL)
+      return;
+
     Diag(LS->getLocation(), diag::err_invalid_decl_in_linkage_spec)
         << (TSK == TSK_ExplicitSpecialization);
     return;
diff --git a/clang/test/SemaCXX/P2615.cpp b/clang/test/SemaCXX/P2615.cpp
index e338ded05c94c..e75a530ae0812 100644
--- a/clang/test/SemaCXX/P2615.cpp
+++ b/clang/test/SemaCXX/P2615.cpp
@@ -5,6 +5,8 @@
 // RUN: %clang_cc1 -std=c++20 -verify -fsyntax-only %t/A.cpp
 // RUN: %clang_cc1 -std=c++20 -verify -fsyntax-only %t/B.cpp
 // RUN: not %clang_cc1 -std=c++20 -fsyntax-only -fdiagnostics-parseable-fixits %t/B.cpp 2>&1 | FileCheck %t/B.cpp
+// RUN: %clang_cc1 -std=c++20 -verify -fsyntax-only %t/msvc-stl-exception-1.cpp
+// RUN: %clang_cc1 -std=c++20 -verify -fsyntax-only %t/msvc-stl-exception-2.cpp
 
 //--- A.cpp
 // expected-no-diagnostics
@@ -110,3 +112,27 @@ extern "C++" { template <typename T> void f4() {} }
 extern "C++" { template <> void f4<int>() {} }
 extern "C++" { template void f4<char>(); }
 extern "C++" { extern template void f4<void>(); }
+
+//--- msvc-stl-exception-1.cpp
+#define _MSVC_STL_UPDATE 202602L
+
+namespace std {
+
+extern "C++" template <typename T> struct s {};
+extern "C++" template<> struct s<int> {}; // expected-no-error {{language linkage specification cannot be applied to a specialization}}
+
+} // namespace std
+
+// Should still diagnose outside the std namespace.
+extern "C++" template <typename T> struct c {};
+extern "C++" template<> struct c<int> {}; // expected-error {{language linkage specification cannot be applied to a specialization}}
+
+//--- msvc-stl-exception-2.cpp
+#define _MSVC_STL_UPDATE 202603L
+
+namespace std {
+
+extern "C++" template <typename T> struct s {};
+extern "C++" template<> struct s<int> {}; // expected-error {{language linkage specification cannot be applied to a specialization}}
+
+} // namespace std

>From a6d6b282fb11f0a30f85074acea1402762f6e0b1 Mon Sep 17 00:00:00 2001
From: Victor Chernyakin <chernyakin.victor.j at outlook.com>
Date: Mon, 27 Apr 2026 00:13:16 -0700
Subject: [PATCH 5/6] Adjust text of error message; address nits in tests

---
 .../clang/Basic/DiagnosticSemaKinds.td        |  4 +-
 clang/test/SemaCXX/P2615.cpp                  | 51 +++++++++----------
 2 files changed, 27 insertions(+), 28 deletions(-)

diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 941d27ce1b11c..e981eddcd6bd0 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -12858,9 +12858,9 @@ def err_meaningless_export : Error<
   "%select{an explicit instantiation|a specialization}0 cannot be "
   "marked 'export'">;
 def note_meaningless_export_explanation : Note<
-  "as long as its primary template is exported, it will be too">;
+  "it is exported if the primary template is exported">;
 def err_invalid_decl_in_linkage_spec : Error<
-  "language linkage specification cannot be applied to "
+  "language linkage cannot be specified for "
   "%select{an explicit instantiation|a specialization|an export declaration}0">;
 
 def warn_exposure : Warning <
diff --git a/clang/test/SemaCXX/P2615.cpp b/clang/test/SemaCXX/P2615.cpp
index e75a530ae0812..c0b6abd9731bd 100644
--- a/clang/test/SemaCXX/P2615.cpp
+++ b/clang/test/SemaCXX/P2615.cpp
@@ -19,41 +19,41 @@ export module B;
 
 export template <typename T> class s1 {};
 export template <typename T> class s1<T *> {}; // expected-error {{a specialization cannot be marked 'export'}}
-// expected-note at -1 {{as long as its primary template is exported, it will be too}}
+// expected-note at -1 {{it is exported if the primary template is exported}}
 // CHECK: fix-it:"{{.*}}":{[[@LINE-2]]:1-[[@LINE-2]]:8}:""
 export template <> class s1<int> {}; // expected-error {{a specialization cannot be marked 'export'}}
-// expected-note at -1 {{as long as its primary template is exported, it will be too}}
+// expected-note at -1 {{it is exported if the primary template is exported}}
 // CHECK: fix-it:"{{.*}}":{[[@LINE-2]]:1-[[@LINE-2]]:8}:""
 export template class s1<char>; // expected-error {{an explicit instantiation cannot be marked 'export'}}
-// expected-note at -1 {{as long as its primary template is exported, it will be too}}
+// expected-note at -1 {{it is exported if the primary template is exported}}
 // CHECK: fix-it:"{{.*}}":{[[@LINE-2]]:1-[[@LINE-2]]:8}:""
 export extern template class s1<void>; // expected-error {{an explicit instantiation cannot be marked 'export'}}
-// expected-note at -1 {{as long as its primary template is exported, it will be too}}
+// expected-note at -1 {{it is exported if the primary template is exported}}
 // CHECK: fix-it:"{{.*}}":{[[@LINE-2]]:1-[[@LINE-2]]:8}:""
 
 export template <typename T> int v1 = 0;
 export template <typename T> int v1<T *> = 0; // expected-error {{a specialization cannot be marked 'export'}}
-// expected-note at -1 {{as long as its primary template is exported, it will be too}}
+// expected-note at -1 {{it is exported if the primary template is exported}}
 // CHECK: fix-it:"{{.*}}":{[[@LINE-2]]:1-[[@LINE-2]]:8}:""
 export template <> int v1<int> = 0; // expected-error {{a specialization cannot be marked 'export'}}
-// expected-note at -1 {{as long as its primary template is exported, it will be too}}
+// expected-note at -1 {{it is exported if the primary template is exported}}
 // CHECK: fix-it:"{{.*}}":{[[@LINE-2]]:1-[[@LINE-2]]:8}:""
 export template int v1<char>; // expected-error {{an explicit instantiation cannot be marked 'export'}}
-// expected-note at -1 {{as long as its primary template is exported, it will be too}}
+// expected-note at -1 {{it is exported if the primary template is exported}}
 // CHECK: fix-it:"{{.*}}":{[[@LINE-2]]:1-[[@LINE-2]]:8}:""
 export extern template int v1<void>; // expected-error {{an explicit instantiation cannot be marked 'export'}}
-// expected-note at -1 {{as long as its primary template is exported, it will be too}}
+// expected-note at -1 {{it is exported if the primary template is exported}}
 // CHECK: fix-it:"{{.*}}":{[[@LINE-2]]:1-[[@LINE-2]]:8}:""
 
 export template <typename T> void f1() {}
 export template <> void f1<int>() {} // expected-error {{a specialization cannot be marked 'export'}}
-// expected-note at -1 {{as long as its primary template is exported, it will be too}}
+// expected-note at -1 {{it is exported if the primary template is exported}}
 // CHECK: fix-it:"{{.*}}":{[[@LINE-2]]:1-[[@LINE-2]]:8}:""
 export template void f1<char>(); // expected-error {{an explicit instantiation cannot be marked 'export'}}
-// expected-note at -1 {{as long as its primary template is exported, it will be too}}
+// expected-note at -1 {{it is exported if the primary template is exported}}
 // CHECK: fix-it:"{{.*}}":{[[@LINE-2]]:1-[[@LINE-2]]:8}:""
 export extern template void f1<void>(); // expected-error {{an explicit instantiation cannot be marked 'export'}}
-// expected-note at -1 {{as long as its primary template is exported, it will be too}}
+// expected-note at -1 {{it is exported if the primary template is exported}}
 // CHECK: fix-it:"{{.*}}":{[[@LINE-2]]:1-[[@LINE-2]]:8}:""
 
 
@@ -77,23 +77,23 @@ export { extern template void f2<void>(); }
 
 extern "C++" template <typename T> class s3 {};
 extern "C++" template <typename T> class s3<T *> {};
-extern "C++" template <> class s3<int> {}; // expected-error {{language linkage specification cannot be applied to a specialization}}
-extern "C++" template class s3<char>; // expected-error {{language linkage specification cannot be applied to an explicit instantiation}}
-extern "C++" extern template class s3<void>; // expected-error {{language linkage specification cannot be applied to an explicit instantiation}}
+extern "C++" template <> class s3<int> {}; // expected-error {{language linkage cannot be specified for a specialization}}
+extern "C++" template class s3<char>; // expected-error {{language linkage cannot be specified for an explicit instantiation}}
+extern "C++" extern template class s3<void>; // expected-error {{language linkage cannot be specified for an explicit instantiation}}
 
 extern "C++" template <typename T> int v3 = 0;
 extern "C++" template <typename T> int v3<T *> = 0;
-extern "C++" template <> int v3<int> = 0; // expected-error {{language linkage specification cannot be applied to a specialization}}
-extern "C++" template int v3<char>; // expected-error {{language linkage specification cannot be applied to an explicit instantiation}}
-extern "C++" extern template int v3<void>; // expected-error {{language linkage specification cannot be applied to an explicit instantiation}}
+extern "C++" template <> int v3<int> = 0; // expected-error {{language linkage cannot be specified for a specialization}}
+extern "C++" template int v3<char>; // expected-error {{language linkage cannot be specified for an explicit instantiation}}
+extern "C++" extern template int v3<void>; // expected-error {{language linkage cannot be specified for an explicit instantiation}}
 
 extern "C++" template <typename T> void f3() {}
-extern "C++" template <> void f3<int>() {} // expected-error {{language linkage specification cannot be applied to a specialization}}
-extern "C++" template void f3<char>(); // expected-error {{language linkage specification cannot be applied to an explicit instantiation}}
-extern "C++" extern template void f3<void>(); // expected-error {{language linkage specification cannot be applied to an explicit instantiation}}
+extern "C++" template <> void f3<int>() {} // expected-error {{language linkage cannot be specified for a specialization}}
+extern "C++" template void f3<char>(); // expected-error {{language linkage cannot be specified for an explicit instantiation}}
+extern "C++" extern template void f3<void>(); // expected-error {{language linkage cannot be specified for an explicit instantiation}}
 
-extern "C++" export int i; // expected-error {{language linkage specification cannot be applied to an export declaration}}
-extern "C++" export {} // expected-error {{language linkage specification cannot be applied to an export declaration}}
+extern "C++" export int i; // expected-error {{language linkage cannot be specified for an export declaration}}
+extern "C++" export {} // expected-error {{language linkage cannot be specified for an export declaration}}
 
 
 extern "C++" { template <typename T> class s4 {}; }
@@ -119,13 +119,12 @@ extern "C++" { extern template void f4<void>(); }
 namespace std {
 
 extern "C++" template <typename T> struct s {};
-extern "C++" template<> struct s<int> {}; // expected-no-error {{language linkage specification cannot be applied to a specialization}}
+extern "C++" template<> struct s<int> {};
 
 } // namespace std
 
-// Should still diagnose outside the std namespace.
 extern "C++" template <typename T> struct c {};
-extern "C++" template<> struct c<int> {}; // expected-error {{language linkage specification cannot be applied to a specialization}}
+extern "C++" template<> struct c<int> {}; // expected-error {{language linkage cannot be specified for a specialization}}
 
 //--- msvc-stl-exception-2.cpp
 #define _MSVC_STL_UPDATE 202603L
@@ -133,6 +132,6 @@ extern "C++" template<> struct c<int> {}; // expected-error {{language linkage s
 namespace std {
 
 extern "C++" template <typename T> struct s {};
-extern "C++" template<> struct s<int> {}; // expected-error {{language linkage specification cannot be applied to a specialization}}
+extern "C++" template<> struct s<int> {}; // expected-error {{language linkage cannot be specified for a specialization}}
 
 } // namespace std

>From c8bee3995af3c2c9fb754ee07d0ac438577196a1 Mon Sep 17 00:00:00 2001
From: Victor Chernyakin <chernyakin.victor.j at outlook.com>
Date: Mon, 27 Apr 2026 00:13:49 -0700
Subject: [PATCH 6/6] Mark CWG2443 as resolved and add tests

---
 clang/test/CXX/drs/cwg2443.cpp | 66 ++++++++++++++++++++++++++++++++++
 clang/test/CXX/drs/cwg24xx.cpp |  3 ++
 clang/www/cxx_dr_status.html   |  2 +-
 3 files changed, 70 insertions(+), 1 deletion(-)
 create mode 100644 clang/test/CXX/drs/cwg2443.cpp

diff --git a/clang/test/CXX/drs/cwg2443.cpp b/clang/test/CXX/drs/cwg2443.cpp
new file mode 100644
index 0000000000000..c883b1ff1358e
--- /dev/null
+++ b/clang/test/CXX/drs/cwg2443.cpp
@@ -0,0 +1,66 @@
+// RUN: %clang_cc1 -std=c++20 -fexceptions -fcxx-exceptions -pedantic-errors %s -verify
+// RUN: %clang_cc1 -std=c++23 -fexceptions -fcxx-exceptions -pedantic-errors %s -verify
+// RUN: %clang_cc1 -std=c++2c -fexceptions -fcxx-exceptions -pedantic-errors %s -verify
+
+export module foo;
+
+namespace cwg2443 { // cwg2443: 23
+
+export template <typename T> class s1 {};
+export template <typename T> class s1<T *> {}; // expected-error {{a specialization cannot be marked 'export'}}
+// expected-note at -1 {{it is exported if the primary template is exported}}
+// CHECK: fix-it:"{{.*}}":{[[@LINE-2]]:1-[[@LINE-2]]:8}:""
+export template <> class s1<int> {}; // expected-error {{a specialization cannot be marked 'export'}}
+// expected-note at -1 {{it is exported if the primary template is exported}}
+// CHECK: fix-it:"{{.*}}":{[[@LINE-2]]:1-[[@LINE-2]]:8}:""
+export template class s1<char>; // expected-error {{an explicit instantiation cannot be marked 'export'}}
+// expected-note at -1 {{it is exported if the primary template is exported}}
+// CHECK: fix-it:"{{.*}}":{[[@LINE-2]]:1-[[@LINE-2]]:8}:""
+export extern template class s1<void>; // expected-error {{an explicit instantiation cannot be marked 'export'}}
+// expected-note at -1 {{it is exported if the primary template is exported}}
+// CHECK: fix-it:"{{.*}}":{[[@LINE-2]]:1-[[@LINE-2]]:8}:""
+
+export template <typename T> int v1 = 0;
+export template <typename T> int v1<T *> = 0; // expected-error {{a specialization cannot be marked 'export'}}
+// expected-note at -1 {{it is exported if the primary template is exported}}
+// CHECK: fix-it:"{{.*}}":{[[@LINE-2]]:1-[[@LINE-2]]:8}:""
+export template <> int v1<int> = 0; // expected-error {{a specialization cannot be marked 'export'}}
+// expected-note at -1 {{it is exported if the primary template is exported}}
+// CHECK: fix-it:"{{.*}}":{[[@LINE-2]]:1-[[@LINE-2]]:8}:""
+export template int v1<char>; // expected-error {{an explicit instantiation cannot be marked 'export'}}
+// expected-note at -1 {{it is exported if the primary template is exported}}
+// CHECK: fix-it:"{{.*}}":{[[@LINE-2]]:1-[[@LINE-2]]:8}:""
+export extern template int v1<void>; // expected-error {{an explicit instantiation cannot be marked 'export'}}
+// expected-note at -1 {{it is exported if the primary template is exported}}
+// CHECK: fix-it:"{{.*}}":{[[@LINE-2]]:1-[[@LINE-2]]:8}:""
+
+export template <typename T> void f1() {}
+export template <> void f1<int>() {} // expected-error {{a specialization cannot be marked 'export'}}
+// expected-note at -1 {{it is exported if the primary template is exported}}
+// CHECK: fix-it:"{{.*}}":{[[@LINE-2]]:1-[[@LINE-2]]:8}:""
+export template void f1<char>(); // expected-error {{an explicit instantiation cannot be marked 'export'}}
+// expected-note at -1 {{it is exported if the primary template is exported}}
+// CHECK: fix-it:"{{.*}}":{[[@LINE-2]]:1-[[@LINE-2]]:8}:""
+export extern template void f1<void>(); // expected-error {{an explicit instantiation cannot be marked 'export'}}
+// expected-note at -1 {{it is exported if the primary template is exported}}
+// CHECK: fix-it:"{{.*}}":{[[@LINE-2]]:1-[[@LINE-2]]:8}:""
+
+
+export { template <typename T> class s2 {}; }
+export { template <typename T> class s2<T *> {}; }
+export { template <> class s2<int> {}; }
+export { template class s2<char>; }
+export { extern template class s2<void>; }
+
+export { template <typename T> int v2 = 0; }
+export { template <typename T> int v2<T *> = 0; }
+export { template <> int v2<int> = 0; }
+export { template int v2<char>; }
+export { extern template int v2<void>; }
+
+export { template <typename T> void f2() {} }
+export { template <> void f2<int>() {} }
+export { template void f2<char>(); }
+export { extern template void f2<void>(); }
+
+} // namespace cwg2443
diff --git a/clang/test/CXX/drs/cwg24xx.cpp b/clang/test/CXX/drs/cwg24xx.cpp
index 0a6a05c125451..7eb22f596e13f 100644
--- a/clang/test/CXX/drs/cwg24xx.cpp
+++ b/clang/test/CXX/drs/cwg24xx.cpp
@@ -65,6 +65,9 @@ struct S {
 };
 } // namespace cwg2430
 
+
+// cwg2443 is in cwg2443.cpp
+
 namespace cwg2450 { // cwg2450: 18
 #if __cplusplus >= 202302L
 struct S {int a;};
diff --git a/clang/www/cxx_dr_status.html b/clang/www/cxx_dr_status.html
index 2a1e79471ceea..f844487e977e3 100755
--- a/clang/www/cxx_dr_status.html
+++ b/clang/www/cxx_dr_status.html
@@ -16894,7 +16894,7 @@ <h2 id="cxxdr">C++ defect report implementation status</h2>
     <td>[<a href="https://wg21.link/module.interface">module.interface</a>]</td>
     <td>C++23</td>
     <td>Meaningless template exports</td>
-    <td class="unknown" align="center">Unknown</td>
+    <td class="unreleased" align="center">Clang 23</td>
   </tr>
   <tr class="open" id="2444">
     <td><a href="https://cplusplus.github.io/CWG/issues/2444.html">2444</a></td>



More information about the cfe-commits mailing list