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 @@ -7853,8 +7853,8 @@ def err_export_non_namespace_scope_name : Error< "cannot export %0 as it is not at namespace scope">; def err_redeclaration_non_exported : Error < - "cannot export redeclaration %0 here since the previous declaration is not " - "exported">; + "cannot export redeclaration %0 here since the previous declaration " + "%select{is not exported|has internal linkage|has module linkage}1">; def err_invalid_declarator_global_scope : Error< "definition or redeclaration of %0 cannot name the global scope">; def err_invalid_declarator_in_function : Error< @@ -11071,6 +11071,8 @@ def ext_export_no_names : ExtWarn< "ISO C++20 does not permit a declaration that does not introduce any names " "to be exported">, InGroup; +def introduces_no_names : Error< + "declaration does not introduce any names to be exported">; def note_export : Note<"export block begins here">; def err_export_no_name : Error< "%select{empty|static_assert|asm}0 declaration cannot be exported">; @@ -11082,7 +11084,8 @@ def err_export_internal : Error< "declaration of %0 with internal linkage cannot be exported">; def err_export_using_internal : Error< - "using declaration referring to %0 with internal linkage cannot be exported">; + "using declaration referring to %1 with %select{internal|module|unknown}0 " + "linkage cannot be exported">; def err_export_not_in_module_interface : Error< "export declaration can only be used within a module interface unit" "%select{ after the module declaration|}0">; diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -1679,7 +1679,13 @@ assert(IsNewExported); - Diag(New->getLocation(), diag::err_redeclaration_non_exported) << New; + auto Lk = Old->getFormalLinkage(); + int S = 0; + if (Lk == Linkage::InternalLinkage) + S = 1; + else if (Lk == Linkage::ModuleLinkage) + S = 2; + Diag(New->getLocation(), diag::err_redeclaration_non_exported) << New << S; Diag(Old->getLocation(), diag::note_previous_declaration); return true; } 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 @@ -762,6 +762,7 @@ StaticAssert, Asm, UsingDirective, + Namespace, Context }; } @@ -791,6 +792,10 @@ // Allow exporting using-directives as an extension. return diag::ext_export_using_directive; + case UnnamedDeclKind::Namespace: + // Anonymous namespace with no content. + return diag::introduces_no_names; + case UnnamedDeclKind::Context: // Allow exporting DeclContexts that transitively contain no declarations // as an extension. @@ -818,10 +823,12 @@ diagExportedUnnamedDecl(S, *UDK, D, BlockStart); // [...] shall not declare a name with internal linkage. + bool HasName = false; if (auto *ND = dyn_cast(D)) { // Don't diagnose anonymous union objects; we'll diagnose their members // instead. - if (ND->getDeclName() && ND->getFormalLinkage() == InternalLinkage) { + HasName = (bool)ND->getDeclName(); + if (HasName && ND->getFormalLinkage() == InternalLinkage) { S.Diag(ND->getLocation(), diag::err_export_internal) << ND; if (BlockStart.isValid()) S.Diag(BlockStart, diag::note_export); @@ -833,8 +840,10 @@ // shall have been introduced with a name having external linkage if (auto *USD = dyn_cast(D)) { NamedDecl *Target = USD->getUnderlyingDecl(); - if (Target->getFormalLinkage() == InternalLinkage) { - S.Diag(USD->getLocation(), diag::err_export_using_internal) << Target; + Linkage Lk = Target->getFormalLinkage(); + if (Lk == InternalLinkage || Lk == ModuleLinkage) { + S.Diag(USD->getLocation(), diag::err_export_using_internal) + << (Lk == InternalLinkage ? 0 : 1) << Target; S.Diag(Target->getLocation(), diag::note_using_decl_target); if (BlockStart.isValid()) S.Diag(BlockStart, diag::note_export); @@ -842,10 +851,18 @@ } // Recurse into namespace-scope DeclContexts. (Only namespace-scope - // declarations are exported.) - if (auto *DC = dyn_cast(D)) - if (DC->getRedeclContext()->isFileContext() && !isa(D)) + // declarations are exported.). + if (auto *DC = dyn_cast(D)) { + if (isa(D) && DC->decls().empty()) { + if (!HasName) + // We don't allow an empty anonymous namespace (we don't allow decls + // in them either, but that's handled in the recursion). + diagExportedUnnamedDecl(S, UnnamedDeclKind::Namespace, D, BlockStart); + else + ; // We allow an empty named namespace decl. + } else if (DC->getRedeclContext()->isFileContext() && !isa(D)) return checkExportedDeclContext(S, DC, BlockStart); + } return false; } diff --git a/clang/test/CXX/module/module.interface/p3.cpp b/clang/test/CXX/module/module.interface/p3.cpp --- a/clang/test/CXX/module/module.interface/p3.cpp +++ b/clang/test/CXX/module/module.interface/p3.cpp @@ -2,17 +2,17 @@ export module p3; -namespace A { int ns_mem; } +namespace A { int ns_mem; } // expected-note 2{{target}} // An exported declaration shall declare at least one name. export; // expected-error {{empty declaration cannot be exported}} export static_assert(true); // expected-error {{static_assert declaration cannot be exported}} -export using namespace A; // expected-error {{ISO C++20 does not permit using directive to be exported}} +export using namespace A; // expected-error {{ISO C++20 does not permit using directive to be exported}} export { // expected-note 3{{export block begins here}} ; // expected-error {{ISO C++20 does not permit an empty declaration to appear in an export block}} static_assert(true); // expected-error {{ISO C++20 does not permit a static_assert declaration to appear in an export block}} - using namespace A; // expected-error {{ISO C++20 does not permit using directive to be exported}} + using namespace A; // expected-error {{ISO C++20 does not permit using directive to be exported}} } export struct {}; // expected-error {{must be class member}} expected-error {{GNU extension}} expected-error {{does not declare anything}} @@ -26,9 +26,9 @@ export static union {}; // expected-error {{does not declare anything}} export asm(""); // expected-error {{asm declaration cannot be exported}} export namespace B = A; -export using A::ns_mem; +export using A::ns_mem; // expected-error {{using declaration referring to 'ns_mem' with module linkage cannot be exported}} namespace A { - export using A::ns_mem; + export using A::ns_mem; // expected-error {{using declaration referring to 'ns_mem' with module linkage cannot be exported}} } export using Int = int; export extern "C++" {} // expected-error {{ISO C++20 does not permit a declaration that does not introduce any names to be exported}} diff --git a/clang/test/CXX/module/module.interface/p5.cpp b/clang/test/CXX/module/module.interface/p5.cpp --- a/clang/test/CXX/module/module.interface/p5.cpp +++ b/clang/test/CXX/module/module.interface/p5.cpp @@ -2,24 +2,24 @@ export module p5; -int a; +int a; // expected-note {{target}} static int sa; // expected-note {{target}} -void b(); +void b(); // expected-note {{target}} static void sb(); // expected-note {{target}} -struct c {}; -enum d {}; +struct c {}; // expected-note {{target}} +enum d {}; // expected-note {{target}} using e = int; using f = c; static union { int sg1, sg2; }; // expected-note {{target}} namespace NS {} -template int ta; +template int ta; // expected-note {{target}} template static int sta; // expected-note {{target}} -template void tb(); +template void tb(); // expected-note {{target}} template static void stb(); // expected-note {{target}} -template struct tc {}; -template using te = int; -template using tf = c; +template struct tc {}; // expected-note {{target}} +template using te = int; // expected-note {{target}} +template using tf = c; // expected-note {{target}} namespace UnnamedNS { namespace { @@ -44,24 +44,24 @@ } } -export { // expected-note 19{{here}} - using ::a; +export { // expected-note 28{{here}} + using ::a; // expected-error {{using declaration referring to 'a' with module linkage cannot be exported}} using ::sa; // expected-error {{using declaration referring to 'sa' with internal linkage}} - using ::b; + using ::b; // expected-error {{using declaration referring to 'b' with module linkage cannot be exported}} using ::sb; // expected-error {{using declaration referring to 'sb' with internal linkage}} - using ::c; - using ::d; + using ::c; // expected-error {{using declaration referring to 'c' with module linkage cannot be exported}} + using ::d; // expected-error {{using declaration referring to 'd' with module linkage cannot be exported}} using ::e; using ::f; using ::sg1; // expected-error {{using declaration referring to 'sg1' with internal linkage}} - using ::ta; + using ::ta; // expected-error {{using declaration referring to 'ta' with module linkage cannot be exported}} using ::sta; // expected-error {{using declaration referring to 'sta' with internal linkage}} - using ::tb; + using ::tb; // expected-error {{using declaration referring to 'tb' with module linkage cannot be exported}} using ::stb; // expected-error {{using declaration referring to 'stb' with internal linkage}} - using ::tc; - using ::te; - using ::tf; + using ::tc; // expected-error {{using declaration referring to 'tc' with module linkage cannot be exported}} + using ::te; // expected-error {{using declaration referring to 'te' with module linkage cannot be exported}} + using ::tf; // expected-error {{using declaration referring to 'tf' with module linkage cannot be exported}} namespace NS2 = ::NS; namespace UnnamedNS { diff --git a/clang/test/CXX/module/module.interface/p6.cpp b/clang/test/CXX/module/module.interface/p6.cpp --- a/clang/test/CXX/module/module.interface/p6.cpp +++ b/clang/test/CXX/module/module.interface/p6.cpp @@ -9,7 +9,7 @@ }; typedef S S; export typedef S S; // OK, does not redeclare an entity -export struct S; // expected-error {{cannot export redeclaration 'S' here since the previous declaration is not exported}} +export struct S; // expected-error {{cannot export redeclaration 'S' here since the previous declaration has module linkage}} namespace A { struct X; // expected-note {{previous declaration is here}} @@ -17,10 +17,10 @@ } // namespace A namespace A { -export struct X; // expected-error {{cannot export redeclaration 'X' here since the previous declaration is not exported}} +export struct X; // expected-error {{cannot export redeclaration 'X' here since the previous declaration has module linkage}} export struct Y; // OK struct Z; // expected-note {{previous declaration is here}} -export struct Z; // expected-error {{cannot export redeclaration 'Z' here since the previous declaration is not exported}} +export struct Z; // expected-error {{cannot export redeclaration 'Z' here since the previous declaration has module linkage}} } // namespace A namespace A { @@ -29,29 +29,29 @@ } // namespace A namespace A { -export struct B {}; // expected-error {{cannot export redeclaration 'B' here since the previous declaration is not exported}} -export struct C; // expected-error {{cannot export redeclaration 'C' here since the previous declaration is not exported}} +export struct B {}; // expected-error {{cannot export redeclaration 'B' here since the previous declaration has module linkage}} +export struct C; // expected-error {{cannot export redeclaration 'C' here since the previous declaration has module linkage}} } // namespace A template struct TemplS; // expected-note {{previous declaration is here}} export template -struct TemplS {}; // expected-error {{cannot export redeclaration 'TemplS' here since the previous declaration is not exported}} +struct TemplS {}; // expected-error {{cannot export redeclaration 'TemplS' here since the previous declaration has module linkage}} template struct TemplS2; // expected-note {{previous declaration is here}} export template -struct TemplS2 {}; // expected-error {{cannot export redeclaration 'TemplS2' here since the previous declaration is not exported}} +struct TemplS2 {}; // expected-error {{cannot export redeclaration 'TemplS2' here since the previous declaration has module linkage}} void baz(); // expected-note {{previous declaration is here}} -export void baz(); // expected-error {{cannot export redeclaration 'baz' here since the previous declaration is not exported}} +export void baz(); // expected-error {{cannot export redeclaration 'baz' here since the previous declaration has module linkage}} namespace A { export void foo(); void bar(); // expected-note {{previous declaration is here}} -export void bar(); // expected-error {{cannot export redeclaration 'bar' here since the previous declaration is not exported}} +export void bar(); // expected-error {{cannot export redeclaration 'bar' here since the previous declaration has module linkage}} void f1(); // expected-note {{previous declaration is here}} } // namespace A @@ -63,34 +63,34 @@ // The compiler couldn't export A::f1() here since A::f1() is declared above without exported. // See [module.interface]/p6 for details. -export void A::f1(); // expected-error {{cannot export redeclaration 'f1' here since the previous declaration is not exported}} +export void A::f1(); // expected-error {{cannot export redeclaration 'f1' here since the previous declaration has module linkage}} template void TemplFunc(); // expected-note {{previous declaration is here}} export template -void TemplFunc() { // expected-error {{cannot export redeclaration 'TemplFunc' here since the previous declaration is not exported}} +void TemplFunc() { // expected-error {{cannot export redeclaration 'TemplFunc' here since the previous declaration has module linkage}} } namespace A { template void TemplFunc2(); // expected-note {{previous declaration is here}} export template -void TemplFunc2() {} // expected-error {{cannot export redeclaration 'TemplFunc2' here since the previous declaration is not exported}} +void TemplFunc2() {} // expected-error {{cannot export redeclaration 'TemplFunc2' here since the previous declaration has module linkage}} template void TemplFunc3(); // expected-note {{previous declaration is here}} } // namespace A export template -void A::TemplFunc3() {} // expected-error {{cannot export redeclaration 'TemplFunc3' here since the previous declaration is not exported}} +void A::TemplFunc3() {} // expected-error {{cannot export redeclaration 'TemplFunc3' here since the previous declaration has module linkage}} int var; // expected-note {{previous declaration is here}} -export int var; // expected-error {{cannot export redeclaration 'var' here since the previous declaration is not exported}} +export int var; // expected-error {{cannot export redeclaration 'var' here since the previous declaration has module linkage}} template T TemplVar; // expected-note {{previous declaration is here}} export template -T TemplVar; // expected-error {{cannot export redeclaration 'TemplVar' here since the previous declaration is not exported}} +T TemplVar; // expected-error {{cannot export redeclaration 'TemplVar' here since the previous declaration has module linkage}} // Test the compiler wouldn't complain about the redeclaration of friend in exported class. namespace Friend { diff --git a/clang/test/Modules/cxx20-10-2-ex1.cpp b/clang/test/Modules/cxx20-10-2-ex1.cpp new file mode 100644 --- /dev/null +++ b/clang/test/Modules/cxx20-10-2-ex1.cpp @@ -0,0 +1,31 @@ +// Based on C++20 10.2 example 1. + +// RUN: rm -rf %t +// RUN: mkdir -p %t +// RUN: split-file %s %t + +// RUN: %clang_cc1 -std=c++20 -emit-module-interface %t/std-10-2-ex1-tu1.cpp \ +// RUN: -pedantic-errors -verify -o %t/m1.pcm + +//--- std-10-2-ex1.h +export int x; + +//--- std-10-2-ex1-tu1.cpp +module; + +#include "std-10-2-ex1.h" +// expected-error@std-10-2-ex1.h:* {{export declaration can only be used within a module interface unit after the module declaration}} + +export module M1; +export namespace {} // expected-error {{declaration does not introduce any names to be exported}} +export namespace { +int a1; // expected-error {{declaration of 'a1' with internal linkage cannot be exported}} +} +namespace { // expected-note {{anonymous namespace begins here}} +export int a2; // expected-error {{export declaration appears within anonymous namespace}} +} +export static int b; // expected-error {{declaration of 'b' with internal linkage cannot be exported}} +export int f(); // OK + +export namespace N {} // namespace N +export using namespace N; // expected-error {{ISO C++20 does not permit using directive to be exported}} diff --git a/clang/test/Modules/cxx20-10-2-ex3.cpp b/clang/test/Modules/cxx20-10-2-ex3.cpp new file mode 100644 --- /dev/null +++ b/clang/test/Modules/cxx20-10-2-ex3.cpp @@ -0,0 +1,9 @@ +// Based on C++20 10.2 example 3. + +// RUN: %clang_cc1 -std=c++20 -emit-module-interface %s -o M.pcm + +export module M; +struct S; +export using T = S; // OK, exports name T denoting type S + +// expected-no-diagnostics diff --git a/clang/test/Modules/cxx20-10-2-ex4.cpp b/clang/test/Modules/cxx20-10-2-ex4.cpp new file mode 100644 --- /dev/null +++ b/clang/test/Modules/cxx20-10-2-ex4.cpp @@ -0,0 +1,12 @@ +// Based on C++20 10.2 example 4. + +// RUN: %clang_cc1 -std=c++20 -emit-module-interface %s -verify -o M.pcm + +export module M; + +struct S { // expected-note {{previous declaration is here}} + int n; +}; +typedef S S; +export typedef S S; // OK, does not redeclare an entity +export struct S; // expected-error {{cannot export redeclaration 'S' here since the previous declaration has module linkage}} diff --git a/clang/test/Modules/cxx20-10-2-ex5.cpp b/clang/test/Modules/cxx20-10-2-ex5.cpp new file mode 100644 --- /dev/null +++ b/clang/test/Modules/cxx20-10-2-ex5.cpp @@ -0,0 +1,54 @@ +// Based on C++20 10.2 example 5. + +// RUN: rm -rf %t +// RUN: mkdir -p %t +// RUN: split-file %s %t + +// RUN: %clang_cc1 -std=c++20 -emit-module-interface %t/std-10-2-ex5-tu1.cpp \ +// RUN: -o %t/M.pcm + +// RUN: %clang_cc1 -std=c++20 -emit-obj %t/std-10-2-ex5-tu2.cpp \ +// RUN: -fmodule-file=%t/M.pcm -o %t/tu-2.o + +// RUN: %clang_cc1 -std=c++20 -emit-obj %t/std-10-2-ex5-tu3.cpp \ +// RUN: -fmodule-file=%t/M.pcm -verify -o %t/main.o + +//--- std-10-2-ex5-tu1.cpp +export module M; +export struct X { + static void f(); + struct Y {}; +}; +namespace { +struct S {}; +} // namespace +export void f(S); // OK +struct T {}; +export T id(T); // OK +export struct A; // A exported as incomplete + +export auto rootFinder(double a) { + return [=](double x) { return (x + a / x) / 2; }; +} +export const int n = 5; // OK, n has external linkage + +//--- std-10-2-ex5-tu2.cpp + +module M; +struct A { + int value; +}; + +//--- std-10-2-ex5-tu3.cpp + +import M; + +int main() { + X::f(); // OK, X is exported and definition of X is reachable + X::Y y; // OK, X::Y is exported as a complete type + auto f = rootFinder(2); // OK + // error: A is incomplete + return A{45}.value; // expected-error {{invalid use of incomplete type 'A'}} + // expected-error@-1 {{member access into incomplete type 'A'}} + // expected-note@std-10-2-ex5-tu1.cpp:12 2{{forward declaration of 'A'}} +} diff --git a/clang/test/Modules/cxx20-10-2-ex6.cpp b/clang/test/Modules/cxx20-10-2-ex6.cpp new file mode 100644 --- /dev/null +++ b/clang/test/Modules/cxx20-10-2-ex6.cpp @@ -0,0 +1,21 @@ +// Based on C++20 10.2 example 6. + +// RUN: %clang_cc1 -std=c++20 -emit-module-interface %s -verify -o M.pcm + +export module M; + +static int f(); // expected-note {{previous declaration is here}} #1 + // error: #1 gives internal linkage +export int f(); // expected-error {{cannot export redeclaration 'f' here since the previous declaration has internal linkage}} +struct S; // expected-note {{previous declaration is here}} #2 + // error: #2 gives module linkage +export struct S; // expected-error {{cannot export redeclaration 'S' here since the previous declaration has module linkage}} + +namespace { +namespace N { +extern int x; // expected-note {{previous declaration is here}} #3 +} +} // namespace + // error: #3 gives internal linkage +export int N::x; // expected-error {{cannot export redeclaration 'x' here since the previous declaration has internal linkage}} + // expected-error@-1 {{declaration of 'x' with internal linkage cannot be exported}} diff --git a/clang/test/Modules/cxx20-10-2-ex7.cpp b/clang/test/Modules/cxx20-10-2-ex7.cpp new file mode 100644 --- /dev/null +++ b/clang/test/Modules/cxx20-10-2-ex7.cpp @@ -0,0 +1,9 @@ +// Based on C++20 10.2 example 6. + +// RUN: %clang_cc1 -std=c++20 -emit-module-interface %s -verify -o M.pcm + +export module M; +export namespace N { +int x; // OK +static_assert(1 == 1); // expected-error {{static_assert declaration cannot be exported}} +} // namespace N