iains created this revision.
Herald added a project: All.
iains added reviewers: rsmith, urnathan, ChuanqiXu.
iains added a subscriber: clang-modules.
iains published this revision for review.
iains added a comment.
Herald added a project: clang.
Herald added a subscriber: cfe-commits.

this covers most of the tests from the standard section 10.3 (one to follow 
that has an additional dependency on header units)

note the change in the "cannot export redeclaration 'xxxx here since the 
previous declaration" errors to include a more specific reason than before.


This adjusts the handling for:

export module  M;

export namespace {};

export namespace N {};
export using namespace N;

In the first case, we were allowing empty anonymous namespaces
as part of an extension allowing empty top-level entities, but that seems
inappropriate in this case, since the linkage would be internal for the
anonymous namespace.  We now report an error for this.

The second case was producing a warning diagnostic that this was
accepted as an extension - however the C++20 standard does allow this
as well-formed.

In the third case we keep the current practice that this is accepted with a
warning (as an extension). The C++20 standard says it's an error.

We also ensure that using decls are only applied to items with external linkage.

This adjusts error messages for exports involving redeclarations in modules to
be more specific about the reason that the decl has been rejected.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D122119

Files:
  clang/include/clang/Basic/DiagnosticSemaKinds.td
  clang/lib/Sema/SemaDecl.cpp
  clang/lib/Sema/SemaModule.cpp
  clang/test/CXX/module/module.interface/p3.cpp
  clang/test/CXX/module/module.interface/p5.cpp
  clang/test/CXX/module/module.interface/p6.cpp
  clang/test/Modules/cxx20-10-2-ex1.cpp
  clang/test/Modules/cxx20-10-2-ex3.cpp
  clang/test/Modules/cxx20-10-2-ex4.cpp
  clang/test/Modules/cxx20-10-2-ex5.cpp
  clang/test/Modules/cxx20-10-2-ex6.cpp
  clang/test/Modules/cxx20-10-2-ex7.cpp

Index: clang/test/Modules/cxx20-10-2-ex7.cpp
===================================================================
--- /dev/null
+++ 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
Index: clang/test/Modules/cxx20-10-2-ex6.cpp
===================================================================
--- /dev/null
+++ 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}}
Index: clang/test/Modules/cxx20-10-2-ex5.cpp
===================================================================
--- /dev/null
+++ 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-n...@std-10-2-ex5-tu1.cpp:12 2{{forward declaration of 'A'}}
+}
Index: clang/test/Modules/cxx20-10-2-ex4.cpp
===================================================================
--- /dev/null
+++ 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}}
Index: clang/test/Modules/cxx20-10-2-ex3.cpp
===================================================================
--- /dev/null
+++ 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
Index: clang/test/Modules/cxx20-10-2-ex1.cpp
===================================================================
--- /dev/null
+++ 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}}
Index: clang/test/CXX/module/module.interface/p6.cpp
===================================================================
--- clang/test/CXX/module/module.interface/p6.cpp
+++ 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 <typename T>
 struct TemplS; // expected-note {{previous declaration is here}}
 
 export template <typename T>
-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 <typename T>
 struct TemplS2; // expected-note {{previous declaration is here}}
 
 export template <typename U>
-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 <typename T>
 void TemplFunc(); // expected-note {{previous declaration is here}}
 
 export template <typename T>
-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 <typename T>
 void TemplFunc2(); // expected-note {{previous declaration is here}}
 export template <typename T>
-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 <typename T>
 void TemplFunc3(); // expected-note {{previous declaration is here}}
 } // namespace A
 
 export template <typename T>
-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 <typename T>
 T TemplVar; // expected-note {{previous declaration is here}}
 export template <typename T>
-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 {
Index: clang/test/CXX/module/module.interface/p5.cpp
===================================================================
--- clang/test/CXX/module/module.interface/p5.cpp
+++ 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<typename> int ta;
+template<typename> int ta; // expected-note {{target}}
 template<typename> static int sta; // expected-note {{target}}
-template<typename> void tb();
+template<typename> void tb(); // expected-note {{target}}
 template<typename> static void stb(); // expected-note {{target}}
-template<typename> struct tc {};
-template<typename> using te = int;
-template<typename> using tf = c;
+template<typename> struct tc {}; // expected-note {{target}}
+template<typename> using te = int; // expected-note {{target}}
+template<typename> 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 {
Index: clang/test/CXX/module/module.interface/p3.cpp
===================================================================
--- clang/test/CXX/module/module.interface/p3.cpp
+++ 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}}
Index: clang/lib/Sema/SemaModule.cpp
===================================================================
--- clang/lib/Sema/SemaModule.cpp
+++ clang/lib/Sema/SemaModule.cpp
@@ -714,6 +714,7 @@
   StaticAssert,
   Asm,
   UsingDirective,
+  Namespace,
   Context
 };
 }
@@ -743,6 +744,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.
@@ -766,14 +771,17 @@
 static bool checkExportedDecl(Sema &S, Decl *D, SourceLocation BlockStart) {
   // C++2a [module.interface]p3:
   //   An exported declaration shall declare at least one name
-  if (auto UDK = getUnnamedDeclKind(D))
+  if (auto UDK = getUnnamedDeclKind(D)) {
     diagExportedUnnamedDecl(S, *UDK, D, BlockStart);
+  }
 
   //   [...] shall not declare a name with internal linkage.
+  bool HasName = false;
   if (auto *ND = dyn_cast<NamedDecl>(D)) {
     // Don't diagnose anonymous union objects; we'll diagnose their members
     // instead.
-    if (ND->getDeclName() && ND->getFormalLinkage() == InternalLinkage) {
+    HasName = !!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);
@@ -785,8 +793,10 @@
   //   shall have been introduced with a name having external linkage
   if (auto *USD = dyn_cast<UsingShadowDecl>(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);
@@ -794,10 +804,18 @@
   }
 
   // Recurse into namespace-scope DeclContexts. (Only namespace-scope
-  // declarations are exported.)
-  if (auto *DC = dyn_cast<DeclContext>(D))
-    if (DC->getRedeclContext()->isFileContext() && !isa<EnumDecl>(D))
+  // declarations are exported.).
+  if (auto *DC = dyn_cast<DeclContext>(D)) {
+    if (isa<NamespaceDecl>(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<EnumDecl>(D))
       return checkExportedDeclContext(S, DC, BlockStart);
+  }
   return false;
 }
 
Index: clang/lib/Sema/SemaDecl.cpp
===================================================================
--- clang/lib/Sema/SemaDecl.cpp
+++ clang/lib/Sema/SemaDecl.cpp
@@ -1668,7 +1668,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;
 }
Index: clang/include/clang/Basic/DiagnosticSemaKinds.td
===================================================================
--- clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -7829,8 +7829,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<
@@ -11043,6 +11043,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<ExportUnnamed>;
+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">;
@@ -11054,7 +11056,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">;
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to