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/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/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 7ed4684c8359e..0f1363186142a 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -12854,6 +12854,17 @@ 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 warn_meaningless_export : Warning< + "%select{an explicit instantiation|a specialization}0 cannot be " + "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">, + InGroup>; + def warn_exposure : Warning < "TU local entity %0 is exposed">, InGroup>; 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 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/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/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(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 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 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 d1e2e2c2c6ce1..24a6069d0c5ef 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,62 @@ 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::warn_invalid_decl_in_linkage_spec) + << /*export declaration*/ 2; + return; + } + + // [module.interface]/1 says: + // + // The name-declaration of an export-declaration shall not declare a + // partial specialization. + // + // But there's no equivalent wording for linkage-specification. + if (isa(D) && + isa(LinkageOrExportDecl)) + return; + + TemplateSpecializationKind TSK = getTemplateSpecializationKind(D); + if (TSK == TSK_Undeclared) + return; + + if (const auto *ED = dyn_cast(LinkageOrExportDecl)) { + Diag(ED->getExportLoc(), diag::warn_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)) { + // 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. + if (getLangOpts().MicrosoftExt && LinkageOrExportDecl->isInStdNamespace() && + getPreprocessor().NeedsMsvcStlWorkaroundBefore(2026'03)) + return; + + Diag(LS->getLocation(), diag::warn_invalid_decl_in_linkage_spec) + << (TSK == TSK_ExplicitSpecialization); + return; + } + + llvm_unreachable("Expected either an ExportDecl or a LinkageSpecDecl"); +} + Parser::DeclGroupPtrTy Parser::ParseUsingDirectiveOrDeclaration( DeclaratorContext Context, const ParsedTemplateInfo &TemplateInfo, SourceLocation &DeclEnd, ParsedAttributes &Attrs) { 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(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. /// diff --git a/clang/test/CXX/drs/cwg2443.cpp b/clang/test/CXX/drs/cwg2443.cpp new file mode 100644 index 0000000000000..6e87ef54cd67c --- /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 -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-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-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-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-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-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-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-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-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-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-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-warning@-1 {{an explicit instantiation cannot be marked 'export'}} +// expected-note@-2 {{it is exported if the primary template is exported}} + + +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 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/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 { 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 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 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 ea5fdd5c4ce26..8e2685e328133 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 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; +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 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 { 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 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 void foo4() { } -export template <> +template <> void foo4() { } diff --git a/clang/test/SemaCXX/P2615.cpp b/clang/test/SemaCXX/P2615.cpp index fed177c21bc79..ffdc59bb31e91 100644 --- a/clang/test/SemaCXX/P2615.cpp +++ b/clang/test/SemaCXX/P2615.cpp @@ -3,9 +3,135 @@ // 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 -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 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-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-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-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-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-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-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-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-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-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-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-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 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 {}; +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-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-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-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 {}; } +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(); } + +//--- msvc-stl-exception-1.cpp +#define _MSVC_STL_UPDATE 202602L + +namespace std { + +extern "C++" template struct s {}; +extern "C++" template<> struct s {}; + +} // namespace std + +extern "C++" template struct c {}; +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 + +namespace std { + +extern "C++" template struct s {}; +extern "C++" template<> struct s {}; // expected-warning {{language linkage cannot be specified for a specialization}} + +} // namespace std 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(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 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 { +template<> class A { A AS; A AB; }; 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 @@

C++ defect report implementation status

[
module.interface] C++23 Meaningless template exports - Unknown + Clang 23 2444 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 @@

C++20 implementation status

P2615R1 (DR) - Clang 17 + Clang 23 P2788R0 (DR)