From b8f38d51fef8dfd2cbaecbf1fd2b6252573cb0f9 Mon Sep 17 00:00:00 2001 From: Victor Chernyakin Date: Sat, 25 Apr 2026 21:16:00 -0700 Subject: [PATCH 01/13] [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 c5c8c1fa12e7aa..357ef60d8bdc17 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 7ed4684c8359e3..941d27ce1b11c0 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>; diff --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h index 1d07d8dbcfa01f..e4d91451db09ef 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 d1e2e2c2c6ce11..7ee1b7d4d9e75f 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(LinkageOrExportDecl); + if (DC->decls_empty()) + return; + + const Decl *D = *DC->decls_begin(); + + // Nested export declarations are diagnosed elsewhere. + if (isa(LinkageOrExportDecl) && isa(D)) { + Diag(LinkageOrExportDecl->getLocation(), + diag::err_invalid_decl_in_linkage_spec) + << 2; + return; + } + + TemplateSpecializationKind TSK = [&] { + if (const auto *EID = dyn_cast(D)) + return EID->getTemplateSpecializationKind(); + if (const auto *CTSD = dyn_cast(D)) + return CTSD->getTemplateSpecializationKind(); + if (const auto *VTSD = dyn_cast(D)) + return VTSD->getTemplateSpecializationKind(); + if (const auto *FD = dyn_cast(D)) + return FD->getTemplateSpecializationKind(); + return TSK_Undeclared; + }(); + + if (TSK == TSK_Undeclared) + return; + + if (const auto *ED = dyn_cast(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(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 914144018e8808..b5b8d152e3a7b5 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 { static constexpr bool selected = true; }; @@ -34,7 +34,7 @@ struct V { static constexpr bool selected = false; }; -export template <> +template<> struct V { 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 { static constexpr bool selected = true; }; @@ -79,7 +79,7 @@ struct Array { }; export int array[5]; -export template<> +template<> struct 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 f389d9604ef3a8..e13d0003f248b0 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 db3f4cd5187169..e09fcbaa92d953 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 constexpr T* zero = nullptr; // expected-error-re {{decla // expected-note@* {{previous}} template <> constexpr int** zero = nullptr; // ok, new specialization. -template constexpr T** zero = nullptr; // ok, new partial specilization. +template constexpr T** zero = nullptr; // ok, new partial specialization. //--- var_def.cppm export module var_def; @@ -37,8 +37,16 @@ export template constexpr T zero = 0; export struct Int { int value; }; -export template <> constexpr Int zero = {0}; -export template constexpr T* zero = 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 = {0}; +template constexpr T* zero = nullptr; + +} //--- reexport1.cppm export module reexport1; diff --git a/clang/test/Modules/pr59780.cppm b/clang/test/Modules/pr59780.cppm index ea5fdd5c4ce268..8e2685e3281333 100644 --- a/clang/test/Modules/pr59780.cppm +++ b/clang/test/Modules/pr59780.cppm @@ -25,7 +25,7 @@ export module a; export template int x = 0; -export template<> +template<> int x = 0; export template @@ -36,7 +36,7 @@ struct Y { template int Y::value = 0; -export template<> +template<> struct Y { static int value; }; diff --git a/clang/test/Modules/pr60890.cppm b/clang/test/Modules/pr60890.cppm index b1d9114bf1ebee..5df2f2ea4bdf24 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; +template struct a; export template void foo(T) requires(true) {} -export template void foo(double); +template void foo(double); export template class A { diff --git a/clang/test/Modules/pr97313.cppm b/clang/test/Modules/pr97313.cppm index 99795d6e43030f..833e7ff416247c 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 { public: virtual ~Template(); @@ -84,11 +84,11 @@ public: // CHECK: @_ZTIW3Mod8TemplateIcE = {{.*}}constant -export template class Template; +template class Template; // CHECK: @_ZTIW3Mod8TemplateIjE = {{.*}}weak_odr -export extern template class Template; +extern template class Template; auto v = new Template(); diff --git a/clang/test/Modules/template-function-specialization.cpp b/clang/test/Modules/template-function-specialization.cpp index d5d7d7e8123988..7619f76ab56348 100644 --- a/clang/test/Modules/template-function-specialization.cpp +++ b/clang/test/Modules/template-function-specialization.cpp @@ -42,7 +42,7 @@ export template void foo4() { } -export template <> +template <> void foo4() { } diff --git a/clang/test/SemaCXX/P2615.cpp b/clang/test/SemaCXX/P2615.cpp index fed177c21bc790..947d7e4bf459ff 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 class s1 {}; +export template class s1 {}; // expected-error {{a specialization cannot be marked 'export'}} +// expected-note@-1 {{as long as its primary template is exported, it will be too}} +export template <> class s1 {}; // expected-error {{a specialization cannot be marked 'export'}} +// expected-note@-1 {{as long as its primary template is exported, it will be too}} +export template class s1; // expected-error {{an explicit instantiation cannot be marked 'export'}} +// expected-note@-1 {{as long as its primary template is exported, it will be too}} +export extern template class s1; // expected-error {{an explicit instantiation cannot be marked 'export'}} +// expected-note@-1 {{as long as its primary template is exported, it will be too}} + +export template int v1 = 0; +export template int v1 = 0; // expected-error {{a specialization cannot be marked 'export'}} +// expected-note@-1 {{as long as its primary template is exported, it will be too}} +export template <> int v1 = 0; // expected-error {{a specialization cannot be marked 'export'}} +// expected-note@-1 {{as long as its primary template is exported, it will be too}} +export template int v1; // expected-error {{an explicit instantiation cannot be marked 'export'}} +// expected-note@-1 {{as long as its primary template is exported, it will be too}} +export extern template int v1; // expected-error {{an explicit instantiation cannot be marked 'export'}} +// expected-note@-1 {{as long as its primary template is exported, it will be too}} + +export template void f1() {} +export template <> void f1() {} // expected-error {{a specialization cannot be marked 'export'}} +// expected-note@-1 {{as long as its primary template is exported, it will be too}} +export template void f1(); // expected-error {{an explicit instantiation cannot be marked 'export'}} +// expected-note@-1 {{as long as its primary template is exported, it will be too}} +export extern template void f1(); // expected-error {{an explicit instantiation cannot be marked 'export'}} +// expected-note@-1 {{as long as its primary template is exported, it will be too}} + + +export { template class s2 {}; } +export { template class s2 {}; } +export { template <> class s2 {}; } +export { template class s2; } +export { extern template class s2; } + +export { template int v2 = 0; } +export { template int v2 = 0; } +export { template <> int v2 = 0; } +export { template int v2; } +export { extern template int v2; } + +export { template void f2() {} } +export { template <> void f2() {} } +export { template void f2(); } +export { extern template void f2(); } + + +extern "C++" template class s3 {}; +extern "C++" template class s3 {}; // expected-error {{language linkage specification cannot be applied to a specialization}} +extern "C++" template <> class s3 {}; // expected-error {{language linkage specification cannot be applied to a specialization}} +extern "C++" template class s3; // expected-error {{language linkage specification cannot be applied to an explicit instantiation}} +extern "C++" extern template class s3; // expected-error {{language linkage specification cannot be applied to an explicit instantiation}} + +extern "C++" template int v3 = 0; +extern "C++" template int v3 = 0; // expected-error {{language linkage specification cannot be applied to a specialization}} +extern "C++" template <> int v3 = 0; // expected-error {{language linkage specification cannot be applied to a specialization}} +extern "C++" template int v3; // expected-error {{language linkage specification cannot be applied to an explicit instantiation}} +extern "C++" extern template int v3; // expected-error {{language linkage specification cannot be applied to an explicit instantiation}} + +extern "C++" template void f3() {} +extern "C++" template <> void f3() {} // expected-error {{language linkage specification cannot be applied to a specialization}} +extern "C++" template void f3(); // expected-error {{language linkage specification cannot be applied to an explicit instantiation}} +extern "C++" extern template void f3(); // 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 class s4 {}; } +extern "C++" { template class s4 {}; } +extern "C++" { template <> class s4 {}; } +extern "C++" { template class s4; } +extern "C++" { extern template class s4; } + +extern "C++" { template int v4 = 0; } +extern "C++" { template int v4 = 0; } +extern "C++" { template <> int v4 = 0; } +extern "C++" { template int v4; } +extern "C++" { extern template int v4; } + +extern "C++" { template void f4() {} } +extern "C++" { template <> void f4() {} } +extern "C++" { template void f4(); } +extern "C++" { extern template void f4(); } diff --git a/clang/test/SemaHLSL/Language/groupsharedArgs/ExportNoInlineTest.hlsl b/clang/test/SemaHLSL/Language/groupsharedArgs/ExportNoInlineTest.hlsl index c120adb4a4fbcf..9881b9d43897c1 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(groupshared uint A, groupshared uint B); +template void fn3(groupshared uint A, groupshared uint B); template __attribute__((noinline)) void fn3(groupshared float A, groupshared float B); // expected-error@-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 f55925aeae1f20..33fabf451de70b 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 { +template<> class A { A AS; A AB; }; diff --git a/clang/www/cxx_status.html b/clang/www/cxx_status.html index 3096e55803b84f..8a0f3a9b28439d 100755 --- a/clang/www/cxx_status.html +++ b/clang/www/cxx_status.html @@ -988,7 +988,7 @@

C++20 implementation status

P2615R1 (DR) - Clang 17 + Clang 23 P2788R0 (DR) From b2e7ab497d8e9f01b521b01b33ebbd756a977a75 Mon Sep 17 00:00:00 2001 From: Victor Chernyakin Date: Sun, 26 Apr 2026 11:19:49 -0700 Subject: [PATCH 02/13] 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 947d7e4bf459ff..ea57bfd790a1e1 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 class s1 {}; export template class s1 {}; // expected-error {{a specialization cannot be marked 'export'}} // expected-note@-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 {}; // expected-error {{a specialization cannot be marked 'export'}} // expected-note@-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; // expected-error {{an explicit instantiation cannot be marked 'export'}} // expected-note@-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; // expected-error {{an explicit instantiation cannot be marked 'export'}} // expected-note@-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 = 0; export template int v1 = 0; // expected-error {{a specialization cannot be marked 'export'}} // expected-note@-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 = 0; // expected-error {{a specialization cannot be marked 'export'}} // expected-note@-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; // expected-error {{an explicit instantiation cannot be marked 'export'}} // expected-note@-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; // expected-error {{an explicit instantiation cannot be marked 'export'}} // expected-note@-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() {} export template <> void f1() {} // expected-error {{a specialization cannot be marked 'export'}} // expected-note@-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(); // expected-error {{an explicit instantiation cannot be marked 'export'}} // expected-note@-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(); // expected-error {{an explicit instantiation cannot be marked 'export'}} // expected-note@-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 s2 {}; } From fc4e1c3cfa4cb5d4f8a577e00d7928daf676a0a1 Mon Sep 17 00:00:00 2001 From: Victor Chernyakin Date: Sun, 26 Apr 2026 11:33:11 -0700 Subject: [PATCH 03/13] 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 7ee1b7d4d9e75f..efbfa43a387943 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(D) && + isa(LinkageOrExportDecl)) + return; + TemplateSpecializationKind TSK = [&] { if (const auto *EID = dyn_cast(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 ea57bfd790a1e1..e338ded05c94c8 100644 --- a/clang/test/SemaCXX/P2615.cpp +++ b/clang/test/SemaCXX/P2615.cpp @@ -74,13 +74,13 @@ export { extern template void f2(); } extern "C++" template class s3 {}; -extern "C++" template class s3 {}; // expected-error {{language linkage specification cannot be applied to a specialization}} +extern "C++" template class s3 {}; extern "C++" template <> class s3 {}; // expected-error {{language linkage specification cannot be applied to a specialization}} extern "C++" template class s3; // expected-error {{language linkage specification cannot be applied to an explicit instantiation}} extern "C++" extern template class s3; // expected-error {{language linkage specification cannot be applied to an explicit instantiation}} extern "C++" template int v3 = 0; -extern "C++" template int v3 = 0; // expected-error {{language linkage specification cannot be applied to a specialization}} +extern "C++" template int v3 = 0; extern "C++" template <> int v3 = 0; // expected-error {{language linkage specification cannot be applied to a specialization}} extern "C++" template int v3; // expected-error {{language linkage specification cannot be applied to an explicit instantiation}} extern "C++" extern template int v3; // expected-error {{language linkage specification cannot be applied to an explicit instantiation}} From 012415d30920c8892d6391dd16fb1577a505a336 Mon Sep 17 00:00:00 2001 From: Victor Chernyakin Date: Sun, 26 Apr 2026 13:02:32 -0700 Subject: [PATCH 04/13] 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 efbfa43a387943..92e76a11ed02a4 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(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 e338ded05c94c8..e75a530ae0812a 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 void f4() {} } extern "C++" { template <> void f4() {} } extern "C++" { template void f4(); } extern "C++" { extern template void f4(); } + +//--- msvc-stl-exception-1.cpp +#define _MSVC_STL_UPDATE 202602L + +namespace std { + +extern "C++" template struct s {}; +extern "C++" template<> struct s {}; // expected-no-error {{language linkage specification cannot be applied to a specialization}} + +} // namespace std + +// Should still diagnose outside the std namespace. +extern "C++" template struct c {}; +extern "C++" template<> struct c {}; // 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 struct s {}; +extern "C++" template<> struct s {}; // 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 Date: Mon, 27 Apr 2026 00:13:16 -0700 Subject: [PATCH 05/13] 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 941d27ce1b11c0..e981eddcd6bd0f 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 e75a530ae0812a..c0b6abd9731bd1 100644 --- a/clang/test/SemaCXX/P2615.cpp +++ b/clang/test/SemaCXX/P2615.cpp @@ -19,41 +19,41 @@ export module B; export template class s1 {}; export template class s1 {}; // expected-error {{a specialization cannot be marked 'export'}} -// expected-note@-1 {{as long as its primary template is exported, it will be too}} +// expected-note@-1 {{it is exported if the primary template is exported}} // CHECK: fix-it:"{{.*}}":{[[@LINE-2]]:1-[[@LINE-2]]:8}:"" export template <> class s1 {}; // expected-error {{a specialization cannot be marked 'export'}} -// expected-note@-1 {{as long as its primary template is exported, it will be too}} +// expected-note@-1 {{it is exported if the primary template is exported}} // CHECK: fix-it:"{{.*}}":{[[@LINE-2]]:1-[[@LINE-2]]:8}:"" export template class s1; // expected-error {{an explicit instantiation cannot be marked 'export'}} -// expected-note@-1 {{as long as its primary template is exported, it will be too}} +// expected-note@-1 {{it is exported if the primary template is exported}} // CHECK: fix-it:"{{.*}}":{[[@LINE-2]]:1-[[@LINE-2]]:8}:"" export extern template class s1; // expected-error {{an explicit instantiation cannot be marked 'export'}} -// expected-note@-1 {{as long as its primary template is exported, it will be too}} +// expected-note@-1 {{it is exported if the primary template is exported}} // CHECK: fix-it:"{{.*}}":{[[@LINE-2]]:1-[[@LINE-2]]:8}:"" export template int v1 = 0; export template int v1 = 0; // expected-error {{a specialization cannot be marked 'export'}} -// expected-note@-1 {{as long as its primary template is exported, it will be too}} +// expected-note@-1 {{it is exported if the primary template is exported}} // CHECK: fix-it:"{{.*}}":{[[@LINE-2]]:1-[[@LINE-2]]:8}:"" export template <> int v1 = 0; // expected-error {{a specialization cannot be marked 'export'}} -// expected-note@-1 {{as long as its primary template is exported, it will be too}} +// expected-note@-1 {{it is exported if the primary template is exported}} // CHECK: fix-it:"{{.*}}":{[[@LINE-2]]:1-[[@LINE-2]]:8}:"" export template int v1; // expected-error {{an explicit instantiation cannot be marked 'export'}} -// expected-note@-1 {{as long as its primary template is exported, it will be too}} +// expected-note@-1 {{it is exported if the primary template is exported}} // CHECK: fix-it:"{{.*}}":{[[@LINE-2]]:1-[[@LINE-2]]:8}:"" export extern template int v1; // expected-error {{an explicit instantiation cannot be marked 'export'}} -// expected-note@-1 {{as long as its primary template is exported, it will be too}} +// expected-note@-1 {{it is exported if the primary template is exported}} // CHECK: fix-it:"{{.*}}":{[[@LINE-2]]:1-[[@LINE-2]]:8}:"" export template void f1() {} export template <> void f1() {} // expected-error {{a specialization cannot be marked 'export'}} -// expected-note@-1 {{as long as its primary template is exported, it will be too}} +// expected-note@-1 {{it is exported if the primary template is exported}} // CHECK: fix-it:"{{.*}}":{[[@LINE-2]]:1-[[@LINE-2]]:8}:"" export template void f1(); // expected-error {{an explicit instantiation cannot be marked 'export'}} -// expected-note@-1 {{as long as its primary template is exported, it will be too}} +// expected-note@-1 {{it is exported if the primary template is exported}} // CHECK: fix-it:"{{.*}}":{[[@LINE-2]]:1-[[@LINE-2]]:8}:"" export extern template void f1(); // expected-error {{an explicit instantiation cannot be marked 'export'}} -// expected-note@-1 {{as long as its primary template is exported, it will be too}} +// expected-note@-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(); } extern "C++" template class s3 {}; extern "C++" template class s3 {}; -extern "C++" template <> class s3 {}; // expected-error {{language linkage specification cannot be applied to a specialization}} -extern "C++" template class s3; // expected-error {{language linkage specification cannot be applied to an explicit instantiation}} -extern "C++" extern template class s3; // expected-error {{language linkage specification cannot be applied to an explicit instantiation}} +extern "C++" template <> class s3 {}; // expected-error {{language linkage cannot be specified for a specialization}} +extern "C++" template class s3; // expected-error {{language linkage cannot be specified for an explicit instantiation}} +extern "C++" extern template class s3; // expected-error {{language linkage cannot be specified for an explicit instantiation}} extern "C++" template int v3 = 0; extern "C++" template int v3 = 0; -extern "C++" template <> int v3 = 0; // expected-error {{language linkage specification cannot be applied to a specialization}} -extern "C++" template int v3; // expected-error {{language linkage specification cannot be applied to an explicit instantiation}} -extern "C++" extern template int v3; // expected-error {{language linkage specification cannot be applied to an explicit instantiation}} +extern "C++" template <> int v3 = 0; // expected-error {{language linkage cannot be specified for a specialization}} +extern "C++" template int v3; // expected-error {{language linkage cannot be specified for an explicit instantiation}} +extern "C++" extern template int v3; // expected-error {{language linkage cannot be specified for an explicit instantiation}} extern "C++" template void f3() {} -extern "C++" template <> void f3() {} // expected-error {{language linkage specification cannot be applied to a specialization}} -extern "C++" template void f3(); // expected-error {{language linkage specification cannot be applied to an explicit instantiation}} -extern "C++" extern template void f3(); // expected-error {{language linkage specification cannot be applied to an explicit instantiation}} +extern "C++" template <> void f3() {} // expected-error {{language linkage cannot be specified for a specialization}} +extern "C++" template void f3(); // expected-error {{language linkage cannot be specified for an explicit instantiation}} +extern "C++" extern template void f3(); // 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 class s4 {}; } @@ -119,13 +119,12 @@ extern "C++" { extern template void f4(); } namespace std { extern "C++" template struct s {}; -extern "C++" template<> struct s {}; // expected-no-error {{language linkage specification cannot be applied to a specialization}} +extern "C++" template<> struct s {}; } // namespace std -// Should still diagnose outside the std namespace. extern "C++" template struct c {}; -extern "C++" template<> struct c {}; // expected-error {{language linkage specification cannot be applied to a specialization}} +extern "C++" template<> struct c {}; // 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 {}; // expected-error {{language linkage s namespace std { extern "C++" template struct s {}; -extern "C++" template<> struct s {}; // expected-error {{language linkage specification cannot be applied to a specialization}} +extern "C++" template<> struct s {}; // expected-error {{language linkage cannot be specified for a specialization}} } // namespace std From c8bee3995af3c2c9fb754ee07d0ac438577196a1 Mon Sep 17 00:00:00 2001 From: Victor Chernyakin Date: Mon, 27 Apr 2026 00:13:49 -0700 Subject: [PATCH 06/13] 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 00000000000000..c883b1ff1358ee --- /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 class s1 {}; +export template class s1 {}; // expected-error {{a specialization cannot be marked 'export'}} +// expected-note@-1 {{it is exported if the primary template is exported}} +// CHECK: fix-it:"{{.*}}":{[[@LINE-2]]:1-[[@LINE-2]]:8}:"" +export template <> class s1 {}; // expected-error {{a specialization cannot be marked 'export'}} +// expected-note@-1 {{it is exported if the primary template is exported}} +// CHECK: fix-it:"{{.*}}":{[[@LINE-2]]:1-[[@LINE-2]]:8}:"" +export template class s1; // expected-error {{an explicit instantiation cannot be marked 'export'}} +// expected-note@-1 {{it is exported if the primary template is exported}} +// CHECK: fix-it:"{{.*}}":{[[@LINE-2]]:1-[[@LINE-2]]:8}:"" +export extern template class s1; // expected-error {{an explicit instantiation cannot be marked 'export'}} +// expected-note@-1 {{it is exported if the primary template is exported}} +// CHECK: fix-it:"{{.*}}":{[[@LINE-2]]:1-[[@LINE-2]]:8}:"" + +export template int v1 = 0; +export template int v1 = 0; // expected-error {{a specialization cannot be marked 'export'}} +// expected-note@-1 {{it is exported if the primary template is exported}} +// CHECK: fix-it:"{{.*}}":{[[@LINE-2]]:1-[[@LINE-2]]:8}:"" +export template <> int v1 = 0; // expected-error {{a specialization cannot be marked 'export'}} +// expected-note@-1 {{it is exported if the primary template is exported}} +// CHECK: fix-it:"{{.*}}":{[[@LINE-2]]:1-[[@LINE-2]]:8}:"" +export template int v1; // expected-error {{an explicit instantiation cannot be marked 'export'}} +// expected-note@-1 {{it is exported if the primary template is exported}} +// CHECK: fix-it:"{{.*}}":{[[@LINE-2]]:1-[[@LINE-2]]:8}:"" +export extern template int v1; // expected-error {{an explicit instantiation cannot be marked 'export'}} +// expected-note@-1 {{it is exported if the primary template is exported}} +// CHECK: fix-it:"{{.*}}":{[[@LINE-2]]:1-[[@LINE-2]]:8}:"" + +export template void f1() {} +export template <> void f1() {} // expected-error {{a specialization cannot be marked 'export'}} +// expected-note@-1 {{it is exported if the primary template is exported}} +// CHECK: fix-it:"{{.*}}":{[[@LINE-2]]:1-[[@LINE-2]]:8}:"" +export template void f1(); // expected-error {{an explicit instantiation cannot be marked 'export'}} +// expected-note@-1 {{it is exported if the primary template is exported}} +// CHECK: fix-it:"{{.*}}":{[[@LINE-2]]:1-[[@LINE-2]]:8}:"" +export extern template void f1(); // expected-error {{an explicit instantiation cannot be marked 'export'}} +// expected-note@-1 {{it is exported if the primary template is exported}} +// CHECK: fix-it:"{{.*}}":{[[@LINE-2]]:1-[[@LINE-2]]:8}:"" + + +export { template class s2 {}; } +export { template class s2 {}; } +export { template <> class s2 {}; } +export { template class s2; } +export { extern template class s2; } + +export { template int v2 = 0; } +export { template int v2 = 0; } +export { template <> int v2 = 0; } +export { template int v2; } +export { extern template int v2; } + +export { template void f2() {} } +export { template <> void f2() {} } +export { template void f2(); } +export { extern template void f2(); } + +} // namespace cwg2443 diff --git a/clang/test/CXX/drs/cwg24xx.cpp b/clang/test/CXX/drs/cwg24xx.cpp index 0a6a05c1254515..7eb22f596e13fe 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 2a1e79471ceea6..f844487e977e37 100755 --- a/clang/www/cxx_dr_status.html +++ b/clang/www/cxx_dr_status.html @@ -16894,7 +16894,7 @@

C++ defect report implementation status

[module.interface] C++23 Meaningless template exports - Unknown + Clang 23 2444 From 95c6d0058fca79d1a72dfad9e940d00d3333354c Mon Sep 17 00:00:00 2001 From: Victor Chernyakin Date: Mon, 27 Apr 2026 08:14:44 -0700 Subject: [PATCH 07/13] 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 c883b1ff1358ee..0506caf1c14d6c 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 class s1 {}; -export template class s1 {}; // expected-error {{a specialization cannot be marked 'export'}} -// expected-note@-1 {{it is exported if the primary template is exported}} -// CHECK: fix-it:"{{.*}}":{[[@LINE-2]]:1-[[@LINE-2]]:8}:"" -export template <> class s1 {}; // expected-error {{a specialization cannot be marked 'export'}} -// expected-note@-1 {{it is exported if the primary template is exported}} -// CHECK: fix-it:"{{.*}}":{[[@LINE-2]]:1-[[@LINE-2]]:8}:"" -export template class s1; // expected-error {{an explicit instantiation cannot be marked 'export'}} -// expected-note@-1 {{it is exported if the primary template is exported}} -// CHECK: fix-it:"{{.*}}":{[[@LINE-2]]:1-[[@LINE-2]]:8}:"" -export extern template class s1; // expected-error {{an explicit instantiation cannot be marked 'export'}} -// expected-note@-1 {{it is exported if the primary template is exported}} -// CHECK: fix-it:"{{.*}}":{[[@LINE-2]]:1-[[@LINE-2]]:8}:"" +export template class s1 {}; +// expected-error@-1 {{a specialization cannot be marked 'export'}} +// expected-note@-2 {{it is exported if the primary template is exported}} +export template <> class s1 {}; +// expected-error@-1 {{a specialization cannot be marked 'export'}} +// expected-note@-2 {{it is exported if the primary template is exported}} +export template class s1; +// expected-error@-1 {{an explicit instantiation cannot be marked 'export'}} +// expected-note@-2 {{it is exported if the primary template is exported}} +export extern template class s1; +// expected-error@-1 {{an explicit instantiation cannot be marked 'export'}} +// expected-note@-2 {{it is exported if the primary template is exported}} export template int v1 = 0; -export template int v1 = 0; // expected-error {{a specialization cannot be marked 'export'}} -// expected-note@-1 {{it is exported if the primary template is exported}} -// CHECK: fix-it:"{{.*}}":{[[@LINE-2]]:1-[[@LINE-2]]:8}:"" -export template <> int v1 = 0; // expected-error {{a specialization cannot be marked 'export'}} -// expected-note@-1 {{it is exported if the primary template is exported}} -// CHECK: fix-it:"{{.*}}":{[[@LINE-2]]:1-[[@LINE-2]]:8}:"" -export template int v1; // expected-error {{an explicit instantiation cannot be marked 'export'}} -// expected-note@-1 {{it is exported if the primary template is exported}} -// CHECK: fix-it:"{{.*}}":{[[@LINE-2]]:1-[[@LINE-2]]:8}:"" -export extern template int v1; // expected-error {{an explicit instantiation cannot be marked 'export'}} -// expected-note@-1 {{it is exported if the primary template is exported}} -// CHECK: fix-it:"{{.*}}":{[[@LINE-2]]:1-[[@LINE-2]]:8}:"" +export template int v1 = 0; +// expected-error@-1 {{a specialization cannot be marked 'export'}} +// expected-note@-2 {{it is exported if the primary template is exported}} +export template <> int v1 = 0; +// expected-error@-1 {{a specialization cannot be marked 'export'}} +// expected-note@-2 {{it is exported if the primary template is exported}} +export template int v1; +// expected-error@-1 {{an explicit instantiation cannot be marked 'export'}} +// expected-note@-2 {{it is exported if the primary template is exported}} +export extern template int v1; +// expected-error@-1 {{an explicit instantiation cannot be marked 'export'}} +// expected-note@-2 {{it is exported if the primary template is exported}} export template void f1() {} -export template <> void f1() {} // expected-error {{a specialization cannot be marked 'export'}} -// expected-note@-1 {{it is exported if the primary template is exported}} -// CHECK: fix-it:"{{.*}}":{[[@LINE-2]]:1-[[@LINE-2]]:8}:"" -export template void f1(); // expected-error {{an explicit instantiation cannot be marked 'export'}} -// expected-note@-1 {{it is exported if the primary template is exported}} -// CHECK: fix-it:"{{.*}}":{[[@LINE-2]]:1-[[@LINE-2]]:8}:"" -export extern template void f1(); // expected-error {{an explicit instantiation cannot be marked 'export'}} -// expected-note@-1 {{it is exported if the primary template is exported}} -// CHECK: fix-it:"{{.*}}":{[[@LINE-2]]:1-[[@LINE-2]]:8}:"" +export template <> void f1() {} +// expected-error@-1 {{a specialization cannot be marked 'export'}} +// expected-note@-2 {{it is exported if the primary template is exported}} +export template void f1(); +// expected-error@-1 {{an explicit instantiation cannot be marked 'export'}} +// expected-note@-2 {{it is exported if the primary template is exported}} +export extern template void f1(); +// expected-error@-1 {{an explicit instantiation cannot be marked 'export'}} +// expected-note@-2 {{it is exported if the primary template is exported}} export { template class s2 {}; } From ac5f8265fede224cfe9cca682926ffbb0315060a Mon Sep 17 00:00:00 2001 From: Victor Chernyakin Date: Mon, 27 Apr 2026 22:40:49 -0700 Subject: [PATCH 08/13] 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 9fb41c87da732a..47e12f7ac584af 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 8830294ea1658d..acfa9e1eabef02 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 CXXStandardLibraryVersion; + void ComputeCXXStandardLibraryVersion(); + + bool NeedsCXXStandardLibraryWorkaroundBefore( + uint64_t FixedVersion, CXXStandardLibraryVersionInfo::Library Lib) { + ComputeCXXStandardLibraryVersion(); + return CXXStandardLibraryVersion && CXXStandardLibraryVersion->Lib == Lib && + CXXStandardLibraryVersion->Version < FixedVersion; + } + public: - std::optional 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 6567c8fa4d783b..75878c2ef0ee8f 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(D)) + return Record->getTemplateSpecializationKind(); + if (const auto *Function = dyn_cast(D)) + return Function->getTemplateSpecializationKind(); + if (const auto *Var = dyn_cast(D)) + return Var->getTemplateSpecializationKind(); + if (const auto *EID = dyn_cast(D)) + return EID->getTemplateSpecializationKind(); + + return TSK_Undeclared; +} diff --git a/clang/lib/Lex/PPExpressions.cpp b/clang/lib/Lex/PPExpressions.cpp index 887fd25ac318de..4e596965bb6060 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 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 Ver = getStdLibCxxVersion(); - if (!Ver) - return false; - return *Ver < FixedVersion; +void Preprocessor::ComputeCXXStandardLibraryVersion() { + if (CXXStandardLibraryVersion) + return; + + static constexpr std::pair + VersionMacros[] = { + {"__GLIBCXX__", CXXStandardLibraryVersionInfo::LibStdCXX}, + {"_MSVC_STL_UPDATE", CXXStandardLibraryVersionInfo::MsvcStl}, + }; + + for (const auto &[MacroName, Lib] : VersionMacros) + if (std::optional VersionInfo = + getCXXStandardLibraryVersion(*this, MacroName, Lib)) + CXXStandardLibraryVersion = VersionInfo; } diff --git a/clang/lib/Parse/ParseDeclCXX.cpp b/clang/lib/Parse/ParseDeclCXX.cpp index 92e76a11ed02a4..cd8be9d7af860d 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(LinkageOrExportDecl); - if (DC->decls_empty()) - return; - + assert(!DC->decls_empty()); const Decl *D = *DC->decls_begin(); // Nested export declarations are diagnosed elsewhere. if (isa(LinkageOrExportDecl) && isa(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(D) && isa(LinkageOrExportDecl)) return; - TemplateSpecializationKind TSK = [&] { - if (const auto *EID = dyn_cast(D)) - return EID->getTemplateSpecializationKind(); - if (const auto *CTSD = dyn_cast(D)) - return CTSD->getTemplateSpecializationKind(); - if (const auto *VTSD = dyn_cast(D)) - return VTSD->getTemplateSpecializationKind(); - if (const auto *FD = dyn_cast(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(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 7b7d43ef3234cb..64c725b5331fd2 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(D)) - return Record->getTemplateSpecializationKind(); - if (FunctionDecl *Function = dyn_cast(D)) - return Function->getTemplateSpecializationKind(); - if (VarDecl *Var = dyn_cast(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 Date: Mon, 27 Apr 2026 23:04:06 -0700 Subject: [PATCH 09/13] 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 cd8be9d7af860d..af9f7030285e57 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(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 Date: Tue, 28 Apr 2026 00:10:25 -0700 Subject: [PATCH 10/13] 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 0506caf1c14d6c..02af26d5c98b1e 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 class s1 {}; -export template class s1 {}; +export template class s1 {}; // expected-error@-1 {{a specialization cannot be marked 'export'}} // expected-note@-2 {{it is exported if the primary template is exported}} -export template <> class s1 {}; +export template <> class s1 {}; // expected-error@-1 {{a specialization cannot be marked 'export'}} // expected-note@-2 {{it is exported if the primary template is exported}} -export template class s1; +export template class s1; // expected-error@-1 {{an explicit instantiation cannot be marked 'export'}} // expected-note@-2 {{it is exported if the primary template is exported}} -export extern template class s1; +export extern template class s1; // expected-error@-1 {{an explicit instantiation cannot be marked 'export'}} // expected-note@-2 {{it is exported if the primary template is exported}} export template int v1 = 0; -export template int v1 = 0; +export template int v1 = 0; // expected-error@-1 {{a specialization cannot be marked 'export'}} // expected-note@-2 {{it is exported if the primary template is exported}} -export template <> int v1 = 0; +export template <> int v1 = 0; // expected-error@-1 {{a specialization cannot be marked 'export'}} // expected-note@-2 {{it is exported if the primary template is exported}} -export template int v1; +export template int v1; // expected-error@-1 {{an explicit instantiation cannot be marked 'export'}} // expected-note@-2 {{it is exported if the primary template is exported}} -export extern template int v1; +export extern template int v1; // expected-error@-1 {{an explicit instantiation cannot be marked 'export'}} // expected-note@-2 {{it is exported if the primary template is exported}} export template void f1() {} -export template <> void f1() {} +export template <> void f1() {} // expected-error@-1 {{a specialization cannot be marked 'export'}} // expected-note@-2 {{it is exported if the primary template is exported}} -export template void f1(); +export template void f1(); // expected-error@-1 {{an explicit instantiation cannot be marked 'export'}} // expected-note@-2 {{it is exported if the primary template is exported}} -export extern template void f1(); +export extern template void f1(); // expected-error@-1 {{an explicit instantiation cannot be marked 'export'}} // expected-note@-2 {{it is exported if the primary template is exported}} From 376bad9944134217d652706335cf250c20af4571 Mon Sep 17 00:00:00 2001 From: Victor Chernyakin Date: Thu, 30 Apr 2026 07:26:11 -0700 Subject: [PATCH 11/13] Change from error to warning --- .../clang/Basic/DiagnosticSemaKinds.td | 4 +- clang/lib/Parse/ParseDeclCXX.cpp | 6 +-- clang/test/CXX/drs/cwg2443.cpp | 22 ++++---- clang/test/SemaCXX/P2615.cpp | 50 +++++++++---------- 4 files changed, 41 insertions(+), 41 deletions(-) diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index e981eddcd6bd0f..327d3e4977293a 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -12854,12 +12854,12 @@ 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< +def warn_meaningless_export : Warning< "%select{an explicit instantiation|a specialization}0 cannot be " "marked 'export'">; def note_meaningless_export_explanation : Note< "it is exported if the primary template is exported">; -def err_invalid_decl_in_linkage_spec : Error< +def warn_invalid_decl_in_linkage_spec : Warning< "language linkage cannot be specified for " "%select{an explicit instantiation|a specialization|an export declaration}0">; diff --git a/clang/lib/Parse/ParseDeclCXX.cpp b/clang/lib/Parse/ParseDeclCXX.cpp index af9f7030285e57..73900968844ff9 100644 --- a/clang/lib/Parse/ParseDeclCXX.cpp +++ b/clang/lib/Parse/ParseDeclCXX.cpp @@ -454,7 +454,7 @@ void Parser::CheckUnbracedLinkageOrExportDeclaration( // Nested export declarations are diagnosed elsewhere. if (isa(LinkageOrExportDecl) && isa(D)) { Diag(LinkageOrExportDecl->getLocation(), - diag::err_invalid_decl_in_linkage_spec) + diag::warn_invalid_decl_in_linkage_spec) << /*export declaration*/ 2; return; } @@ -475,7 +475,7 @@ void Parser::CheckUnbracedLinkageOrExportDeclaration( return; if (const auto *ED = dyn_cast(LinkageOrExportDecl)) { - Diag(ED->getExportLoc(), diag::err_meaningless_export) + Diag(ED->getExportLoc(), diag::warn_meaningless_export) << (TSK == TSK_ExplicitSpecialization) << FixItHint::CreateRemoval(ED->getExportLoc()); Diag(ED->getExportLoc(), diag::note_meaningless_export_explanation); @@ -491,7 +491,7 @@ void Parser::CheckUnbracedLinkageOrExportDeclaration( getPreprocessor().NeedsMsvcStlWorkaroundBefore(2026'03)) return; - Diag(LS->getLocation(), diag::err_invalid_decl_in_linkage_spec) + Diag(LS->getLocation(), diag::warn_invalid_decl_in_linkage_spec) << (TSK == TSK_ExplicitSpecialization); return; } diff --git a/clang/test/CXX/drs/cwg2443.cpp b/clang/test/CXX/drs/cwg2443.cpp index 02af26d5c98b1e..6e87ef54cd67c6 100644 --- a/clang/test/CXX/drs/cwg2443.cpp +++ b/clang/test/CXX/drs/cwg2443.cpp @@ -8,41 +8,41 @@ namespace cwg2443 { // cwg2443: 23 export template class s1 {}; export template class s1 {}; -// expected-error@-1 {{a specialization cannot be marked 'export'}} +// expected-warning@-1 {{a specialization cannot be marked 'export'}} // expected-note@-2 {{it is exported if the primary template is exported}} export template <> class s1 {}; -// expected-error@-1 {{a specialization cannot be marked 'export'}} +// expected-warning@-1 {{a specialization cannot be marked 'export'}} // expected-note@-2 {{it is exported if the primary template is exported}} export template class s1; -// expected-error@-1 {{an explicit instantiation cannot be marked 'export'}} +// expected-warning@-1 {{an explicit instantiation cannot be marked 'export'}} // expected-note@-2 {{it is exported if the primary template is exported}} export extern template class s1; -// expected-error@-1 {{an explicit instantiation cannot be marked 'export'}} +// expected-warning@-1 {{an explicit instantiation cannot be marked 'export'}} // expected-note@-2 {{it is exported if the primary template is exported}} export template int v1 = 0; export template int v1 = 0; -// expected-error@-1 {{a specialization cannot be marked 'export'}} +// expected-warning@-1 {{a specialization cannot be marked 'export'}} // expected-note@-2 {{it is exported if the primary template is exported}} export template <> int v1 = 0; -// expected-error@-1 {{a specialization cannot be marked 'export'}} +// expected-warning@-1 {{a specialization cannot be marked 'export'}} // expected-note@-2 {{it is exported if the primary template is exported}} export template int v1; -// expected-error@-1 {{an explicit instantiation cannot be marked 'export'}} +// expected-warning@-1 {{an explicit instantiation cannot be marked 'export'}} // expected-note@-2 {{it is exported if the primary template is exported}} export extern template int v1; -// expected-error@-1 {{an explicit instantiation cannot be marked 'export'}} +// expected-warning@-1 {{an explicit instantiation cannot be marked 'export'}} // expected-note@-2 {{it is exported if the primary template is exported}} export template void f1() {} export template <> void f1() {} -// expected-error@-1 {{a specialization cannot be marked 'export'}} +// expected-warning@-1 {{a specialization cannot be marked 'export'}} // expected-note@-2 {{it is exported if the primary template is exported}} export template void f1(); -// expected-error@-1 {{an explicit instantiation cannot be marked 'export'}} +// expected-warning@-1 {{an explicit instantiation cannot be marked 'export'}} // expected-note@-2 {{it is exported if the primary template is exported}} export extern template void f1(); -// expected-error@-1 {{an explicit instantiation cannot be marked 'export'}} +// expected-warning@-1 {{an explicit instantiation cannot be marked 'export'}} // expected-note@-2 {{it is exported if the primary template is exported}} diff --git a/clang/test/SemaCXX/P2615.cpp b/clang/test/SemaCXX/P2615.cpp index c0b6abd9731bd1..0578675f206856 100644 --- a/clang/test/SemaCXX/P2615.cpp +++ b/clang/test/SemaCXX/P2615.cpp @@ -4,7 +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 +// RUN: %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 @@ -18,41 +18,41 @@ export using namespace N; export module B; export template class s1 {}; -export template class s1 {}; // expected-error {{a specialization cannot be marked 'export'}} +export template class s1 {}; // expected-warning {{a specialization cannot be marked 'export'}} // expected-note@-1 {{it is exported if the primary template is exported}} // CHECK: fix-it:"{{.*}}":{[[@LINE-2]]:1-[[@LINE-2]]:8}:"" -export template <> class s1 {}; // expected-error {{a specialization cannot be marked 'export'}} +export template <> class s1 {}; // expected-warning {{a specialization cannot be marked 'export'}} // expected-note@-1 {{it is exported if the primary template is exported}} // CHECK: fix-it:"{{.*}}":{[[@LINE-2]]:1-[[@LINE-2]]:8}:"" -export template class s1; // expected-error {{an explicit instantiation cannot be marked 'export'}} +export template class s1; // expected-warning {{an explicit instantiation cannot be marked 'export'}} // expected-note@-1 {{it is exported if the primary template is exported}} // CHECK: fix-it:"{{.*}}":{[[@LINE-2]]:1-[[@LINE-2]]:8}:"" -export extern template class s1; // expected-error {{an explicit instantiation cannot be marked 'export'}} +export extern template class s1; // expected-warning {{an explicit instantiation cannot be marked 'export'}} // expected-note@-1 {{it is exported if the primary template is exported}} // CHECK: fix-it:"{{.*}}":{[[@LINE-2]]:1-[[@LINE-2]]:8}:"" export template int v1 = 0; -export template int v1 = 0; // expected-error {{a specialization cannot be marked 'export'}} +export template int v1 = 0; // expected-warning {{a specialization cannot be marked 'export'}} // expected-note@-1 {{it is exported if the primary template is exported}} // CHECK: fix-it:"{{.*}}":{[[@LINE-2]]:1-[[@LINE-2]]:8}:"" -export template <> int v1 = 0; // expected-error {{a specialization cannot be marked 'export'}} +export template <> int v1 = 0; // expected-warning {{a specialization cannot be marked 'export'}} // expected-note@-1 {{it is exported if the primary template is exported}} // CHECK: fix-it:"{{.*}}":{[[@LINE-2]]:1-[[@LINE-2]]:8}:"" -export template int v1; // expected-error {{an explicit instantiation cannot be marked 'export'}} +export template int v1; // expected-warning {{an explicit instantiation cannot be marked 'export'}} // expected-note@-1 {{it is exported if the primary template is exported}} // CHECK: fix-it:"{{.*}}":{[[@LINE-2]]:1-[[@LINE-2]]:8}:"" -export extern template int v1; // expected-error {{an explicit instantiation cannot be marked 'export'}} +export extern template int v1; // expected-warning {{an explicit instantiation cannot be marked 'export'}} // expected-note@-1 {{it is exported if the primary template is exported}} // CHECK: fix-it:"{{.*}}":{[[@LINE-2]]:1-[[@LINE-2]]:8}:"" export template void f1() {} -export template <> void f1() {} // expected-error {{a specialization cannot be marked 'export'}} +export template <> void f1() {} // expected-warning {{a specialization cannot be marked 'export'}} // expected-note@-1 {{it is exported if the primary template is exported}} // CHECK: fix-it:"{{.*}}":{[[@LINE-2]]:1-[[@LINE-2]]:8}:"" -export template void f1(); // expected-error {{an explicit instantiation cannot be marked 'export'}} +export template void f1(); // expected-warning {{an explicit instantiation cannot be marked 'export'}} // expected-note@-1 {{it is exported if the primary template is exported}} // CHECK: fix-it:"{{.*}}":{[[@LINE-2]]:1-[[@LINE-2]]:8}:"" -export extern template void f1(); // expected-error {{an explicit instantiation cannot be marked 'export'}} +export extern template void f1(); // expected-warning {{an explicit instantiation cannot be marked 'export'}} // expected-note@-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(); } extern "C++" template class s3 {}; extern "C++" template class s3 {}; -extern "C++" template <> class s3 {}; // expected-error {{language linkage cannot be specified for a specialization}} -extern "C++" template class s3; // expected-error {{language linkage cannot be specified for an explicit instantiation}} -extern "C++" extern template class s3; // expected-error {{language linkage cannot be specified for an explicit instantiation}} +extern "C++" template <> class s3 {}; // expected-warning {{language linkage cannot be specified for a specialization}} +extern "C++" template class s3; // expected-warning {{language linkage cannot be specified for an explicit instantiation}} +extern "C++" extern template class s3; // expected-warning {{language linkage cannot be specified for an explicit instantiation}} extern "C++" template int v3 = 0; extern "C++" template int v3 = 0; -extern "C++" template <> int v3 = 0; // expected-error {{language linkage cannot be specified for a specialization}} -extern "C++" template int v3; // expected-error {{language linkage cannot be specified for an explicit instantiation}} -extern "C++" extern template int v3; // expected-error {{language linkage cannot be specified for an explicit instantiation}} +extern "C++" template <> int v3 = 0; // expected-warning {{language linkage cannot be specified for a specialization}} +extern "C++" template int v3; // expected-warning {{language linkage cannot be specified for an explicit instantiation}} +extern "C++" extern template int v3; // expected-warning {{language linkage cannot be specified for an explicit instantiation}} extern "C++" template void f3() {} -extern "C++" template <> void f3() {} // expected-error {{language linkage cannot be specified for a specialization}} -extern "C++" template void f3(); // expected-error {{language linkage cannot be specified for an explicit instantiation}} -extern "C++" extern template void f3(); // expected-error {{language linkage cannot be specified for an explicit instantiation}} +extern "C++" template <> void f3() {} // expected-warning {{language linkage cannot be specified for a specialization}} +extern "C++" template void f3(); // expected-warning {{language linkage cannot be specified for an explicit instantiation}} +extern "C++" extern template void f3(); // expected-warning {{language linkage cannot be specified for an explicit instantiation}} -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++" export int i; // expected-warning {{language linkage cannot be specified for an export declaration}} +extern "C++" export {} // expected-warning {{language linkage cannot be specified for an export declaration}} extern "C++" { template class s4 {}; } @@ -124,7 +124,7 @@ extern "C++" template<> struct s {}; } // namespace std extern "C++" template struct c {}; -extern "C++" template<> struct c {}; // expected-error {{language linkage cannot be specified for a specialization}} +extern "C++" template<> struct c {}; // expected-warning {{language linkage cannot be specified for a specialization}} //--- msvc-stl-exception-2.cpp #define _MSVC_STL_UPDATE 202603L @@ -132,6 +132,6 @@ extern "C++" template<> struct c {}; // expected-error {{language linkage c namespace std { extern "C++" template struct s {}; -extern "C++" template<> struct s {}; // expected-error {{language linkage cannot be specified for a specialization}} +extern "C++" template<> struct s {}; // expected-warning {{language linkage cannot be specified for a specialization}} } // namespace std From 8464a81f233e1283c0c87c578ec2c59a42055569 Mon Sep 17 00:00:00 2001 From: Victor Chernyakin Date: Thu, 30 Apr 2026 07:44:10 -0700 Subject: [PATCH 12/13] Add check for `-fms-extensions` --- clang/lib/Parse/ParseDeclCXX.cpp | 2 +- clang/test/SemaCXX/P2615.cpp | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/clang/lib/Parse/ParseDeclCXX.cpp b/clang/lib/Parse/ParseDeclCXX.cpp index 73900968844ff9..24a6069d0c5efd 100644 --- a/clang/lib/Parse/ParseDeclCXX.cpp +++ b/clang/lib/Parse/ParseDeclCXX.cpp @@ -487,7 +487,7 @@ void Parser::CheckUnbracedLinkageOrExportDeclaration( // 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. - if (LinkageOrExportDecl->isInStdNamespace() && + if (getLangOpts().MicrosoftExt && LinkageOrExportDecl->isInStdNamespace() && getPreprocessor().NeedsMsvcStlWorkaroundBefore(2026'03)) return; diff --git a/clang/test/SemaCXX/P2615.cpp b/clang/test/SemaCXX/P2615.cpp index 0578675f206856..ffdc59bb31e912 100644 --- a/clang/test/SemaCXX/P2615.cpp +++ b/clang/test/SemaCXX/P2615.cpp @@ -5,8 +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: %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 +// RUN: %clang_cc1 -std=c++20 -verify -fsyntax-only -fms-extensions %t/msvc-stl-exception-1.cpp +// RUN: %clang_cc1 -std=c++20 -verify -fsyntax-only -fms-extensions %t/msvc-stl-exception-2.cpp //--- A.cpp // expected-no-diagnostics From b94cd84c99482f8e00008e680298cb4865dcb230 Mon Sep 17 00:00:00 2001 From: Victor Chernyakin Date: Thu, 30 Apr 2026 08:11:09 -0700 Subject: [PATCH 13/13] Add warning groups --- clang/include/clang/Basic/DiagnosticSemaKinds.td | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 327d3e4977293a..0f1363186142a9 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -12856,12 +12856,14 @@ def err_extern_def_in_header_unit : Error< def warn_meaningless_export : Warning< "%select{an explicit instantiation|a specialization}0 cannot be " - "marked 'export'">; + "marked 'export'">, + InGroup>; def note_meaningless_export_explanation : Note< "it is exported if the primary template is exported">; def warn_invalid_decl_in_linkage_spec : Warning< "language linkage cannot be specified for " - "%select{an explicit instantiation|a specialization|an export declaration}0">; + "%select{an explicit instantiation|a specialization|an export declaration}0">, + InGroup>; def warn_exposure : Warning < "TU local entity %0 is exposed">,