diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -11272,6 +11272,10 @@ def err_export_using_internal : Error< "using declaration referring to %1 with %select{internal|module|unknown}0 " "linkage cannot be exported">; +def err_export_partial_specialization : Error< + "partial %select{class|variable}0 specialization %1 cannot be exported">; +def err_export_explicit_specialization : Error< + "explicit specialization %0 cannot be exported">; def err_export_not_in_module_interface : Error< "export declaration can only be used within a module purview">; def err_export_inline_not_defined : Error< diff --git a/clang/lib/Sema/SemaModule.cpp b/clang/lib/Sema/SemaModule.cpp --- a/clang/lib/Sema/SemaModule.cpp +++ b/clang/lib/Sema/SemaModule.cpp @@ -828,6 +828,39 @@ /// Check that it's valid to export \p D. static bool checkExportedDecl(Sema &S, Decl *D, SourceLocation BlockStart) { + if (isa(D) || + isa(D)) { + // C++20 [module.interface]p1: + // [...] shall not declare a partial specialization. + int Kind = isa(D) ? 0 : 1; + auto *ND = dyn_cast(D); + S.Diag(ND->getLocation(), diag::err_export_partial_specialization) + << Kind << ND; + if (BlockStart.isValid()) + S.Diag(BlockStart, diag::note_export); + } else if (auto *ND = dyn_cast(D)) { + // C++20 [temp.expl.spec]p2 + // The declaration in an explicit-specialization shall not be an + // export-declaration. + bool BadExport = isa(ND) || + isa(ND); + if (auto *FD = dyn_cast(D)) { + if (FD->getTemplateSpecializationKind() == TSK_ExplicitSpecialization) + BadExport = true; + } else if (auto *VD = dyn_cast(D)) { + if (VD->getTemplateSpecializationKind() == TSK_ExplicitSpecialization) + BadExport = true; + } else if (auto *ED = dyn_cast(D)) { + if (ED->getTemplateSpecializationKind() == TSK_ExplicitSpecialization) + BadExport = true; + } + if (BadExport) { + S.Diag(ND->getLocation(), diag::err_export_explicit_specialization) << ND; + if (BlockStart.isValid()) + S.Diag(BlockStart, diag::note_export); + } + } + // C++20 [module.interface]p3: // [...] it shall not declare a name with internal linkage. bool HasName = false; @@ -843,7 +876,7 @@ } } - // C++2a [module.interface]p5: + // C++20 [module.interface]p5: // all entities to which all of the using-declarators ultimately refer // shall have been introduced with a name having external linkage if (auto *USD = dyn_cast(D)) { diff --git a/clang/test/CXX/module/module.interface/p1-p2615r1.cpp b/clang/test/CXX/module/module.interface/p1-p2615r1.cpp new file mode 100644 --- /dev/null +++ b/clang/test/CXX/module/module.interface/p1-p2615r1.cpp @@ -0,0 +1,22 @@ +// RUN: %clang_cc1 -std=c++20 %s -emit-module-interface -o p2615r1.pcm \ +// RUN: -verify -pedantic-errors + +export module P2615R1; + +export template +class A { }; + +export template +class A { }; // expected-error {{partial class specialization 'A' cannot be exported}} + +export template +struct B { }; + +export template +struct B { }; // expected-error {{partial class specialization 'B' cannot be exported}} + +export template +T1 C; + +export template +T C; // expected-error {{partial variable specialization 'C' cannot be exported}} diff --git a/clang/test/CXX/temp/temp.explicit/p2-p2615r1.cpp b/clang/test/CXX/temp/temp.explicit/p2-p2615r1.cpp new file mode 100644 --- /dev/null +++ b/clang/test/CXX/temp/temp.explicit/p2-p2615r1.cpp @@ -0,0 +1,20 @@ +// RUN: %clang_cc1 -std=c++20 %s -fsyntax-only -verify -pedantic-errors + +export module P2615R1; + +export template void fA (T) { }; + +export template<> +void fA (int) { }; // expected-error {{explicit specialization 'fA' cannot be exported}} + +export template +class B { }; + +export template<> +class B { }; // expected-error {{explicit specialization 'B' cannot be exported}} + +export template T C; + +export template<> +int C; // expected-error {{explicit specialization 'C' cannot be exported}} + diff --git a/clang/test/Modules/merge-var-template-spec-cxx-modules.cppm b/clang/test/Modules/merge-var-template-spec-cxx-modules.cppm deleted file mode 100644 --- a/clang/test/Modules/merge-var-template-spec-cxx-modules.cppm +++ /dev/null @@ -1,44 +0,0 @@ -// RUN: rm -rf %t -// RUN: mkdir %t -// RUN: split-file %s %t -// -// RUN: %clang_cc1 -std=c++20 -emit-module-interface %t/var_def.cppm -o %t/var_def.pcm -// RUN: %clang_cc1 -std=c++20 -emit-module-interface -fprebuilt-module-path=%t %t/reexport1.cppm -o %t/reexport1.pcm -// RUN: %clang_cc1 -std=c++20 -emit-module-interface -fprebuilt-module-path=%t %t/reexport2.cppm -o %t/reexport2.pcm -// RUN: %clang_cc1 -std=c++20 -fprebuilt-module-path=%t %t/use.cppm -fsyntax-only -verify - -//--- use.cppm -import reexport1; -import reexport2; - -auto foo = zero; -auto bar = zero; -auto baz = zero; - -template constexpr T zero = 0; // expected-error-re {{declaration{{.*}}in the global module follows declaration in module var_def}} - // expected-note@* {{previous}} -template <> constexpr Int zero = {0}; // expected-error-re {{declaration{{.*}}in the global module follows declaration in module var_def}} - // expected-note@* {{previous}} -template constexpr T* zero = nullptr; // expected-error-re {{declaration{{.*}}in the global module follows declaration in module var_def}} - // expected-note@* {{previous}} - -template <> constexpr int** zero = nullptr; // ok, new specialization. -template constexpr T** zero = nullptr; // ok, new partial specilization. - -//--- var_def.cppm -export module var_def; - -export template constexpr T zero = 0; -export struct Int { - int value; -}; -export template <> constexpr Int zero = {0}; -export template constexpr T* zero = nullptr; - -//--- reexport1.cppm -export module reexport1; -export import var_def; - -//--- reexport2.cppm -export module reexport2; -export import var_def; diff --git a/clang/test/Modules/pr59780.cppm b/clang/test/Modules/pr59780.cppm deleted file mode 100644 --- a/clang/test/Modules/pr59780.cppm +++ /dev/null @@ -1,46 +0,0 @@ -// https://github.com/llvm/llvm-project/issues/59780 -// -// RUN: rm -rf %t -// RUN: mkdir %t -// RUN: split-file %s %t -// -// RUN: %clang_cc1 -std=c++20 %t/a.cppm -triple %itanium_abi_triple -emit-module-interface -o %t/a.pcm -// RUN: %clang_cc1 -std=c++20 %t/use.cpp -fprebuilt-module-path=%t -S \ -// RUN: -triple %itanium_abi_triple -emit-llvm -o - | FileCheck %t/use.cpp -// RUN: %clang_cc1 -std=c++20 %t/a.pcm -triple %itanium_abi_triple -emit-llvm -o - | FileCheck %t/a.cppm - -//--- a.cppm -export module a; - -export template -int x = 0; - -export template<> -int x = 0; - -export template -struct Y { - static int value; -}; - -template -int Y::value = 0; - -export template<> -struct Y { - static int value; -}; - -int Y::value = 0; - -// CHECK-NOT: @_ZW1a1xIiE = {{.*}}external{{.*}}global -// CHECK-NOT: @_ZNW1a1YIiE5valueE = {{.*}}external{{.*}}global - -//--- use.cpp -import a; -int foo() { - return x + Y::value; -} - -// CHECK: @_ZW1a1xIiE = {{.*}}external{{.*}}global -// CHECK: @_ZNW1a1YIiE5valueE = {{.*}}external{{.*}}global diff --git a/clang/test/Modules/pr60890.cppm b/clang/test/Modules/pr60890.cppm --- a/clang/test/Modules/pr60890.cppm +++ b/clang/test/Modules/pr60890.cppm @@ -18,8 +18,6 @@ void aaa() requires(true) {} }; -export template struct a; - export template void foo(T) requires(true) {} diff --git a/clang/test/Modules/pr62796.cppm b/clang/test/Modules/pr62796.cppm --- a/clang/test/Modules/pr62796.cppm +++ b/clang/test/Modules/pr62796.cppm @@ -38,8 +38,6 @@ template constexpr unsigned long Cache = Compute(Number{}, Strategy{}); - - template constexpr unsigned long Cache<10ul>; } //--- Use.cpp diff --git a/clang/test/Modules/template-function-specialization.cpp b/clang/test/Modules/template-function-specialization.cpp --- a/clang/test/Modules/template-function-specialization.cpp +++ b/clang/test/Modules/template-function-specialization.cpp @@ -39,10 +39,6 @@ void foo4() { } -export template <> -void foo4() { -} - //--- Use.cpp import foo; void use() { diff --git a/clang/unittests/Serialization/VarDeclConstantInitTest.cpp b/clang/unittests/Serialization/VarDeclConstantInitTest.cpp --- a/clang/unittests/Serialization/VarDeclConstantInitTest.cpp +++ b/clang/unittests/Serialization/VarDeclConstantInitTest.cpp @@ -86,7 +86,6 @@ template constexpr unsigned long Cache = Compute(Number{}, Strategy{}); - template constexpr unsigned long Cache<10ul>; } )cpp"); @@ -116,6 +115,7 @@ std::unique_ptr AST = tooling::buildASTFromCodeWithArgs( R"cpp( import Fibonacci.Cache; +template constexpr unsigned long Fibonacci::Cache<10ul>; )cpp", /*Args=*/{"-std=c++20", DepArg.c_str()}); diff --git a/clang/www/cxx_status.html b/clang/www/cxx_status.html --- a/clang/www/cxx_status.html +++ b/clang/www/cxx_status.html @@ -772,7 +772,7 @@ P2615R1 (DR) - No + Clang 17 P2788R0 (DR)