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

Victor Chernyakin via cfe-commits cfe-commits at lists.llvm.org
Tue Apr 28 00:15:40 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 01/10] [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 02/10] 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 03/10] 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 04/10] 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 05/10] 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 06/10] 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>

>From 95c6d0058fca79d1a72dfad9e940d00d3333354c Mon Sep 17 00:00:00 2001
From: Victor Chernyakin <chernyakin.victor.j at outlook.com>
Date: Mon, 27 Apr 2026 08:14:44 -0700
Subject: [PATCH 07/10] Address feedback in DR test

---
 clang/test/CXX/drs/cwg2443.cpp | 72 +++++++++++++++++-----------------
 1 file changed, 36 insertions(+), 36 deletions(-)

diff --git a/clang/test/CXX/drs/cwg2443.cpp b/clang/test/CXX/drs/cwg2443.cpp
index c883b1ff1358e..0506caf1c14d6 100644
--- a/clang/test/CXX/drs/cwg2443.cpp
+++ b/clang/test/CXX/drs/cwg2443.cpp
@@ -1,49 +1,49 @@
-// 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
+// RUN: %clang_cc1 -std=c++20 -fexceptions -fcxx-exceptions -pedantic-errors %s -verify -verify-directives
+// RUN: %clang_cc1 -std=c++23 -fexceptions -fcxx-exceptions -pedantic-errors %s -verify -verify-directives
+// RUN: %clang_cc1 -std=c++2c -fexceptions -fcxx-exceptions -pedantic-errors %s -verify -verify-directives
 
 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> class s1<T *> {}; 
+// expected-error at -1 {{a specialization cannot be marked 'export'}}
+//   expected-note at -2 {{it is exported if the primary template is exported}}
+export template <> class s1<int> {}; 
+// expected-error at -1 {{a specialization cannot be marked 'export'}}
+//   expected-note at -2 {{it is exported if the primary template is exported}}
+export template class s1<char>; 
+// expected-error at -1 {{an explicit instantiation cannot be marked 'export'}}
+//   expected-note at -2 {{it is exported if the primary template is exported}}
+export extern template class s1<void>; 
+// expected-error at -1 {{an explicit instantiation cannot be marked 'export'}}
+//   expected-note at -2 {{it is exported if the primary template is exported}}
 
 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> int v1<T *> = 0; 
+// expected-error at -1 {{a specialization cannot be marked 'export'}}
+//   expected-note at -2 {{it is exported if the primary template is exported}}
+export template <> int v1<int> = 0; 
+// expected-error at -1 {{a specialization cannot be marked 'export'}}
+//   expected-note at -2 {{it is exported if the primary template is exported}}
+export template int v1<char>; 
+// expected-error at -1 {{an explicit instantiation cannot be marked 'export'}}
+//   expected-note at -2 {{it is exported if the primary template is exported}}
+export extern template int v1<void>; 
+// expected-error at -1 {{an explicit instantiation cannot be marked 'export'}}
+//   expected-note at -2 {{it is exported if the primary template is exported}}
 
 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 <> void f1<int>() {} 
+// expected-error at -1 {{a specialization cannot be marked 'export'}}
+//   expected-note at -2 {{it is exported if the primary template is exported}}
+export template void f1<char>(); 
+// expected-error at -1 {{an explicit instantiation cannot be marked 'export'}}
+//   expected-note at -2 {{it is exported if the primary template is exported}}
+export extern template void f1<void>(); 
+// expected-error at -1 {{an explicit instantiation cannot be marked 'export'}}
+//   expected-note at -2 {{it is exported if the primary template is exported}}
 
 
 export { template <typename T> class s2 {}; }

>From ac5f8265fede224cfe9cca682926ffbb0315060a Mon Sep 17 00:00:00 2001
From: Victor Chernyakin <chernyakin.victor.j at outlook.com>
Date: Mon, 27 Apr 2026 22:40:49 -0700
Subject: [PATCH 08/10] Factor out some reusable functions

---
 clang/include/clang/AST/DeclTemplate.h |  4 +++
 clang/include/clang/Lex/Preprocessor.h | 26 ++++++++++++++--
 clang/lib/AST/DeclTemplate.cpp         | 16 ++++++++++
 clang/lib/Lex/PPExpressions.cpp        | 34 +++++++++------------
 clang/lib/Parse/ParseDeclCXX.cpp       | 41 +++++---------------------
 clang/lib/Sema/SemaTemplate.cpp        | 18 -----------
 6 files changed, 64 insertions(+), 75 deletions(-)

diff --git a/clang/include/clang/AST/DeclTemplate.h b/clang/include/clang/AST/DeclTemplate.h
index 9fb41c87da732..47e12f7ac584a 100644
--- a/clang/include/clang/AST/DeclTemplate.h
+++ b/clang/include/clang/AST/DeclTemplate.h
@@ -3538,6 +3538,10 @@ class ExplicitInstantiationDecl final
   static bool classofKind(Kind K) { return K == ExplicitInstantiation; }
 };
 
+/// Determine what kind of template specialization the given declaration
+/// is.
+TemplateSpecializationKind getTemplateSpecializationKind(const Decl *D);
+
 } // namespace clang
 
 #endif // LLVM_CLANG_AST_DECLTEMPLATE_H
diff --git a/clang/include/clang/Lex/Preprocessor.h b/clang/include/clang/Lex/Preprocessor.h
index 8830294ea1658..acfa9e1eabef0 100644
--- a/clang/include/clang/Lex/Preprocessor.h
+++ b/clang/include/clang/Lex/Preprocessor.h
@@ -132,7 +132,7 @@ enum class EmbedResult {
 };
 
 struct CXXStandardLibraryVersionInfo {
-  enum Library { Unknown, LibStdCXX };
+  enum Library { Unknown, LibStdCXX, MsvcStl };
   Library Lib;
   std::uint64_t Version;
 };
@@ -2823,9 +2823,29 @@ class Preprocessor {
   // Standard Library Identification
   std::optional<CXXStandardLibraryVersionInfo> CXXStandardLibraryVersion;
 
+  void ComputeCXXStandardLibraryVersion();
+
+  bool NeedsCXXStandardLibraryWorkaroundBefore(
+      uint64_t FixedVersion, CXXStandardLibraryVersionInfo::Library Lib) {
+    ComputeCXXStandardLibraryVersion();
+    return CXXStandardLibraryVersion && CXXStandardLibraryVersion->Lib == Lib &&
+           CXXStandardLibraryVersion->Version < FixedVersion;
+  }
+
 public:
-  std::optional<std::uint64_t> getStdLibCxxVersion();
-  bool NeedsStdLibCxxWorkaroundBefore(std::uint64_t FixedVersion);
+  bool NeedsStdLibCxxWorkaroundBefore(std::uint64_t FixedVersion) {
+    assert(FixedVersion >= 2000'00'00 && FixedVersion <= 2100'00'00 &&
+           "invalid value for __GLIBCXX__");
+    return NeedsCXXStandardLibraryWorkaroundBefore(
+        FixedVersion, CXXStandardLibraryVersionInfo::LibStdCXX);
+  }
+
+  bool NeedsMsvcStlWorkaroundBefore(std::uint64_t FixedVersion) {
+    assert(FixedVersion >= 2000'00 && FixedVersion <= 2100'00 &&
+           "invalid value for _MSVC_STL_UPDATE");
+    return NeedsCXXStandardLibraryWorkaroundBefore(
+        FixedVersion, CXXStandardLibraryVersionInfo::MsvcStl);
+  }
 
 private:
   //===--------------------------------------------------------------------===//
diff --git a/clang/lib/AST/DeclTemplate.cpp b/clang/lib/AST/DeclTemplate.cpp
index 6567c8fa4d783..75878c2ef0ee8 100644
--- a/clang/lib/AST/DeclTemplate.cpp
+++ b/clang/lib/AST/DeclTemplate.cpp
@@ -1913,3 +1913,19 @@ SourceRange ExplicitInstantiationDecl::getSourceRange() const {
   SourceLocation Begin = ExternLoc.isValid() ? ExternLoc : getLocation();
   return SourceRange(Begin, getEndLoc());
 }
+
+TemplateSpecializationKind clang::getTemplateSpecializationKind(const Decl *D) {
+  if (!D)
+    return TSK_Undeclared;
+
+  if (const auto *Record = dyn_cast<CXXRecordDecl>(D))
+    return Record->getTemplateSpecializationKind();
+  if (const auto *Function = dyn_cast<FunctionDecl>(D))
+    return Function->getTemplateSpecializationKind();
+  if (const auto *Var = dyn_cast<VarDecl>(D))
+    return Var->getTemplateSpecializationKind();
+  if (const auto *EID = dyn_cast<ExplicitInstantiationDecl>(D))
+    return EID->getTemplateSpecializationKind();
+
+  return TSK_Undeclared;
+}
diff --git a/clang/lib/Lex/PPExpressions.cpp b/clang/lib/Lex/PPExpressions.cpp
index 887fd25ac318d..4e596965bb606 100644
--- a/clang/lib/Lex/PPExpressions.cpp
+++ b/clang/lib/Lex/PPExpressions.cpp
@@ -1005,24 +1005,18 @@ getCXXStandardLibraryVersion(Preprocessor &PP, StringRef MacroName,
                                        0};
 }
 
-std::optional<uint64_t> Preprocessor::getStdLibCxxVersion() {
-  if (!CXXStandardLibraryVersion)
-    CXXStandardLibraryVersion = getCXXStandardLibraryVersion(
-        *this, "__GLIBCXX__", CXXStandardLibraryVersionInfo::LibStdCXX);
-  if (!CXXStandardLibraryVersion)
-    return std::nullopt;
-
-  if (CXXStandardLibraryVersion->Lib ==
-      CXXStandardLibraryVersionInfo::LibStdCXX)
-    return CXXStandardLibraryVersion->Version;
-  return std::nullopt;
-}
-
-bool Preprocessor::NeedsStdLibCxxWorkaroundBefore(uint64_t FixedVersion) {
-  assert(FixedVersion >= 2000'00'00 && FixedVersion <= 2100'00'00 &&
-         "invalid value for __GLIBCXX__");
-  std::optional<std::uint64_t> Ver = getStdLibCxxVersion();
-  if (!Ver)
-    return false;
-  return *Ver < FixedVersion;
+void Preprocessor::ComputeCXXStandardLibraryVersion() {
+  if (CXXStandardLibraryVersion)
+    return;
+
+  static constexpr std::pair<StringRef, CXXStandardLibraryVersionInfo::Library>
+      VersionMacros[] = {
+          {"__GLIBCXX__", CXXStandardLibraryVersionInfo::LibStdCXX},
+          {"_MSVC_STL_UPDATE", CXXStandardLibraryVersionInfo::MsvcStl},
+      };
+
+  for (const auto &[MacroName, Lib] : VersionMacros)
+    if (std::optional<CXXStandardLibraryVersionInfo> VersionInfo =
+            getCXXStandardLibraryVersion(*this, MacroName, Lib))
+      CXXStandardLibraryVersion = VersionInfo;
 }
diff --git a/clang/lib/Parse/ParseDeclCXX.cpp b/clang/lib/Parse/ParseDeclCXX.cpp
index 92e76a11ed02a..cd8be9d7af860 100644
--- a/clang/lib/Parse/ParseDeclCXX.cpp
+++ b/clang/lib/Parse/ParseDeclCXX.cpp
@@ -446,16 +446,14 @@ Decl *Parser::ParseExportDeclaration() {
 void Parser::CheckUnbracedLinkageOrExportDeclaration(
     Decl *LinkageOrExportDecl) {
   const auto *DC = cast<DeclContext>(LinkageOrExportDecl);
-  if (DC->decls_empty())
-    return;
-
+  assert(!DC->decls_empty());
   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;
+        << /*export declaration*/ 2;
     return;
   }
 
@@ -464,24 +462,13 @@ void Parser::CheckUnbracedLinkageOrExportDeclaration(
   //     The name-declaration of an export-declaration shall not declare a
   //     partial specialization.
   //
-  // There's no equivalent wording for linkage-specification.
+  // But 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();
-    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;
-  }();
-
+  TemplateSpecializationKind TSK = getTemplateSpecializationKind(D);
   if (TSK == TSK_Undeclared)
     return;
 
@@ -494,26 +481,12 @@ void Parser::CheckUnbracedLinkageOrExportDeclaration(
   }
 
   if (const auto *LS = dyn_cast<LinkageSpecDecl>(LinkageOrExportDecl)) {
-    // HACK: Old versions of the MSVC STL used to have linkage specifications
+    // 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)
+    if (LinkageOrExportDecl->isInStdNamespace() &&
+        getPreprocessor().NeedsMsvcStlWorkaroundBefore(2026'03))
       return;
 
     Diag(LS->getLocation(), diag::err_invalid_decl_in_linkage_spec)
diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp
index 7b7d43ef3234c..64c725b5331fd 100644
--- a/clang/lib/Sema/SemaTemplate.cpp
+++ b/clang/lib/Sema/SemaTemplate.cpp
@@ -4155,8 +4155,6 @@ static bool CheckTemplateSpecializationScope(Sema &S, NamedDecl *Specialized,
                                              SourceLocation Loc,
                                              bool IsPartialSpecialization);
 
-static TemplateSpecializationKind getTemplateSpecializationKind(Decl *D);
-
 static bool isTemplateArgumentTemplateParameter(const TemplateArgument &Arg,
                                                 unsigned Depth,
                                                 unsigned Index) {
@@ -8504,22 +8502,6 @@ Sema::CheckTemplateDeclScope(Scope *S, TemplateParameterList *TemplateParams) {
     << TemplateParams->getSourceRange();
 }
 
-/// Determine what kind of template specialization the given declaration
-/// is.
-static TemplateSpecializationKind getTemplateSpecializationKind(Decl *D) {
-  if (!D)
-    return TSK_Undeclared;
-
-  if (CXXRecordDecl *Record = dyn_cast<CXXRecordDecl>(D))
-    return Record->getTemplateSpecializationKind();
-  if (FunctionDecl *Function = dyn_cast<FunctionDecl>(D))
-    return Function->getTemplateSpecializationKind();
-  if (VarDecl *Var = dyn_cast<VarDecl>(D))
-    return Var->getTemplateSpecializationKind();
-
-  return TSK_Undeclared;
-}
-
 /// Check whether a specialization is well-formed in the current
 /// context.
 ///

>From 8270a0247ecdbfb772efb35243e5c784f7144f7e Mon Sep 17 00:00:00 2001
From: Victor Chernyakin <chernyakin.victor.j at outlook.com>
Date: Mon, 27 Apr 2026 23:04:06 -0700
Subject: [PATCH 09/10] Undo assert

---
 clang/lib/Parse/ParseDeclCXX.cpp | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/clang/lib/Parse/ParseDeclCXX.cpp b/clang/lib/Parse/ParseDeclCXX.cpp
index cd8be9d7af860..af9f7030285e5 100644
--- a/clang/lib/Parse/ParseDeclCXX.cpp
+++ b/clang/lib/Parse/ParseDeclCXX.cpp
@@ -446,7 +446,9 @@ Decl *Parser::ParseExportDeclaration() {
 void Parser::CheckUnbracedLinkageOrExportDeclaration(
     Decl *LinkageOrExportDecl) {
   const auto *DC = cast<DeclContext>(LinkageOrExportDecl);
-  assert(!DC->decls_empty());
+  if (DC->decls_empty())
+    return;
+
   const Decl *D = *DC->decls_begin();
 
   // Nested export declarations are diagnosed elsewhere.

>From a4c37e41f4c9ab02c3491eb25fce300fdab4c437 Mon Sep 17 00:00:00 2001
From: Victor Chernyakin <chernyakin.victor.j at outlook.com>
Date: Tue, 28 Apr 2026 00:10:25 -0700
Subject: [PATCH 10/10] Remove trailing whitespace

---
 clang/test/CXX/drs/cwg2443.cpp | 22 +++++++++++-----------
 1 file changed, 11 insertions(+), 11 deletions(-)

diff --git a/clang/test/CXX/drs/cwg2443.cpp b/clang/test/CXX/drs/cwg2443.cpp
index 0506caf1c14d6..02af26d5c98b1 100644
--- a/clang/test/CXX/drs/cwg2443.cpp
+++ b/clang/test/CXX/drs/cwg2443.cpp
@@ -7,41 +7,41 @@ export module foo;
 namespace cwg2443 { // cwg2443: 23
 
 export template <typename T> class s1 {};
-export template <typename T> class s1<T *> {}; 
+export template <typename T> class s1<T *> {};
 // expected-error at -1 {{a specialization cannot be marked 'export'}}
 //   expected-note at -2 {{it is exported if the primary template is exported}}
-export template <> class s1<int> {}; 
+export template <> class s1<int> {};
 // expected-error at -1 {{a specialization cannot be marked 'export'}}
 //   expected-note at -2 {{it is exported if the primary template is exported}}
-export template class s1<char>; 
+export template class s1<char>;
 // expected-error at -1 {{an explicit instantiation cannot be marked 'export'}}
 //   expected-note at -2 {{it is exported if the primary template is exported}}
-export extern template class s1<void>; 
+export extern template class s1<void>;
 // expected-error at -1 {{an explicit instantiation cannot be marked 'export'}}
 //   expected-note at -2 {{it is exported if the primary template is exported}}
 
 export template <typename T> int v1 = 0;
-export template <typename T> int v1<T *> = 0; 
+export template <typename T> int v1<T *> = 0;
 // expected-error at -1 {{a specialization cannot be marked 'export'}}
 //   expected-note at -2 {{it is exported if the primary template is exported}}
-export template <> int v1<int> = 0; 
+export template <> int v1<int> = 0;
 // expected-error at -1 {{a specialization cannot be marked 'export'}}
 //   expected-note at -2 {{it is exported if the primary template is exported}}
-export template int v1<char>; 
+export template int v1<char>;
 // expected-error at -1 {{an explicit instantiation cannot be marked 'export'}}
 //   expected-note at -2 {{it is exported if the primary template is exported}}
-export extern template int v1<void>; 
+export extern template int v1<void>;
 // expected-error at -1 {{an explicit instantiation cannot be marked 'export'}}
 //   expected-note at -2 {{it is exported if the primary template is exported}}
 
 export template <typename T> void f1() {}
-export template <> void f1<int>() {} 
+export template <> void f1<int>() {}
 // expected-error at -1 {{a specialization cannot be marked 'export'}}
 //   expected-note at -2 {{it is exported if the primary template is exported}}
-export template void f1<char>(); 
+export template void f1<char>();
 // expected-error at -1 {{an explicit instantiation cannot be marked 'export'}}
 //   expected-note at -2 {{it is exported if the primary template is exported}}
-export extern template void f1<void>(); 
+export extern template void f1<void>();
 // expected-error at -1 {{an explicit instantiation cannot be marked 'export'}}
 //   expected-note at -2 {{it is exported if the primary template is exported}}
 



More information about the cfe-commits mailing list