[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