r358932 - [c++2a] Implement semantic restrictions for 'export' declarations.

Richard Smith via cfe-commits cfe-commits at lists.llvm.org
Mon Apr 22 15:50:11 PDT 2019


Author: rsmith
Date: Mon Apr 22 15:50:11 2019
New Revision: 358932

URL: http://llvm.org/viewvc/llvm-project?rev=358932&view=rev
Log:
[c++2a] Implement semantic restrictions for 'export' declarations.

Added:
    cfe/trunk/test/CXX/module/module.interface/Inputs/
    cfe/trunk/test/CXX/module/module.interface/Inputs/header.h
    cfe/trunk/test/CXX/module/module.interface/p2.cpp
    cfe/trunk/test/CXX/module/module.interface/p3.cpp
    cfe/trunk/test/CXX/module/module.interface/p5.cpp
Modified:
    cfe/trunk/include/clang/AST/Decl.h
    cfe/trunk/include/clang/AST/DeclBase.h
    cfe/trunk/include/clang/Basic/DiagnosticGroups.td
    cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td
    cfe/trunk/include/clang/Sema/Sema.h
    cfe/trunk/lib/AST/DeclBase.cpp
    cfe/trunk/lib/Parse/ParseDeclCXX.cpp
    cfe/trunk/lib/Sema/SemaDeclCXX.cpp
    cfe/trunk/lib/Sema/SemaModule.cpp
    cfe/trunk/test/CXX/module/module.interface/p1.cpp
    cfe/trunk/test/CXX/modules-ts/basic/basic.def.odr/p4/module.cpp
    cfe/trunk/test/CXX/modules-ts/basic/basic.def.odr/p4/module.cppm
    cfe/trunk/test/CXX/modules-ts/basic/basic.def.odr/p4/user.cpp
    cfe/trunk/test/SemaCXX/anonymous-union-export.cpp
    cfe/trunk/test/SemaCXX/modules-ts.cppm

Modified: cfe/trunk/include/clang/AST/Decl.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/AST/Decl.h?rev=358932&r1=358931&r2=358932&view=diff
==============================================================================
--- cfe/trunk/include/clang/AST/Decl.h (original)
+++ cfe/trunk/include/clang/AST/Decl.h Mon Apr 22 15:50:11 2019
@@ -4239,8 +4239,10 @@ public:
   SourceLocation getRBraceLoc() const { return RBraceLoc; }
   void setRBraceLoc(SourceLocation L) { RBraceLoc = L; }
 
+  bool hasBraces() const { return RBraceLoc.isValid(); }
+
   SourceLocation getEndLoc() const LLVM_READONLY {
-    if (RBraceLoc.isValid())
+    if (hasBraces())
       return RBraceLoc;
     // No braces: get the end location of the (only) declaration in context
     // (if present).

Modified: cfe/trunk/include/clang/AST/DeclBase.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/AST/DeclBase.h?rev=358932&r1=358931&r2=358932&view=diff
==============================================================================
--- cfe/trunk/include/clang/AST/DeclBase.h (original)
+++ cfe/trunk/include/clang/AST/DeclBase.h Mon Apr 22 15:50:11 2019
@@ -600,10 +600,6 @@ public:
     return getModuleOwnershipKind() == ModuleOwnershipKind::ModulePrivate;
   }
 
-  /// Whether this declaration is exported (by virtue of being lexically
-  /// within an ExportDecl or by being a NamespaceDecl).
-  bool isExported() const;
-
   /// Return true if this declaration has an attribute which acts as
   /// definition of the entity, such as 'alias' or 'ifunc'.
   bool hasDefiningAttr() const;

Modified: cfe/trunk/include/clang/Basic/DiagnosticGroups.td
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/DiagnosticGroups.td?rev=358932&r1=358931&r2=358932&view=diff
==============================================================================
--- cfe/trunk/include/clang/Basic/DiagnosticGroups.td (original)
+++ cfe/trunk/include/clang/Basic/DiagnosticGroups.td Mon Apr 22 15:50:11 2019
@@ -168,6 +168,7 @@ def ExtraTokens : DiagGroup<"extra-token
 def CXX98CompatExtraSemi : DiagGroup<"c++98-compat-extra-semi">;
 def CXX11ExtraSemi : DiagGroup<"c++11-extra-semi">;
 def EmptyInitStatement : DiagGroup<"empty-init-stmt">;
+def ExportUnnamed : DiagGroup<"export-unnamed">;
 def ExtraSemiStmt : DiagGroup<"extra-semi-stmt", [EmptyInitStatement]>;
 def ExtraSemi : DiagGroup<"extra-semi", [CXX98CompatExtraSemi,
                                          CXX11ExtraSemi]>;

Modified: cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td?rev=358932&r1=358931&r2=358932&view=diff
==============================================================================
--- cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td (original)
+++ cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td Mon Apr 22 15:50:11 2019
@@ -9273,8 +9273,27 @@ def err_module_decl_not_at_start : Error
 def note_global_module_introducer_missing : Note<
   "add 'module;' to the start of the file to introduce a "
   "global module fragment">;
+def err_export_within_anonymous_namespace : Error<
+  "export declaration appears within anonymous namespace">;
+def note_anonymous_namespace : Note<"anonymous namespace begins here">;
+def ext_export_no_name_block : ExtWarn<
+  "ISO C++20 does not permit %select{an empty|a static_assert}0 declaration "
+  "to appear in an export block">, InGroup<ExportUnnamed>;
+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 note_export : Note<"export block begins here">;
+def err_export_no_name : Error<
+  "%select{empty|static_assert|asm}0 declaration cannot be exported">;
+def ext_export_using_directive : ExtWarn<
+  "ISO C++20 does not permit using directive to be exported">,
+  InGroup<DiagGroup<"export-using-directive">>;
 def err_export_within_export : Error<
   "export declaration appears within another export declaration">;
+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">;
 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">;

Modified: cfe/trunk/include/clang/Sema/Sema.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Sema/Sema.h?rev=358932&r1=358931&r2=358932&view=diff
==============================================================================
--- cfe/trunk/include/clang/Sema/Sema.h (original)
+++ cfe/trunk/include/clang/Sema/Sema.h Mon Apr 22 15:50:11 2019
@@ -1651,6 +1651,9 @@ private:
   /// The modules we're currently parsing.
   llvm::SmallVector<ModuleScope, 16> ModuleScopes;
 
+  /// Namespace definitions that we will export when they finish.
+  llvm::SmallPtrSet<const NamespaceDecl*, 8> DeferredExportedNamespaces;
+
   /// Get the module whose scope we are currently within.
   Module *getCurrentModule() const {
     return ModuleScopes.empty() ? nullptr : ModuleScopes.back().Module;

Modified: cfe/trunk/lib/AST/DeclBase.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/DeclBase.cpp?rev=358932&r1=358931&r2=358932&view=diff
==============================================================================
--- cfe/trunk/lib/AST/DeclBase.cpp (original)
+++ cfe/trunk/lib/AST/DeclBase.cpp Mon Apr 22 15:50:11 2019
@@ -430,22 +430,6 @@ bool Decl::isReferenced() const {
   return false;
 }
 
-bool Decl::isExported() const {
-  if (isModulePrivate())
-    return false;
-  // Namespaces are always exported.
-  if (isa<TranslationUnitDecl>(this) || isa<NamespaceDecl>(this))
-    return true;
-  // Otherwise, this is a strictly lexical check.
-  for (auto *DC = getLexicalDeclContext(); DC; DC = DC->getLexicalParent()) {
-    if (cast<Decl>(DC)->isModulePrivate())
-      return false;
-    if (isa<ExportDecl>(DC))
-      return true;
-  }
-  return false;
-}
-
 ExternalSourceSymbolAttr *Decl::getExternalSourceSymbolAttr() const {
   const Decl *Definition = nullptr;
   if (auto *ID = dyn_cast<ObjCInterfaceDecl>(this)) {

Modified: cfe/trunk/lib/Parse/ParseDeclCXX.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Parse/ParseDeclCXX.cpp?rev=358932&r1=358931&r2=358932&view=diff
==============================================================================
--- cfe/trunk/lib/Parse/ParseDeclCXX.cpp (original)
+++ cfe/trunk/lib/Parse/ParseDeclCXX.cpp Mon Apr 22 15:50:11 2019
@@ -437,9 +437,10 @@ Decl *Parser::ParseExportDeclaration() {
 
   // The Modules TS draft says "An export-declaration shall declare at least one
   // entity", but the intent is that it shall contain at least one declaration.
-  if (Tok.is(tok::r_brace))
+  if (Tok.is(tok::r_brace) && getLangOpts().ModulesTS) {
     Diag(ExportLoc, diag::err_export_empty)
         << SourceRange(ExportLoc, Tok.getLocation());
+  }
 
   while (!tryParseMisplacedModuleImport() && Tok.isNot(tok::r_brace) &&
          Tok.isNot(tok::eof)) {

Modified: cfe/trunk/lib/Sema/SemaDeclCXX.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaDeclCXX.cpp?rev=358932&r1=358931&r2=358932&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaDeclCXX.cpp (original)
+++ cfe/trunk/lib/Sema/SemaDeclCXX.cpp Mon Apr 22 15:50:11 2019
@@ -9037,6 +9037,9 @@ void Sema::ActOnFinishNamespaceDef(Decl
   PopDeclContext();
   if (Namespc->hasAttr<VisibilityAttr>())
     PopPragmaVisibility(true, RBrace);
+  // If this namespace contains an export-declaration, export it now.
+  if (DeferredExportedNamespaces.erase(Namespc))
+    Dcl->setModuleOwnershipKind(Decl::ModuleOwnershipKind::VisibleWhenImported);
 }
 
 CXXRecordDecl *Sema::getStdBadAlloc() const {

Modified: cfe/trunk/lib/Sema/SemaModule.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaModule.cpp?rev=358932&r1=358931&r2=358932&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaModule.cpp (original)
+++ cfe/trunk/lib/Sema/SemaModule.cpp Mon Apr 22 15:50:11 2019
@@ -330,6 +330,14 @@ DeclResult Sema::ActOnModuleImport(Sourc
   return ActOnModuleImport(StartLoc, ExportLoc, ImportLoc, Mod, Path);
 }
 
+/// Determine whether \p D is lexically within an export-declaration.
+static const ExportDecl *getEnclosingExportDecl(const Decl *D) {
+  for (auto *DC = D->getLexicalDeclContext(); DC; DC = DC->getLexicalParent())
+    if (auto *ED = dyn_cast<ExportDecl>(DC))
+      return ED;
+  return nullptr;
+}
+
 DeclResult Sema::ActOnModuleImport(SourceLocation StartLoc,
                                    SourceLocation ExportLoc,
                                    SourceLocation ImportLoc,
@@ -384,7 +392,7 @@ DeclResult Sema::ActOnModuleImport(Sourc
 
   // Re-export the module if needed.
   if (!ModuleScopes.empty() && ModuleScopes.back().ModuleInterface) {
-    if (ExportLoc.isValid() || Import->isExported())
+    if (ExportLoc.isValid() || getEnclosingExportDecl(Import))
       getCurrentModule()->Exports.emplace_back(Mod, false);
   } else if (ExportLoc.isValid()) {
     Diag(ExportLoc, diag::err_export_not_in_module_interface);
@@ -516,11 +524,13 @@ Decl *Sema::ActOnStartExportDecl(Scope *
                                  SourceLocation LBraceLoc) {
   ExportDecl *D = ExportDecl::Create(Context, CurContext, ExportLoc);
 
-  // C++20 [module.interface]p1:
+  // Set this temporarily so we know the export-declaration was braced.
+  D->setRBraceLoc(LBraceLoc);
+
+  // C++2a [module.interface]p1:
   //   An export-declaration shall appear only [...] in the purview of a module
   //   interface unit. An export-declaration shall not appear directly or
-  //   indirectly within an unnamed namespace or a private-module-fragment.
-  // FIXME: Check for the unnamed namespace case.
+  //   indirectly within [...] a private-module-fragment.
   if (ModuleScopes.empty() || !ModuleScopes.back().Module->isModulePurview()) {
     Diag(ExportLoc, diag::err_export_not_in_module_interface) << 0;
   } else if (!ModuleScopes.back().ModuleInterface) {
@@ -534,10 +544,35 @@ Decl *Sema::ActOnStartExportDecl(Scope *
     Diag(ModuleScopes.back().BeginLoc, diag::note_private_module_fragment);
   }
 
+  for (const DeclContext *DC = CurContext; DC; DC = DC->getLexicalParent()) {
+    if (const auto *ND = dyn_cast<NamespaceDecl>(DC)) {
+      //   An export-declaration shall not appear directly or indirectly within
+      //   an unnamed namespace [...]
+      if (ND->isAnonymousNamespace()) {
+        Diag(ExportLoc, diag::err_export_within_anonymous_namespace);
+        Diag(ND->getLocation(), diag::note_anonymous_namespace);
+        // Don't diagnose internal-linkage declarations in this region.
+        D->setInvalidDecl();
+        break;
+      }
+
+      //   A declaration is exported if it is [...] a namespace-definition
+      //   that contains an exported declaration.
+      //
+      // Defer exporting the namespace until after we leave it, in order to
+      // avoid marking all subsequent declarations in the namespace as exported.
+      if (!DeferredExportedNamespaces.insert(ND).second)
+        break;
+    }
+  }
+
   //   [...] its declaration or declaration-seq shall not contain an
   //   export-declaration.
-  if (D->isExported())
+  if (auto *ED = getEnclosingExportDecl(D)) {
     Diag(ExportLoc, diag::err_export_within_export);
+    if (ED->hasBraces())
+      Diag(ED->getLocation(), diag::note_export);
+  }
 
   CurContext->addDecl(D);
   PushDeclContext(S, D);
@@ -545,15 +580,131 @@ Decl *Sema::ActOnStartExportDecl(Scope *
   return D;
 }
 
+static bool checkExportedDeclContext(Sema &S, DeclContext *DC,
+                                     SourceLocation BlockStart);
+
+namespace {
+enum class UnnamedDeclKind {
+  Empty,
+  StaticAssert,
+  Asm,
+  UsingDirective,
+  Context
+};
+}
+
+static llvm::Optional<UnnamedDeclKind> getUnnamedDeclKind(Decl *D) {
+  if (isa<EmptyDecl>(D))
+    return UnnamedDeclKind::Empty;
+  if (isa<StaticAssertDecl>(D))
+    return UnnamedDeclKind::StaticAssert;
+  if (isa<FileScopeAsmDecl>(D))
+    return UnnamedDeclKind::Asm;
+  if (isa<UsingDirectiveDecl>(D))
+    return UnnamedDeclKind::UsingDirective;
+  // Everything else either introduces one or more names or is ill-formed.
+  return llvm::None;
+}
+
+unsigned getUnnamedDeclDiag(UnnamedDeclKind UDK, bool InBlock) {
+  switch (UDK) {
+  case UnnamedDeclKind::Empty:
+  case UnnamedDeclKind::StaticAssert:
+    // Allow empty-declarations and static_asserts in an export block as an
+    // extension.
+    return InBlock ? diag::ext_export_no_name_block : diag::err_export_no_name;
+
+  case UnnamedDeclKind::UsingDirective:
+    // Allow exporting using-directives as an extension.
+    return diag::ext_export_using_directive;
+
+  case UnnamedDeclKind::Context:
+    // Allow exporting DeclContexts that transitively contain no declarations
+    // as an extension.
+    return diag::ext_export_no_names;
+
+  case UnnamedDeclKind::Asm:
+    return diag::err_export_no_name;
+  }
+  llvm_unreachable("unknown kind");
+}
+
+static void diagExportedUnnamedDecl(Sema &S, UnnamedDeclKind UDK, Decl *D,
+                                    SourceLocation BlockStart) {
+  S.Diag(D->getLocation(), getUnnamedDeclDiag(UDK, BlockStart.isValid()))
+      << (unsigned)UDK;
+  if (BlockStart.isValid())
+    S.Diag(BlockStart, diag::note_export);
+}
+
+/// Check that it's valid to export \p D.
+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))
+    diagExportedUnnamedDecl(S, *UDK, D, BlockStart);
+
+  //   [...] shall not declare a name with internal linkage.
+  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) {
+      S.Diag(ND->getLocation(), diag::err_export_internal) << ND;
+      if (BlockStart.isValid())
+        S.Diag(BlockStart, diag::note_export);
+    }
+  }
+
+  // C++2a [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<UsingShadowDecl>(D)) {
+    NamedDecl *Target = USD->getUnderlyingDecl();
+    if (Target->getFormalLinkage() == InternalLinkage) {
+      S.Diag(USD->getLocation(), diag::err_export_using_internal) << Target;
+      S.Diag(Target->getLocation(), diag::note_using_decl_target);
+      if (BlockStart.isValid())
+        S.Diag(BlockStart, diag::note_export);
+    }
+  }
+
+  // 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))
+      return checkExportedDeclContext(S, DC, BlockStart);
+  return false;
+}
+
+/// Check that it's valid to export all the declarations in \p DC.
+static bool checkExportedDeclContext(Sema &S, DeclContext *DC,
+                                     SourceLocation BlockStart) {
+  bool AllUnnamed = true;
+  for (auto *D : DC->decls())
+    AllUnnamed &= checkExportedDecl(S, D, BlockStart);
+  return AllUnnamed;
+}
+
 /// Complete the definition of an export declaration.
 Decl *Sema::ActOnFinishExportDecl(Scope *S, Decl *D, SourceLocation RBraceLoc) {
   auto *ED = cast<ExportDecl>(D);
   if (RBraceLoc.isValid())
     ED->setRBraceLoc(RBraceLoc);
 
-  // FIXME: Diagnose export of internal-linkage declaration (including
-  // anonymous namespace).
-
   PopDeclContext();
+
+  if (!D->isInvalidDecl()) {
+    SourceLocation BlockStart =
+        ED->hasBraces() ? ED->getBeginLoc() : SourceLocation();
+    for (auto *Child : ED->decls()) {
+      if (checkExportedDecl(*this, Child, BlockStart)) {
+        // If a top-level child is a linkage-spec declaration, it might contain
+        // no declarations (transitively), in which case it's ill-formed.
+        diagExportedUnnamedDecl(*this, UnnamedDeclKind::Context, Child,
+                                BlockStart);
+      }
+    }
+  }
+
   return D;
 }

Added: cfe/trunk/test/CXX/module/module.interface/Inputs/header.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CXX/module/module.interface/Inputs/header.h?rev=358932&view=auto
==============================================================================
--- cfe/trunk/test/CXX/module/module.interface/Inputs/header.h (added)
+++ cfe/trunk/test/CXX/module/module.interface/Inputs/header.h Mon Apr 22 15:50:11 2019
@@ -0,0 +1,3 @@
+extern int foo;
+namespace bar { extern int baz(); }
+static int baz;

Modified: cfe/trunk/test/CXX/module/module.interface/p1.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CXX/module/module.interface/p1.cpp?rev=358932&r1=358931&r2=358932&view=diff
==============================================================================
--- cfe/trunk/test/CXX/module/module.interface/p1.cpp (original)
+++ cfe/trunk/test/CXX/module/module.interface/p1.cpp Mon Apr 22 15:50:11 2019
@@ -23,15 +23,15 @@ namespace N {
 }
 
 #ifdef ERRORS
-namespace {
-  export int d1; // FIXME: invalid
+namespace { // expected-note 2{{anonymous namespace begins here}}
+  export int d1; // expected-error {{export declaration appears within anonymous namespace}}
   namespace X {
-    export int d2; // FIXME: invalid
+    export int d2; // expected-error {{export declaration appears within anonymous namespace}}
   }
 }
 
 export export int e; // expected-error {{within another export declaration}}
-export { export int f; } // expected-error {{within another export declaration}}
+export { export int f; } // expected-error {{within another export declaration}} expected-note {{export block begins here}}
 
 module :private; // expected-note {{private module fragment begins here}}
 export int priv; // expected-error {{export declaration cannot be used in a private module fragment}}

Added: cfe/trunk/test/CXX/module/module.interface/p2.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CXX/module/module.interface/p2.cpp?rev=358932&view=auto
==============================================================================
--- cfe/trunk/test/CXX/module/module.interface/p2.cpp (added)
+++ cfe/trunk/test/CXX/module/module.interface/p2.cpp Mon Apr 22 15:50:11 2019
@@ -0,0 +1,94 @@
+// RUN: rm -rf %t
+// RUN: mkdir -p %t
+// RUN: %clang_cc1 -std=c++2a -x c++-header %S/Inputs/header.h -emit-header-module -fmodule-name=FIXME -o %t/h.pcm
+// RUN: %clang_cc1 -std=c++2a %s -DX_INTERFACE -emit-module-interface -o %t/x.pcm
+// RUN: %clang_cc1 -std=c++2a %s -DY_INTERFACE -emit-module-interface -o %t/y.pcm
+// RUN: %clang_cc1 -std=c++2a %s -DINTERFACE -fmodule-file=%t/x.pcm -fmodule-file=%t/y.pcm -emit-module-interface -o %t/m.pcm
+// RUN: %clang_cc1 -std=c++2a %s -DIMPLEMENTATION -I%S/Inputs -fmodule-file=%t/h.pcm -fmodule-file=%t/m.pcm -verify
+// RUN: %clang_cc1 -std=c++2a %s -DUSER -I%S/Inputs -fmodule-file=%t/h.pcm -fmodule-file=%t/m.pcm -verify
+
+#if defined(X_INTERFACE)
+export module X;
+export int x;
+
+#elif defined(Y_INTERFACE)
+export module Y;
+export int y;
+
+#elif defined(INTERFACE)
+export module p2;
+export import X;
+import Y; // not exported
+
+namespace A {
+  int f();
+  export int g();
+  int h();
+  namespace inner {}
+}
+export namespace B {
+  namespace inner {}
+}
+namespace B {
+  int f();
+}
+namespace C {}
+namespace D { int f(); }
+export namespace D {}
+
+#elif defined(IMPLEMENTATION)
+module p2;
+import "header.h";
+
+// Per [basic.scope.namespace]/2.3, exportedness has no impact on visibility
+// within the same module.
+//
+// expected-no-diagnostics
+
+void use() {
+  A::f();
+  A::g();
+  A::h();
+  using namespace A::inner;
+
+  using namespace B;
+  using namespace B::inner;
+  B::f();
+  f();
+
+  using namespace C;
+
+  D::f();
+}
+
+int use_header() { return foo + bar::baz(); }
+
+#elif defined(USER)
+import p2;
+import "header.h";
+
+void use() {
+  // namespace A is implicitly exported by the export of A::g.
+  A::f(); // expected-error {{no member named 'f' in namespace 'A'}}
+  A::g();
+  A::h(); // expected-error {{no member named 'h' in namespace 'A'}}
+  using namespace A::inner; // expected-error {{expected namespace name}}
+
+  // namespace B and B::inner are explicitly exported
+  using namespace B;
+  using namespace B::inner;
+  B::f(); // expected-error {{no member named 'f' in namespace 'B'}}
+  f(); // expected-error {{undeclared identifier 'f'}}
+
+  // namespace C is not exported
+  using namespace C; // expected-error {{expected namespace name}}
+
+  // namespace D is exported, but D::f is not
+  D::f(); // expected-error {{no member named 'f' in namespace 'D'}}
+}
+
+int use_header() { return foo + bar::baz(); }
+
+#else
+#error unknown mode
+#endif

Added: cfe/trunk/test/CXX/module/module.interface/p3.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CXX/module/module.interface/p3.cpp?rev=358932&view=auto
==============================================================================
--- cfe/trunk/test/CXX/module/module.interface/p3.cpp (added)
+++ cfe/trunk/test/CXX/module/module.interface/p3.cpp Mon Apr 22 15:50:11 2019
@@ -0,0 +1,54 @@
+// RUN: %clang_cc1 -std=c++2a %s -verify -pedantic-errors
+
+export module p3;
+
+namespace A { int ns_mem; }
+
+// 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 { // 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}}
+}
+
+export struct {}; // expected-error {{must be class member}} expected-error {{GNU extension}}
+export struct {} struct_;
+export union {}; // expected-error {{must be declared 'static'}}
+export union {} union_;
+export enum {}; // expected-error {{does not declare anything}}
+export enum {} enum_;
+export enum E : int;
+export typedef int; // expected-error {{typedef requires a name}}
+export static union {}; // FIXME: this declaration is ill-formed even without the 'export'
+export asm(""); // expected-error {{asm declaration cannot be exported}}
+export namespace B = A;
+export using A::ns_mem;
+namespace A {
+  export using A::ns_mem;
+}
+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}}
+export extern "C++" { extern "C" {} } // expected-error {{ISO C++20 does not permit a declaration that does not introduce any names to be exported}}
+export extern "C++" { extern "C" int extern_c; }
+export { // expected-note {{export block}}
+  extern "C++" int extern_cxx;
+  extern "C++" {} // expected-error {{ISO C++20 does not permit a declaration that does not introduce any names to be exported}}
+}
+export [[]]; // FIXME (bad diagnostic text): expected-error {{empty declaration cannot be exported}}
+export [[example::attr]]; // FIXME: expected-error {{empty declaration cannot be exported}} expected-warning {{unknown attribute 'attr'}}
+
+// [...] shall not declare a name with internal linkage
+export static int a; // expected-error {{declaration of 'a' with internal linkage cannot be exported}}
+export static int b(); // expected-error {{declaration of 'b' with internal linkage cannot be exported}}
+export namespace { int c; } // expected-error {{declaration of 'c' with internal linkage cannot be exported}}
+namespace { // expected-note {{here}}
+  export int d; // expected-error {{export declaration appears within anonymous namespace}}
+}
+export template<typename> static int e; // FIXME
+export template<typename> static int f(); // expected-error {{declaration of 'f' with internal linkage cannot be exported}}
+export const int k = 5;
+export static union { int n; }; // expected-error {{declaration of 'n' with internal linkage cannot be exported}}

Added: cfe/trunk/test/CXX/module/module.interface/p5.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CXX/module/module.interface/p5.cpp?rev=358932&view=auto
==============================================================================
--- cfe/trunk/test/CXX/module/module.interface/p5.cpp (added)
+++ cfe/trunk/test/CXX/module/module.interface/p5.cpp Mon Apr 22 15:50:11 2019
@@ -0,0 +1,89 @@
+// RUN: %clang_cc1 -std=c++2a %s -verify -pedantic-errors
+
+export module p5;
+
+int a;
+static int sa; // expected-note {{target}}
+void b();
+static void sb(); // expected-note {{target}}
+struct c {};
+enum d {};
+using e = int;
+using f = c;
+static union { int sg1, sg2; }; // expected-note {{target}}
+namespace NS {}
+
+template<typename> int ta;
+template<typename> static int sta;
+template<typename> void tb();
+template<typename> static void stb(); // expected-note {{target}}
+template<typename> struct tc {};
+template<typename> using te = int;
+template<typename> using tf = c;
+
+namespace UnnamedNS {
+  namespace {
+    int a; // expected-note {{target}}
+    static int sa; // expected-note {{target}}
+    void b(); // expected-note {{target}}
+    static void sb(); // expected-note {{target}}
+    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; // expected-note {{target}}
+    template<typename> static int sta; // expected-note {{target}}
+    template<typename> void tb(); // expected-note {{target}}
+    template<typename> static void stb(); // expected-note {{target}}
+    template<typename> struct tc {}; // expected-note {{target}}
+    template<typename> using te = int; // expected-note {{target}}
+    template<typename> using tf = c; // expected-note {{target}}
+  }
+}
+
+export { // expected-note 18{{here}}
+  using ::a;
+  using ::sa; // expected-error {{using declaration referring to 'sa' with internal linkage}}
+  using ::b;
+  using ::sb; // expected-error {{using declaration referring to 'sb' with internal linkage}}
+  using ::c;
+  using ::d;
+  using ::e;
+  using ::f;
+  using ::sg1; // expected-error {{using declaration referring to 'sg1' with internal linkage}}
+
+  using ::ta;
+  using ::sta; // FIXME {{using declaration referring to 'sta' with internal linkage}}
+  using ::tb;
+  using ::stb; // expected-error {{using declaration referring to 'stb' with internal linkage}}
+  using ::tc;
+  using ::te;
+  using ::tf;
+  namespace NS2 = ::NS;
+
+  namespace UnnamedNS {
+    using UnnamedNS::a; // expected-error {{internal linkage}}
+    using UnnamedNS::sa; // expected-error {{internal linkage}}
+    using UnnamedNS::b; // expected-error {{internal linkage}}
+    using UnnamedNS::sb; // expected-error {{internal linkage}}
+    using UnnamedNS::c; // expected-error {{internal linkage}}
+    using UnnamedNS::d; // expected-error {{internal linkage}}
+    using UnnamedNS::e; // ok
+    using UnnamedNS::f; // ok? using-declaration refers to alias-declaration,
+                        // which does not have linkage (even though that then
+                        // refers to a type that has internal linkage)
+    using UnnamedNS::sg1; // expected-error {{internal linkage}}
+
+    using UnnamedNS::ta; // expected-error {{internal linkage}}
+    using UnnamedNS::sta; // expected-error {{internal linkage}}
+    using UnnamedNS::tb; // expected-error {{internal linkage}}
+    using UnnamedNS::stb; // expected-error {{internal linkage}}
+    using UnnamedNS::tc; // expected-error {{internal linkage}}
+    using UnnamedNS::te; // expected-error {{internal linkage}}
+    using UnnamedNS::tf; // expected-error {{internal linkage}}
+    namespace NS2 = UnnamedNS::NS; // ok (wording bug?)
+  }
+}

Modified: cfe/trunk/test/CXX/modules-ts/basic/basic.def.odr/p4/module.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CXX/modules-ts/basic/basic.def.odr/p4/module.cpp?rev=358932&r1=358931&r2=358932&view=diff
==============================================================================
--- cfe/trunk/test/CXX/modules-ts/basic/basic.def.odr/p4/module.cpp (original)
+++ cfe/trunk/test/CXX/modules-ts/basic/basic.def.odr/p4/module.cpp Mon Apr 22 15:50:11 2019
@@ -3,7 +3,6 @@
 
 // CHECK-DAG: @extern_var_exported = external {{(dso_local )?}}global
 // CHECK-DAG: @inline_var_exported = linkonce_odr {{(dso_local )?}}global
-// CHECK-DAG: @_ZW6ModuleE19static_var_exported = available_externally {{(dso_local )?}}global i32 0,
 // CHECK-DAG: @const_var_exported = available_externally {{(dso_local )?}}constant i32 3,
 //
 // CHECK-DAG: @_ZW6ModuleE25extern_var_module_linkage = external {{(dso_local )?}}global
@@ -21,7 +20,6 @@ void use() {
 
   (void)&extern_var_exported;
   (void)&inline_var_exported;
-  (void)&static_var_exported; // FIXME: Should not be exported.
   (void)&const_var_exported;
 
   // FIXME: This symbol should not be visible here.

Modified: cfe/trunk/test/CXX/modules-ts/basic/basic.def.odr/p4/module.cppm
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CXX/modules-ts/basic/basic.def.odr/p4/module.cppm?rev=358932&r1=358931&r2=358932&view=diff
==============================================================================
--- cfe/trunk/test/CXX/modules-ts/basic/basic.def.odr/p4/module.cppm (original)
+++ cfe/trunk/test/CXX/modules-ts/basic/basic.def.odr/p4/module.cppm Mon Apr 22 15:50:11 2019
@@ -11,7 +11,6 @@
 // can discard this global and its initializer (if any), and other TUs are not
 // permitted to run the initializer for this variable.
 // CHECK-DAG: @inline_var_exported = linkonce_odr {{(dso_local )?}}global
-// CHECK-DAG: @_ZW6ModuleE19static_var_exported = {{(dso_local )?}}global
 // CHECK-DAG: @const_var_exported = {{(dso_local )?}}constant
 //
 // CHECK-DAG: @_ZW6ModuleE25extern_var_module_linkage = external {{(dso_local )?}}global
@@ -58,32 +57,17 @@ void noninline_global_module() {
 export module Module;
 
 export {
-  // FIXME: These should be ill-formed: you can't export an internal linkage
-  // symbol, per [dcl.module.interface]p2.
-  // CHECK: define {{(dso_local )?}}void {{.*}}@_ZW6ModuleE22unused_static_exportedv
-  static void unused_static_exported() {}
-  // CHECK: define {{(dso_local )?}}void {{.*}}@_ZW6ModuleE20used_static_exportedv
-  static void used_static_exported() {}
-
   inline void unused_inline_exported() {}
   inline void used_inline_exported() {}
 
   extern int extern_var_exported;
   inline int inline_var_exported;
-  // FIXME: This should be ill-formed: you can't export an internal linkage
-  // symbol.
-  static int static_var_exported;
   const int const_var_exported = 3;
 
   // CHECK: define {{(dso_local )?}}void {{.*}}@_Z18noninline_exportedv
   void noninline_exported() {
-    used_static_exported();
-    // CHECK: define linkonce_odr {{.*}}@_Z20used_inline_exportedv
-    used_inline_exported();
-
     (void)&extern_var_exported;
     (void)&inline_var_exported;
-    (void)&static_var_exported;
     (void)&const_var_exported;
   }
 }

Modified: cfe/trunk/test/CXX/modules-ts/basic/basic.def.odr/p4/user.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CXX/modules-ts/basic/basic.def.odr/p4/user.cpp?rev=358932&r1=358931&r2=358932&view=diff
==============================================================================
--- cfe/trunk/test/CXX/modules-ts/basic/basic.def.odr/p4/user.cpp (original)
+++ cfe/trunk/test/CXX/modules-ts/basic/basic.def.odr/p4/user.cpp Mon Apr 22 15:50:11 2019
@@ -3,7 +3,6 @@
 
 // CHECK-DAG: @extern_var_exported = external {{(dso_local )?}}global
 // CHECK-DAG: @inline_var_exported = linkonce_odr {{(dso_local )?}}global
-// CHECK-DAG: @_ZW6ModuleE19static_var_exported = available_externally {{(dso_local )?}}global i32 0
 // CHECK-DAG: @const_var_exported = available_externally {{(dso_local )?}}constant i32 3
 
 import Module;
@@ -16,7 +15,6 @@ void use() {
 
   (void)&extern_var_exported;
   (void)&inline_var_exported;
-  (void)&static_var_exported;
   (void)&const_var_exported;
 
   // Module-linkage declarations are not visible here.

Modified: cfe/trunk/test/SemaCXX/anonymous-union-export.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCXX/anonymous-union-export.cpp?rev=358932&r1=358931&r2=358932&view=diff
==============================================================================
--- cfe/trunk/test/SemaCXX/anonymous-union-export.cpp (original)
+++ cfe/trunk/test/SemaCXX/anonymous-union-export.cpp Mon Apr 22 15:50:11 2019
@@ -1,6 +1,7 @@
 // RUN: %clang_cc1 -std=c++17 -fmodules-ts -emit-obj -verify -o %t.pcm %s
 
 export module M;
-export {
-    union { bool a; }; // expected-error{{anonymous unions at namespace or global scope must be declared 'static'}}
+export { // expected-note 2{{export block begins here}}
+    union { bool a; }; // expected-error {{anonymous unions at namespace or global scope must be declared 'static'}} expected-error {{declaration of 'a' with internal linkage cannot be exported}}
+    static union { bool a; }; // expected-error {{declaration of 'a' with internal linkage cannot be exported}}
 }

Modified: cfe/trunk/test/SemaCXX/modules-ts.cppm
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCXX/modules-ts.cppm?rev=358932&r1=358931&r2=358932&view=diff
==============================================================================
--- cfe/trunk/test/SemaCXX/modules-ts.cppm (original)
+++ cfe/trunk/test/SemaCXX/modules-ts.cppm Mon Apr 22 15:50:11 2019
@@ -49,8 +49,12 @@ int use_a = a; // expected-error {{decla
 import foo;
 
 export {} // expected-error {{export declaration cannot be empty}}
-export { ; }
-export { static_assert(true); }
+export { // expected-note {{begins here}}
+  ; // expected-warning {{ISO C++20 does not permit an empty declaration to appear in an export block}}
+}
+export { // expected-note {{begins here}}
+  static_assert(true); // expected-warning {{ISO C++20 does not permit a static_assert declaration to appear in an export block}}
+}
 
 int use_b = b;
 int use_n = n; // FIXME: this should not be visible, because it is not exported
@@ -74,7 +78,7 @@ struct S {
 // language rules right now, but (per personal correspondence between zygoloid
 // and gdr) is the intent.
 #if TEST == 1
-export {
+export { // expected-note {{export block begins here}}
   extern "C++" {
     namespace NestedExport {
       export { // expected-error {{appears within another export}}




More information about the cfe-commits mailing list